كيفية إعادة استهداف زوار الويب باستخدام لافتات التطبيقات الذكية الديناميكية

opoinstall
2026-10-09
5 min read

كيف يتم تشغيل حملات إعادة الاستهداف باستخدام لافتات التطبيقات الذكية؟ يتطلب تشغيل حملات إعادة الاستهداف باستخدام لافتات التطبيقات الذكية التقاط سياق تصفح الويب من الطرف الأول، وعرض لافتات HTML ديناميكية تحتوي على روابط عميقة (Deep-link) تحث على اتخاذ إجراء، وتوجيه المستخدمين العائدين مباشرة إلى المشاهد المطابقة داخل التطبيق مع تمرير رموز الإسناد.

تعد إعادة استهداف زوار الويب باستخدام لافتات التطبيقات الذكية استراتيجية هندسية للنمو، حيث تلتقط سياق التصفح من الطرف الأول على مواقع الويب المخصصة للهواتف المحمولة وتعرض لافتات ترويجية مخصصة لتوجيه الزوار مباشرة إلى مشاهد داخل التطبيق الأصلي. ومن خلال استبدال روابط متجر التطبيقات الثابتة بروابط عميقة سياقية، تساعد لافتات إعادة الاستهداف في الحفاظ على نية المستخدم، وإعادة جذب المستخدمين النشطين، ودعم التفاعل طويل الأمد مع التطبيق.

المصطلح التعريف الكيان ذو الصلة دور نية البحث
التفاعل مع التطبيق عمق وتكرار ومدة تفاعلات المستخدم داخل تطبيق الهاتف المحمول. الاحتفاظ بالمستخدمين معلوماتي / تجاري
لافتة التطبيق الذكية مكون ترويجي قائم على الويب يعرض عناصر تحث على اتخاذ إجراء (CTA) لإطلاق التطبيق أو تحميله. إعادة التوجيه من الويب إلى التطبيق معلوماتي
من الويب إلى التطبيق العملية المعمارية لتوجيه زوار متصفح الويب إلى تطبيقات الهاتف المحمول الأصلية. الروابط العميقة للجوال معلوماتي

تحافظ لافتات إعادة الاستهداف الديناميكية على سياق الويب عند توجيه المستخدمين إلى مشاهد مطابقة داخل التطبيق.

كيف يمكن لإعادة استهداف زوار الويب دعم التفاعل مع التطبيق

مفارقة نية تصفح الويب عبر الهاتف المحمول: حجم تصفح مرتفع مقابل معدلات معاملات منخفضة

تمثل مواقع الويب المخصصة للهواتف المحمولة قناة استحواذ واسعة، حيث تستقطب حركة المرور من البحث العضوي، والحملات المدفوعة، واكتشاف وسائل التواصل الاجتماعي، وتجميع المحتوى. ومع ذلك، غالبًا ما يُظهر سلوك المستهلك على متصفحات الويب المحمولة مفارقة في النية: فالمستخدمون يتصفحون ويبحثون ويقيمون المنتجات بشكل متكرر على صفحات الويب، بينما توفر التطبيقات الأصلية غالبًا مسارات معاملات أكثر سلاسة بفضل الحالة المحلية المحفوظة والتنقل المبسط.

تُدخل متصفحات الهاتف المحمول نقاط احتكاك تشغيلية مقارنة بالتطبيقات الأصلية، مثل متطلبات إعادة المصادقة أو النماذج المكونة من عدة خطوات. عندما يتصفح الزوار ذوو النية العالية كتالوج منتجات معين أو يضيفون عناصر إلى سلة تسوق عبر الويب، فإن الفشل في توفير انتقال مباشر إلى بيئة التطبيق الأصلي قد يساهم في التخلي عن السلة وانخفاض القيمة الدائمة للعميل (LTV).

التغلب على "الانقطاع عند المدخل": لماذا يؤدي إعادة جذب المستخدمين في الصفحة الرئيسية إلى تآكل التحويل

من التحديات الشائعة في إعادة استهداف الويب عبر الهاتف المحمول هو نشر لافتات ثابتة توجه المستخدمين العائدين إلى الشاشة الرئيسية الافتراضية للتطبيق. عندما يتصفح مستخدم نشط أو منقطع للتطبيق منتجًا معينًا على موقع ويب، فإن النقر على لافتة عامة يؤدي إلى إطلاق التطبيق الذي يهبط بهم في الردهة الرئيسية.

يخلق هذا الانفصال احتكاكًا معرفيًا فوريًا. يجب على المستخدم التنقل يدويًا عبر قوائم الفئات، أو تنفيذ استعلامات البحث، أو تحديد موقع سلة التسوق الخاصة به من البداية. كل خطوة تنقل يدوية تزيد من خطر التخلي عن العملية. تعالج لافتات التطبيقات الذكية الديناميكية هذا الاحتكاك من خلال ربط سياق تصفح الويب مباشرة بمسارات الروابط العميقة، مما يوصل المستخدمين إلى عرض المنتج ذي الصلة، أو السلة المملوءة مسبقًا، أو شاشة الهبوط الترويجية داخل التطبيق الأصلي.

تقييم وقت التنفيذ كمقياس للاحتكاك التشغيلي لزوار الويب

في تسويق دورة الحياة، يتضاءل انتباه المستخدم بسرعة مع تأخيرات التنقل. يقيس المقياس التشغيلي وقت التنفيذ (Taction) المدة الزمنية بين نقر زائر الويب على لافتة إعادة الاستهداف والتفاعل النشط مع العنصر المستهدف أو شاشة الخروج داخل التطبيق الأصلي:

Taction=ttarget_rendered−tbanner_clickT_{\text{action}} = t_{\text{target\_rendered}} - t_{\text{banner\_click}}

في مسارات التحويل غير السياقية، يطول زمن التنفيذ (Taction) بسبب التنقل اليدوي داخل التطبيق وتأخيرات البحث. يمكن أن يقلل التوجيه السياقي من جزء التنقل اليدوي في Taction. ومع ذلك، يظل Taction الإجمالي يتضمن إطلاق التطبيق، واستعادة المعلمات، والتحقق المحلي، وتفويض الخادم، وعرض المشهد. إن تقليل التنقل اليدوي يحافظ على نية الشراء ويخلق فرصة قابلة للاختبار لتحسين إتمام عملية الدفع.

كيف يقوم ربط السياق بتحويل لافتات الويب الثابتة إلى أدوات إعادة استهداف

التقاط سياق الويب للطرف الأول والتنقل في دورات حياة التخزين

على عكس اللافتات الثابتة التي تعرض نسخًا برمجية مشفرة، تقوم لافتات إعادة الاستهداف الديناميكية بفحص بيانات جلسة الويب للطرف الأول لتخصيص الرسائل. عندما يتنقل الزائر عبر موقع ويب للهاتف المحمول، تقرأ البرامج النصية من جانب العميل حالة الجلسة من كائن مستند الويب (DOM)، أو معلمات استعلام URL، أو تخزين الويب للطرف الأول (sessionStorage أو localStorage):

  • SKU المنتج المعروض: يلتقط معرف المنتج المحدد (مثل item_id=SKU_5501) الذي يتم عرضه حاليًا.
  • رموز التخلي عن السلة: يقرأ معرفات السلة المعلقة وعلامات استحقاق الخصم.
  • تقارب الفئات: يتتبع فئات التصفح عالية المستوى (مثل الإلكترونيات، الملابس) لتخصيص العروض الترويجية البديلة.

يجب على المهندسين مراعاة دورات حياة تخزين متصفح الهاتف المحمول. بموجب سياسات منع التتبع الحديثة في WebKit، قد يتم حذف التخزين القابل للكتابة بواسطة البرنامج النصي للعميل (localStorage، sessionStorage، IndexedDB) بعد سبعة أيام من عدم تفاعل المستخدم مع الموقع، اعتمادًا على حالة منع التتبع في WebKit وتفاعل المستخدم الأخير. يجب التعامل مع تخزين المتصفح كذاكرة تخزين مؤقت للجلسة من جانب العميل كأفضل جهد ممكن، وليس كملف تعريف عميل دائم أو قاعدة بيانات موثوقة. يجب دائمًا حل حالة السلة الموثوقة وتوافر العناصر واستحقاق المستخدم والتحقق منها على الخلفية.

سياق المتصفح هو مدخل توجيه مؤقت بينما تظل أنظمة الخلفية هي الموثوقة لحالة العمل.

حدود الخصوصية والموافقة لبيانات إعادة الاستهداف

يتطلب جمع وتمرير سياق التصفح عبر حدود الويب والتطبيقات الأصلية التزامًا صارمًا بحوكمة الخصوصية:

  • تقليل البيانات: اجمع واستخدم سياق إعادة الاستهداف فقط بموجب سياسات الموافقة والإشعار والاحتفاظ وتقليل البيانات المعمول بها في الموقع والتطبيق.
  • لا توجد بيانات تعريف شخصية (PII) في روابط URL: تجنب ترميز البيانات الشخصية المحددة مباشرة (PII) أو السمات الشخصية الحساسة في روابط اللافتات أو التخزين من جانب العميل.
  • الحالة المؤقتة: عامل سياق التصفح الذي تم التقاطه كحالة مؤقتة للطرف الأول تخضع لتفضيلات موافقة المستخدم وقواعد منع التتبع للمنصة.

عرض المحتوى الديناميكي: تحديث نسخة اللافتة والأعمال الفنية وعناصر الحث على اتخاذ إجراء في الوقت الفعلي

بمجرد استخراج سياق الجلسة، يتم تحديث التخطيط المرئي للافتة ديناميكيًا:

  • يتم تحديث عنوان اللافتة من نص عام إلى مطالبات سياقية (مثل "متابعة طلبك" أو "عرض المنتج في التطبيق").
  • ينتقل زر الحث على اتخاذ إجراء من "احصل على التطبيق" القياسي إلى مطالبة قابلة للتنفيذ (مثل "فتح السلة").
  • تعرض الأعمال الفنية الديناميكية صورة مصغرة للمنتج المحدد بجانب مؤشرات المخزون أو الأسعار الحالية.

هذا الارتباط السياقي يحول اللافتة من عنصر إعلاني سلبي إلى أداة تفاعلية.

إدارة قيود النطاقات المتقاطعة: التوصية بنطاقات فرعية مخصصة لروابط Apple Universal Links

عند نشر Universal Links على نظام iOS، يجب على مهندسي الويب التعامل مع قيد التنقل داخل نفس النطاق في متصفح Safari، كما هو موثق في وثائق مطوري Apple حول السماح للتطبيقات ومواقع الويب بالارتباط بمحتواك. إذا تصفح المستخدم صفحة ويب على https://example.com ونقر على Universal Link يشير إلى نفس النطاق تمامًا، فعادةً ما يظل Safari في المتصفح بدلاً من إطلاق التطبيق الأصلي.

استخدام مضيف توجيه مرتبط بشكل منفصل يمكن أن يتجنب سلوك تصفح Safari الموثق داخل نفس النطاق، ولكن فتح التطبيق الأصلي لا يزال يعتمد على ارتباط Universal Link صالح، واستحقاق التطبيق المثبت، وحالة المنصة:

  • استضف موقع الويب الرئيسي للهاتف المحمول على https://www.example.com.
  • وجه أهداف لافتة Universal Link من خلال نطاق فرعي مرتبط تم التحقق منه، مثل https://app.example.com/product/5501.

دور الروابط العميقة المؤجلة (Deferred Deep Linking) عندما لا يكون التطبيق مثبتًا لدى زوار الويب

لا يمتلك جميع زوار الويب الذين يتم استهدافهم بواسطة اللافتات الديناميكية التطبيق مثبتًا. قد تفشل مخططات URI المخصصة (myapp://) في الحل على الأجهزة غير المثبت عليها التطبيق ما لم توفر الصفحة بديلًا صريحًا.

تعالج الروابط العميقة المؤجلة هذا السيناريو. عندما ينقر مستخدم ليس لديه التطبيق على لافتة إعادة استهداف، تلتقط طبقة التوجيه سياق الوجهة المقصود (مثل SKU المعروض ورمز العرض الترويجي النشط) على خادم الإسناد قبل إعادة توجيه المتصفح إلى Google Play أو متجر التطبيقات. عندما يقوم المستخدم بتنزيل التطبيق وفتحه لأول مرة، يسترد SDK الإسناد المعلمات المخزنة مؤقتًا، مما يمكن التطبيق الأصلي من استعادة المشهد المستهدف عند الإطلاق الأول حيثما تسمح سياسات خصوصية المنصة بذلك.

الآليات التقنية لربط المعلمات الديناميكي وتوجيه الروابط العميقة

هيكلة معلمات URL لإعادة الاستهداف

يقوم سلسلة استعلام إعادة استهداف قوية بهيكلة توجيه الوجهة، والرموز الترويجية، وإسناد الحملة بوضوح:

https://app.example.com/promo/cart?scene=cart&item_id=SKU_5501&promo_code=RESTART10&token=TK_1234567890abcdef&utm_source=web_retargeting

تفصل هذه الحمولة بوضوح تعليمات التوجيه (scene=cart)، ومعرفات الأعمال (item_id)، وسياق التتبع (utm_source).

فرض تعقيم البيانات من جانب العميل وقيود الطول

وفقًا لـ دليل اختبار أمان تطبيقات الهاتف المحمول الخاص بـ OWASP بشأن الروابط العميقة غير الآمنة، يجب التعامل مع جميع المعلمات المستخرجة من روابط ويب أو تخزين جانب العميل كمدخلات غير موثوقة.

قبل إنشاء حمولات تسليم الروابط العميقة:

  • تحقق من صحة معرفات scene مقابل قائمة سماح بأهداف العرض المعتمدة (cart، product_detail، promo_hub).
  • فرض فلاتر التعبيرات العادية الأبجدية الرقمية (مثل ^[A-Za-z0-9_-]{1,64}$) على المعرفات والرموز الترويجية.
  • فرض حدود طول صارمة على رموز المسار (مثل 16 إلى 128 حرفًا) والتحقق من صحة سلاسل الحملة مقابل سياسة الأحرف والطول المحددة من قبل التطبيق، مع رفض أو استبدال القيم غير الصالحة بإعدادات افتراضية آمنة.
  • تعامل مع رموز المسار كمراجع غامضة غير موثوقة. يجب ألا تمنح حيازة رمز المسار بأي حال من الأحوال إذن الوصول إلى السلة، أو الخصومات، أو إجراءات الحساب بدون تحقق مصادق عليه من الخلفية.

تفعيل التسليم المباشر عبر معالجات Web SDK

قد يكشف تكامل OpoInstall Web SDK النموذجي عن طريقة تسليم "التنبيه أو التثبيت"؛ تحقق من اسم الطريقة الدقيق، والمنشئ، ومسار CDN، ومخطط المعلمات مقابل إصدار SDK للإنتاج المنشور في بيئتك.

يدعم OpoInstall التسليم عبر الويب إلى التطبيق عبر الأنظمة الأساسية واستعادة المعلمات المؤجلة؛ يعتمد آلية التوجيه الدقيقة وعقد SDK على إصدار SDK المنشور. راجع وثائق تكامل SDK للحصول على معلمات الواجهة الكاملة ومواصفات API.

[المستخدم يتصفح صفحة ويب للجوال (مثل عرض SKU_1024)]
                         │
                         ▼
[البرنامج النصي يلتقط السياق في جلسة الطرف الأول]
                         │
                         ▼
[اللافتة الذكية الديناميكية تعرض عرضًا سياقيًا]
                         │
                         ▼
[المستخدم ينقر على "متابعة في التطبيق"]
                         │
     ┌───────────────────┴───────────────────┐
     ▼                                       ▼
[التطبيق مثبت]                     [التطبيق غير مثبت]
     │                                       │
     ▼                                       ▼
[Universal Link / App Link]         [طبقة توجيه الويب]
     │                                       │
     ▼                                       ▼
[إطلاق التطبيق الأصلي مباشرة]          [تحميل المتجر / الرابط المؤجل]
     │                                       │
     └───────────────────┬───────────────────┘
                         ▼
          [استرجاع معلمات SDK الأصلي]
                         │
                         ▼
          [حالة الخادم والتحقق من المصادقة]
                         │
                         ▼
          [عرض المشهد المستهدف داخل التطبيق]

كيفية هندسة استعادة المشهد داخل التطبيق بسلاسة لإعادة استهداف الويب

التعامل مع البدايات الباردة مقابل استئناف الخلفية عبر دورات حياة Android و iOS

يجب على تطبيقات الهاتف المحمول الأصلية التعامل مع حمولات إعادة الاستهداف الواردة عبر حالات التنفيذ المتميزة:

  • استئناف دافئ: التطبيق يعمل بالفعل في ذاكرة الخلفية. على Android، يتم تسليم النية إلى onNewIntent عندما يعيد تكوين مهمة النشاط استخدام مثيل موجود. على iOS، يتم تسليم الرابط إلى scene(_:continue:). يقوم موجه التطبيق بالتنقل في التسلسل الهرمي للعرض النشط دون إعادة تهيئة الحالة العالمية.
  • بداية باردة: عملية التطبيق منهية. يقوم نظام التشغيل بتمهيد العملية وتسليم النية أثناء بدء التشغيل. يجب أن تلتقط البنية الأصلية الحمولة، وتتحقق من التهيئة، وتوجه إلى مشهد الوجهة بمجرد تحميل التسلسلات الهرمية الرئيسية لواجهة المستخدم.

عزل معرفات التوجيه عن بيانات اعتماد مصادقة المستخدم

يجب أن تحمل روابط الروابط العميقة واللافتات من الويب إلى التطبيق نية التوجيه فقط (أي منتج أو سلة يجب عرضها) ورموز مرجعية غامضة قصيرة العمر. لا ينبغي بأي حال من الأحوال أن تحمل سلاسل استعلام الروابط العميقة معرفات مستخدم قاعدة البيانات، أو كلمات مرور الحساب، أو رموز الجلسة غير المشفرة.

يجب على التطبيق الأصلي حل مصادقة المستخدم بشكل مستقل من مخزن بيانات الاعتماد المحلية الآمنة الخاصة به (مثل iOS Keychain أو Android Keystore) قبل عرض معلومات المستخدم الخاصة أو تعديل حالة الحساب.

تنفيذ بوابات تفويض من جانب الخادم للعروض الحصرية وحالة السلة

لا يضمن سلسلة استعلام رابط عميق صالح بقاء العرض الترويجي نشطًا أو استحقاق المستخدم للمطالبة به. يجب على تطبيقات العميل إرسال رموز المسار إلى الخلفية للتحقق من جانب الخادم:

  • التحقق من أن رموز القسائم الترويجية (promo_code) غير منتهية الصلاحية ومستحقة للمستخدم المصادق عليه.
  • التحقق من أن رموز السلة نشطة وتنتمي إلى الحساب المصادق عليه.
  • فرض التحقق من عدم التكرار (idempotency) للاستخدام الفردي لمنع إساءة استخدام القسائم.

إدارة الأهداف القديمة: التوجيه البديل للصفقات منتهية الصلاحية والمخزون غير المتوفر

قد ينقر زوار الويب على لافتات إعادة الاستهداف بعد أيام من انتهاء صفقة ترويجية أو نفاد مخزون عنصر ما. إذا حاول تطبيق تحميل منتج محذوف بدون تحقق من الحالة، سيواجه المستخدمون واجهات مكسورة.

تفرض بنى الإنتاج بوابة بديلة من مستويين:

  1. التحقق من المسار من جانب العميل: إذا كان مشهد الوجهة غير معروف أو بناء جملة الحمولة غير صحيح، فتوجه فورًا إلى الشاشة الرئيسية الافتراضية.
  2. التحقق من الحالة من جانب الخادم: إذا كان المسار صالحًا ولكن العنصر قد نفد أو انتهت صلاحية رمز العرض الترويجي، فاعرض إشعارًا مشروطًا إعلاميًا (مثل "هذا العنصر غير متوفر حاليًا، لكن استكشف توصيات مماثلة") وانتقل بسلاسة إلى مركز الفئة ذي الصلة.

التنفيذ الأمامي وللجوال للافتات إعادة الاستهداف السياقية

نموذج سياق واحد موحد يقود كلاً من محتوى اللافتة الديناميكية وتوجيه الروابط العميقة.

هيكلة البرنامج النصي الأمامي السياقي مع بدائل مرفقة مسبقًا

يفترض التنفيذ أن حاوية مكون اللافتة النمطية مثبتة بالفعل داخل ترميز صفحة الويب. يطبق البرنامج النصي فحوصات خلو (null-checks) دفاعية، وتصنيف المنصة، وفحوصات التهدئة للتخزين المحلي، وتعقيم صارم للمعلمات قبل الربط بمعالجات SDK من جانب العميل. يتم اشتقاق تخصيص نص اللافتة مباشرة من نموذج البيانات الموحد لضمان التوافق الصارم بين النسخة المعروضة وحمولة التسليم الأساسية.

اعتراض نية Android واستخراج المعلمات في Kotlin

على Android، يلتقط MainActivity الأساسي نوايا الروابط العميقة الواردة عبر onCreate و onNewIntent، ويطبع أنواع البيانات ويتحقق من صحة حقول الحمولة مقابل قائمة سماح قبل التفويض إلى تفويض الخلفية.

معالجة Universal Link في SceneDelegate على iOS في Swift

على iOS، يعالج SceneDelegate.swift روابط Universal Links التي يتم تسليمها عبر scene(_:continue:)، ويحلل المعلمات، ويعقم المدخلات، ويوجه إلى وحدات تحكم العرض الأصلية على الممثل الرئيسي.

يوضح التنفيذ أدناه تكوين لافتة الواجهة الأمامية واستخراج المعلمات على Android (Kotlin) و iOS (Swift) الأصلي. تظهر أنماط تكامل OpoInstall النموذجية أدناه؛ تحقق من أسماء الحزم، وفئات SDK، ومسارات CDN، وأسماء رد الاتصال، وتوقيعات الطرق مقابل إصدار OpoInstall SDK المنشور حاليًا.

// JavaScript: الاستخراج السياقي، وتوجيه المنصة، وتكامل SDK النموذجي
// نمط تكامل نموذجي. تحقق من عناوين URL للبرنامج النصي، وأسماء المنشئين، وتوقيعات API
// مقابل إصدار OpoInstall SDK للإنتاج المنشور في بيئتك.
// ملاحظة: يفترض أن ترميز مكون اللافتة القابل لإعادة الاستخدام مع المعرفات المستهدفة مثبت بالفعل في DOM.
(function() {
    var DISMISS_KEY = "retarget_smart_banner_dismissed_at";
    var COOL_DOWN_MS = 7 * 24 * 60 * 60 * 1000; // نافذة تهدئة لمدة 7 أيام

    // 1. تقييم المنصة: قمع اللافتة على بيئات سطح المكتب
    function getMobilePlatform() {
        var ua = navigator.userAgent || navigator.vendor || window.opera;
        if (/Android/i.test(ua)) return "android";
        var isIOS = /iPad|iPhone|iPod/.test(ua) && !window.MSStream;
        var isIPadOS = (navigator.platform === "MacIntel" && navigator.maxTouchPoints > 1);
        if (isIOS || isIPadOS) return "ios";
        return "unsupported_desktop";
    }

    var platform = getMobilePlatform();
    if (platform === "unsupported_desktop") {
        return; // قمع على متصفحات سطح المكتب
    }

    // 2. التحقق من الرفض عبر التخزين المحلي
    function shouldShowBanner() {
        try {
            var dismissedAt = localStorage.getItem(DISMISS_KEY);
            if (!dismissedAt) return true;
            var now = new Date().getTime();
            return (now - parseInt(dismissedAt, 10)) > COOL_DOWN_MS;
        } catch (e) {
            return true; // العودة إلى العرض إذا كان التخزين المحلي مقيدًا
        }
    }

    if (!shouldShowBanner()) {
        return;
    }

    // حراس النطاق الفارغ لـ DOM: تحقق من وجود العناصر قبل التلاعب بها
    var bannerContainer = document.getElementById("dynamicRetargetBanner");
    var closeBtn = document.getElementById("bannerCloseBtn");
    var actionBtn = document.getElementById("bannerActionBtn");
    var bannerTitle = document.getElementById("bannerTitle");

    if (!bannerContainer || !actionBtn || !bannerTitle) {
        return;
    }

    // 3. استخراج وتعقيم سياق تصفح الطرف الأول (مخطط 'item_id' متسق)
    var urlParams = new URLSearchParams(window.location.search);
    var rawScene = urlParams.get("scene") || "cart";
    var rawId = urlParams.get("item_id") || "";
    var rawPromo = urlParams.get("promo_code") || "";
    var rawToken = urlParams.get("token") || "";
    var rawChannel = urlParams.get("utm_source") || "web_retargeting";

    function sanitizePayload() {
        var allowedScenes = ["cart", "product_detail", "promo_hub"];
        var targetScene = allowedScenes.indexOf(rawScene) !== -1 ? rawScene : "cart";

        var idRegex = /^[A-Za-z0-9_-]{1,64}$/;
        var targetId = idRegex.test(rawId) ? rawId : "";

        // التحقق المستقل من رمز العرض الترويجي (حد صارم 32 حرفًا يطابق عقد التطبيق الأصلي)
        var promoRegex = /^[A-Za-z0-9_-]{1,32}$/;
        var promoCode = promoRegex.test(rawPromo) ? rawPromo : "";

        var tokenRegex = /^[A-Za-z0-9_-]{16,128}$/;
        var routeToken = tokenRegex.test(rawToken) ? rawToken : "";

        var channelRegex = /^[A-Za-z0-9_-]{1,32}$/;
        var channelCode = channelRegex.test(rawChannel) ? rawChannel : "web_retargeting";

        var payload = {
            scene: targetScene,
            token: routeToken
        };
        if (targetId.length > 0) {
            payload.item_id = targetId;
        }
        if (promoCode.length > 0) {
            payload.promo_code = promoCode;
        }

        return {
            payload: payload,
            channelCode: channelCode
        };
    }

    var normalizedData = sanitizePayload();

    // تخصيص رسائل اللافتة بناءً على النموذج الموحد
    if (normalizedData.payload.scene === "cart") {
        bannerTitle.textContent = "متابعة طلبك";
        actionBtn.textContent = "فتح السلة";
    } else if (normalizedData.payload.scene === "product_detail") {
        bannerTitle.textContent = "عرض المنتج في التطبيق";
        actionBtn.textContent = "عرض العنصر";
    }

    bannerContainer.style.display = "block";

    // التعامل مع رفض المستخدم
    if (closeBtn) {
        closeBtn.addEventListener("click", function() {
            try {
                localStorage.setItem(DISMISS_KEY, new Date().getTime().toString());
            } catch (e) {}
            bannerContainer.style.display = "none";
        });
    }

    // 4. مسار الرجوع الثابت الأولي
    function executeStaticFallback() {
        if (platform === "android") {
            window.location.href = "https://play.google.com/store/apps/details?id=com.example.app";
        } else if (platform === "ios") {
            window.location.href = "https://apps.apple.com/app/id123456789";
        }
    }

    var activeClickHandler = function() {
        executeStaticFallback();
    };

    actionBtn.addEventListener("click", function(e) {
        activeClickHandler(e);
    });

    // 5. حقن البرنامج النصي الديناميكي لتكامل SDK
    var script = document.createElement("script");
    script.type = "text/javascript";
    script.src = "https://web.cdn.opoinstallcloud.com/openinstall.js";

    script.onload = function() {
        try {
            if (typeof OpenInstall === "function") {
                var openInstall = new OpenInstall({
                    appKey: "YOUR_OPOINSTALL_APPKEY",
                    onready: function() {
                        var m = this;
                        // الترقية إلى تسليم الرابط العميق الديناميكي بمجرد جاهزية SDK
                        activeClickHandler = function() {
                            var sanitized = sanitizePayload();
                            m.wakeupOrInstall({
                                data: sanitized.payload,
                                channelCode: sanitized.channelCode
                            });
                        };
                    }
                }, actionBtn);
            }
        } catch (err) {
            // الاحتفاظ بالبديل الثابت المرفق مسبقًا إذا حدث خطأ في التهيئة
        }
    };

    script.onerror = function() {
        // الاحتفاظ بالبديل الثابت المرفق مسبقًا إذا فشل طلب الشبكة
    };

    document.head.appendChild(script);
})();
// Android: MainActivity.kt - معالجة نية إعادة الجذب وبوابة التحقق من المسار
// نمط تكامل نموذجي. تحقق من أسماء الحزم، وفئات رد الاتصال، وتوقيعات الطرق
// مقابل إصدار OpoInstall SDK للإنتاج المنشور في بيئتك.
package com.example.app.ui

import android.content.Intent
import android.os.Bundle
import android.util.Log
import androidx.appcompat.app.AppCompatActivity
import com.opoinstall.api.OpoInstall
import com.opoinstall.api.listener.AppWakeUpAdapter
import com.opoinstall.api.model.AppData
import org.json.JSONObject

data class ValidatedRetargetingRoute(
    val scene: String,
    val targetId: String,
    val promoCode: String,
    val routeToken: String,
    val rawKeys: Set<String>
)

object OpoInstallPayloadAdapter {
    /**
     * يطبع تمثيلات بيانات SDK غير المتجانسة (سلسلة JSON، خريطة، أو JSONObject)
     * إلى نموذج إعادة استهداف قانوني مملوك للتطبيق مع فحص صارم للنوع عند الفشل.
     */
    fun normalize(rawPayload: Any?): ValidatedRetargetingRoute? {
        if (rawPayload == null) return null

        val stringMap = when (rawPayload) {
            is String -> parseJsonStringStrict(rawPayload)
            is Map<*, *> -> parseMapStrict(rawPayload)
            is JSONObject -> parseJsonObjectStrict(rawPayload)
            else -> {
                Log.w("PayloadAdapter", "نوع حمولة SDK غير مدعوم: ${rawPayload.javaClass.name}")
                null
            }
        } ?: return null

        val scene = stringMap["scene"] ?: ""
        if (scene.isEmpty()) return null

        return ValidatedRetargetingRoute(
            scene = scene,
            targetId = stringMap["item_id"] ?: "",
            promoCode = stringMap["promo_code"] ?: "",
            routeToken = stringMap["token"] ?: "",
            rawKeys = stringMap.keys
        )
    }

    private fun parseJsonStringStrict(rawJson: String): Map<String, String>? {
        return try {
            val json = JSONObject(rawJson)
            parseJsonObjectStrict(json)
        } catch (e: Exception) {
            Log.e("PayloadAdapter", "فشل تحليل سلسلة JSON", e)
            null
        }
    }

    private fun parseJsonObjectStrict(json: JSONObject): Map<String, String>? {
        val map = mutableMapOf<String, String>()
        for (key in json.keys()) {
            val value = json.opt(key)
            // فشل عند الإغلاق: رفض أنواع غير السلسلة لمنع استغلال إجبار النوع
            if (value !is String) {
                Log.w("PayloadAdapter", "قيمة حمولة غير سلسلة للمفتاح المرفوض: $key")
                return null
            }
            map[key] = value
        }
        return map
    }

    private fun parseMapStrict(rawMap: Map<*, *>): Map<String, String>? {
        val map = mutableMapOf<String, String>()
        for ((key, value) in rawMap) {
            if (key !is String || value !is String) {
                Log.w("PayloadAdapter", "مفتاح أو قيمة غير سلسلة مرفوضة في خريطة خام: $key")
                return null
            }
            map[key] = value
        }
        return map
    }
}

object RetargetingRouteValidator {
    private val allowedKeys = setOf("scene", "item_id", "promo_code", "token")
    private val allowedScenes = setOf("cart", "product_detail", "promo_hub")

    fun validate(payload: ValidatedRetargetingRoute): ValidatedRetargetingRoute? {
        // الخطوة 1: التحقق الصارم من مفاتيح الفشل عند الإغلاق (رفض مفاتيح الحمولة غير المعروفة)
        if (!allowedKeys.containsAll(payload.rawKeys)) {
            return null
        }

        // الخطوة 2: التحقق من المشهد مقابل قائمة السماح الصارمة
        if (!allowedScenes.contains(payload.scene)) {
            return null
        }

        // الخطوة 3: فرض حدود أبجدية رقمية وطولية على معرف الهدف
        val alphanumericRegex = Regex("^[A-Za-z0-9_-]+$")
        if (payload.targetId.isNotEmpty() && (payload.targetId.length > 64 || !payload.targetId.matches(alphanumericRegex))) {
            return null
        }
        if (payload.promoCode.isNotEmpty() && (payload.promoCode.length > 32 || !payload.promoCode.matches(alphanumericRegex))) {
            return null
        }

        // الخطوة 4: التحقق من تنسيق رمز المسار
        if (payload.routeToken.isNotEmpty() && (payload.routeToken.length !in 16..128 || !payload.routeToken.matches(alphanumericRegex))) {
            return null
        }

        return payload
    }
}

class MainActivity : AppCompatActivity() {

    override fun onCreate(savedInstanceState: Bundle?) {
        super.onCreate(savedInstanceState)
        setContentView(R.layout.activity_main)

        // معالجة نية إعادة الاستهداف عند البدء البارد
        intent?.let { handleRetargetingIntent(it) }
    }

    override fun onNewIntent(intent: Intent) {
        super.onNewIntent(intent)
        setIntent(intent)

        // معالجة نية إعادة الاستهداف عند استئناف الدافئ عندما يُعاد استخدام النشاط
        handleRetargetingIntent(intent)
    }

    private fun handleRetargetingIntent(intent: Intent) {
        OpoInstall.getInstance().getWakeUp(intent, object : AppWakeUpAdapter() {
            override fun onWakeUp(appData: AppData?) {
                if (appData == null) return

                val rawPayload = appData.data
                if (rawPayload == null) return

                // الخطوة 1: تطبيع حمولة SDK البائع مباشرة عبر المحول
                val canonicalPayload = OpoInstallPayloadAdapter.normalize(rawPayload)
                val validatedRoute = canonicalPayload?.let { RetargetingRouteValidator.validate(it) }

                if (validatedRoute != null) {
                    // الخطوة 2: التحقق من تفويض الخلفية وحالة المورد باستخدام جلسة التطبيق المصادق عليها
                    BackendRouteAuthorizer.verifyRouteAuthorization(validatedRoute.scene, validatedRoute.targetId, validatedRoute.routeToken) { isAuthorized ->
                        runOnUiThread {
                            if (isAuthorized) {
                                executeTargetNavigation(validatedRoute)
                            } else {
                                executeLobbyFallback("انتهت صلاحية صفقة أو عنصر إعادة الاستهداف المطلوب.")
                            }
                        }
                    }
                } else {
                    runOnUiThread {
                        executeLobbyFallback("حمولة إعادة استهداف غير صحيحة أو غير مفوضة.")
                    }
                }
            }
        })
    }

    private fun executeTargetNavigation(route: ValidatedRetargetingRoute) {
        Log.i("AppNavigator", "التنقل إلى مشهد إعادة الاستهداف: ${route.scene}")
        // الإرسال إلى وحدة تحكم التنقل الداخلية
    }

    private fun executeLobbyFallback(reason: String) {
        Log.w("AppNavigator", "الرجوع الآمن إلى ردهة المنزل: $reason")
        // عرض إشعار وتوجيه إلى عرض الصفحة الرئيسية الافتراضي
    }
}

// عنصر نائب لتفويض الخلفية خاص بالتطبيق (ليس OpoInstall SDK API)
object BackendRouteAuthorizer {
    fun verifyRouteAuthorization(scene: String, targetId: String, token: String, callback: (Boolean) -> Unit) {
        // عنصر نائب للفشل عند الإغلاق: يجب الاتصال بـ API تفويض الخلفية المباشر.
        // تتحقق الخلفية الإنتاجية من جلسة المستخدم النشطة، وانتهاء صلاحية الرمز، وملكية السلة، وعدم التكرار للاستخدام الفردي.
        val isAuthorized = false
        callback(isAuthorized)
    }
}
// iOS: SceneDelegate.swift - معالجة Universal Link وبوابة التحقق من المسار
// نمط تكامل نموذجي. تحقق من أسماء الحزم، وفئات رد الاتصال، وتوقيعات الطرق
// مقابل إصدار OpoInstall SDK للإنتاج المنشور في بيئتك.
import UIKit
import libOpoInstallSDK

struct ValidatedRetargetingRoute {
    let scene: String
    let targetId: String
    let promoCode: String
    let routeToken: String
    let rawKeys: Set<String>
}

class OpoInstallPayloadAdapter {
    /**
     * يطبع تمثيلات بيانات SDK غير المتجانسة (قاموس، سلسلة JSON، أو كائن مخصص)
     * إلى نموذج قانوني مملوك للتطبيق مع فحص صارم للنوع عند الفشل.
     */
    static func normalize(rawPayload: Any?) -> ValidatedRetargetingRoute? {
        guard let payload = rawPayload else { return nil }

        if let dict = payload as? [String: Any] {
            return normalizeDictionaryStrict(dict)
        } else if let jsonString = payload as? String, let data = jsonString.data(using: .utf8) {
            do {
                if let dict = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] {
                    return normalizeDictionaryStrict(dict)
                }
            } catch {
                NSLog("[PayloadAdapter] فشل إلغاء تسلسل JSON: %@", error.localizedDescription)
                return nil
            }
        }
        return nil
    }

    private static func normalizeDictionaryStrict(_ dict: [String: Any]) -> ValidatedRetargetingRoute? {
        for (key, value) in dict {
            guard value is String else {
                NSLog("[PayloadAdapter] قيمة غير سلسلة مرفوضة للمفتاح: %@", key)
                return nil
            }
        }

        guard let scene = dict["scene"] as? String, !scene.isEmpty else {
            return nil
        }

        let targetId = dict["item_id"] as? String ?? ""
        let promoCode = dict["promo_code"] as? String ?? ""
        let routeToken = dict["token"] as? String ?? ""
        let keys = Set(dict.keys)

        return ValidatedRetargetingRoute(
            scene: scene,
            targetId: targetId,
            promoCode: promoCode,
            routeToken: routeToken,
            rawKeys: keys
        )
    }
}

class RetargetingRouteValidator {
    private static let allowedKeys: Set<String> = ["scene", "item_id", "promo_code", "token"]
    private static let allowedScenes: Set<String> = ["cart", "product_detail", "promo_hub"]

    static func validate(payload: ValidatedRetargetingRoute) -> ValidatedRetargetingRoute? {
        // الخطوة 1: التحقق من مفتاح الفشل عند الإغلاق (رفض مفاتيح الحمولة غير المعروفة)
        guard payload.rawKeys.isSubset(of: allowedKeys) else {
            return nil
        }

        // الخطوة 2: التحقق من المشهد مقابل قائمة السماح الصارمة
        guard allowedScenes.contains(payload.scene) else {
            return nil
        }

        // الخطوة 3: فرض حدود أبجدية رقمية وطولية على معرف الهدف ورمز العرض الترويجي
        let validChars = CharacterSet(charactersIn: "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-")
        if !payload.targetId.isEmpty {
            guard payload.targetId.count <= 64, payload.targetId.rangeOfCharacter(from: validChars.inverted) == nil else {
                return nil
            }
        }
        if !payload.promoCode.isEmpty {
            guard payload.promoCode.count <= 32, payload.promoCode.rangeOfCharacter(from: validChars.inverted) == nil else {
                return nil
            }
        }

        // الخطوة 4: التحقق من تنسيق رمز المسار
        if !payload.routeToken.isEmpty {
            guard payload.routeToken.count >= 16 && payload.routeToken.count <= 128,
                  payload.routeToken.rangeOfCharacter(from: validChars.inverted) == nil else {
                return nil
            }
        }

        return payload
    }
}

class SceneDelegate: UIResponder, UIWindowSceneDelegate, OpoInstallDelegate {

    var window: UIWindow?

    func scene(
        _ scene: UIScene,
        willConnectTo session: UISceneSession,
        options connectionOptions: UIScene.ConnectionOptions
    ) {
        guard let _ = (scene as? UIWindowScene) else { return }

        // تهيئة OpoInstall SDK
        OpoInstallSDK.initWith(self)

        // التعامل مع الإطلاق البارد عبر Universal Link
        if let userActivity = connectionOptions.userActivities.first(where: { $0.activityType == NSUserActivityTypeBrowsingWeb }) {
            OpoInstallSDK.continue(userActivity)
        }
    }

    func scene(_ scene: UIScene, continue userActivity: NSUserActivity) {
        // التعامل مع استئناف الدافئ عبر Universal Link
        if userActivity.activityType == NSUserActivityTypeBrowsingWeb {
            OpoInstallSDK.continue(userActivity)
        }
    }

    // رد اتصال Wakeup الخاص بـ OpoInstall
    func getWakeUpParams(_ appData: OpoInstallData?) {
        guard let data = appData, let rawPayload = data.data else {
            return
        }

        // الخطوة 1: تطبيع تمثيل حمولة SDK البائع مع فحص صارم للنوع
        guard let canonicalPayload = OpoInstallPayloadAdapter.normalize(rawPayload: rawPayload),
              let validatedRoute = RetargetingRouteValidator.validate(payload: canonicalPayload) else {
            DispatchQueue.main.async {
                self.executeLobbyFallback(reason: "حمولة إعادة استهداف غير صحيحة أو غير مفوضة")
            }
            return
        }

        // الخطوة 2: التحقق من تفويض الخلفية وحالة المورد باستخدام جلسة التطبيق المصادق عليها
        BackendRouteAuthorizer.shared.verifyRouteAuthorization(scene: validatedRoute.scene, targetId: validatedRoute.targetId, token: validatedRoute.routeToken) { isAuthorized in
            DispatchQueue.main.async {
                if isAuthorized {
                    self.executeTargetNavigation(route: validatedRoute)
                } else {
                    self.executeLobbyFallback(reason: "المورد منتهي الصلاحية أو غير مفوض")
                }
            }
        }
    }

    private func executeTargetNavigation(route: ValidatedRetargetingRoute) {
        NSLog("[AppNavigator] التنقل إلى مشهد إعادة الاستهداف: %@", route.scene)
        // تنفيذ انتقال وحدة تحكم العرض الداخلية
    }

    private func executeLobbyFallback(reason: String) {
        NSLog("[AppNavigator] الرجوع الآمن إلى ردهة المنزل: %@", reason)
        // عرض إشعار وتوجيه إلى وحدة تحكم عرض الجذر
    }
}

// عنصر نائب لتفويض الخلفية خاص بالتطبيق (ليس OpoInstall SDK API)
class BackendRouteAuthorizer {
    static let shared = BackendRouteAuthorizer()

    func verifyRouteAuthorization(scene: String, targetId: String, token: String, completion: @escaping (Bool) -> Void) {
        // عنصر نائب للفشل عند الإغلاق: يجب الاتصال بـ API تفويض الخلفية المباشر.
        // تتحقق الخلفية الإنتاجية من جلسة المستخدم، وانتهاء صلاحية الرمز، وملكية السلة، وعدم التكرار للاستخدام الفردي.
        let isAuthorized = false
        completion(isAuthorized)
    }
}

التنفيذ الآمن للتنقل: الحفاظ على سلامة خيط واجهة المستخدم (UI Thread)

تصل ردود اتصالات المشهد (Scene callbacks) على دورة حياة UIKit؛ يجب أن تقوم ردود اتصالات التفويض غير المتزامنة بترحيل طفرات واجهة المستخدم مرة أخرى إلى الممثل الرئيسي أو خيط واجهة المستخدم (DispatchQueue.main.async في Swift، runOnUiThread في Kotlin) للحفاظ على سلامة الخيط ومنع خلل العرض المرئي.

مصفوفة قياس الأداء ومسار إعادة الاستهداف من الويب إلى التطبيق

بوابات القياس عن بعد لإعادة الاستهداف من الويب إلى التطبيق

لتقييم أداء حملة إعادة الاستهداف تجريبيًا، يتتبع فريق النمو أربعة مقاييس مسار رئيسية:

  • معدل النقر إلى الظهور (CTR) للافتة: نسبة زوار صفحة ويب الهاتف المحمول الذين ينقرون على لافتة التطبيق الذكية الديناميكية.
  • معدل النقر إلى فتح التطبيق (CAOR): النسبة المئوية لنقرات اللافتة التي تؤدي إلى فتح تطبيق أصلي تم التحقق منه.
  • معدل نجاح استعادة المشهد: النسبة المئوية لجلسات التطبيق المرتبطة بعمق التي تتحقق بنجاح وتعرض المشهد المستهدف دون الرجوع إلى ردهة المنزل.
  • معدل التحويل اللاحق (CVR): نسبة المستخدمين المعاد استهدافهم الذين يكملون إجراءً أساسيًا (مثل الدفع أو التسجيل) ضمن نافذة إسناد محددة (على سبيل المثال، 24 ساعة).

تدقيق منحنيات الاحتفاظ: تجارب الأتراب الخاضعة للرقابة لزوار إعادة الاستهداف

تجارب إعادة الاستهداف تفصل الرفع المعتمد على نية المعالجة (ITT) عن الاحتفاظ المشروط بعد الفتح.

يجب تقييم فعالية إعادة الجذب من خلال تجربة خاضعة للرقابة تميز بوضوح بين رفع أعمال "نية المعالجة" (ITT) والاحتفاظ المشروط بعد الفتح:

  1. معدل التطبيق النشط لنوايا المعالجة (ITT) (At): لتقييم الرفع السببي عبر جميع زوار الويب المؤهلين دون تكييف ما بعد المعالجة، تقارن فرق البيانات أتراب الزوار العشوائية المعرضة للافتات سياقية مقابل مجموعة عشوائية محتجزة معرضة للافتات عامة أو تنقل ويب قياسي:

    At=Active App Users on Day t from Randomized Eligible Web CohortTotal Randomized Eligible Web Visitors on Day 0×100%A_t = \frac{\text{Active App Users on Day } t \text{ from Randomized Eligible Web Cohort}}{\text{Total Randomized Eligible Web Visitors on Day 0}} \times 100\%
  2. الاحتفاظ المشروط بعد الفتح (Rt): لتحليل مدى التصاق التطبيق بين المستخدمين الذين أكملوا بنجاح عملية التسليم من الويب إلى التطبيق، تراقب الفرق الاحتفاظ المشروط عند إطلاق التطبيق الأول:

    Rt=Users Active in App on Day t who Opened the App on Day 0Total Verified Day 0 App-Open Users×100%R_t = \frac{\text{Users Active in App on Day } t \text{ who Opened the App on Day 0}}{\text{Total Verified Day 0 App-Open Users}} \times 100\%

الاحتفاظ المشروط (Rt) وصفي بطبيعته لأن فتح التطبيق هو حدث وسيط بعد المعالجة؛ يجب التحقق من عائد الاستثمار الإجمالي للحملة ورفع إعادة الجذب عبر مقياس نوايا المعالجة (At).

مصفوفة تقييم قناة إعادة الاستهداف من الويب إلى التطبيق التوضيحية

يقارن الجدول أدناه مناهج لافتات الويب إلى التطبيق الأساسية عبر القدرات التقنية والخصائص التشغيلية:

بعد مسار التحويل لافتة الويب الثابتة لافتة Safari الأصلية لافتة إعادة الاستهداف الديناميكية (OpoInstall)
دقة الاستهداف عامة (جميع المستخدمين يرون نفس النسخة) بيانات وصفية ثابتة لمتجر التطبيقات ديناميكية (مخصصة لـ SKU، أو السلة، أو الفئة)
الوصول عبر المنصات عرض واسع في متصفح الويب Safari على منصات Apple المدعومة فقط متصفحات واسعة (Android، iOS، Chrome، Safari)
هدف التسليم داخل التطبيق ردهة المنزل الافتراضية المنزل الافتراضي أو حجة ثابتة مشهد مستهدف مرتبط بعمق (سلة، منتج، عرض)
إدارة الرفض غير مدار / ملف تعريف ارتباط أساسي قمع محكوم بواسطة Safari نافذة تهدئة مكونة من قبل المطور
التفاعل اللاحق تجريبي (قياس لكل أتراب/قناة) تجريبي (قياس لكل أتراب/قناة) تجريبي (قياس لكل أتراب/قناة)

الأسئلة الشائعة (FAQ)

كيف تختلف لافتات التطبيقات الذكية الديناميكية عن اللافتات الذكية الثابتة؟
تعرض اللافتات الذكية الثابتة رسائل برمجية مشفرة، وتوجه جميع المستخدمين إلى شاشة رئيسية عامة أو قائمة متجر تطبيقات. في المقابل، تلتقط لافتات التطبيقات الذكية الديناميكية سياق تصفح الويب للطرف الأول—مثل رمز SKU المحدد للمنتج أو سلة مهجورة—وتحدث أعمالها الفنية، ونسخها، ومعلمات الروابط العميقة في الوقت الفعلي لتوجيه المستخدمين إلى مشاهد محددة داخل التطبيق.
كيف تحافظ إعادة استهداف الويب إلى التطبيق على السياق إذا قام المستخدم بإلغاء تثبيت التطبيق؟
إذا لم يكن لدى المستخدم التطبيق مثبتًا، فإن النقر على اللافتة يوجهه إلى متجر التطبيقات المناسب مع تخزين وجهته المقصودة مؤقتًا على خلفية الإسناد. عندما يقوم المستخدم بتثبيت التطبيق وفتحه لأول مرة، يسترد SDK الروابط العميقة المؤجلة المعلمات المخزنة مؤقتًا لتنفيذ استعادة المشهد بعد التثبيت حيثما تسمح سياسات خصوصية المنصة بذلك.
كيف يمكنني منع لافتات إعادة استهداف الويب إلى التطبيق من التسبب في تحول التخطيط التراكمي (CLS)؟
لتقليل أو تجنب تحول التخطيط، تضع فرق النمو لافتات إعادة الاستهداف كطبقات تراكب ثابتة في التذييل (`position: fixed; bottom: 0; left: 0; right: 0;`)، والتي تطفو فوق المحتوى دون تحويل عناصر DOM. بدلاً من ذلك، إذا تم وضعها في الأعلى، خصص حاوية تخطيط محجوزة مسبقًا في HTML الأولي لمنع المحتوى من القفز عند عرض اللافتة.

ملخص وإطار عمل القرار

تعمل إعادة استهداف زوار الويب عبر الهاتف المحمول عبر لافتات التطبيقات الذكية الديناميكية على سد الفجوة بين حركة مرور تصفح أعلى المسار وتجارب التطبيق الأصلية مع الحفاظ على السياق. يؤدي الاعتماد على مطالبات الشاشة الرئيسية العامة أو روابط المتجر الثابتة إلى تجاهل نية الشراء القيمة وقد يؤدي إلى احتكاك تحويل إضافي يجب قياسه مقابل كفاءة الاستحواذ.

من خلال التقاط إشارات تصفح الويب للطرف الأول، وعرض لافتات HTML مخصصة، وتنفيذ تسليم روابط عميقة تم التحقق منها إلى وحدات تحكم العرض الأصلية، يقلل مهندسو وفرق النمو من احتكاك التحويل ويدعمون التفاعل المستدام مع التطبيق. يجب التحقق من مكاسب الاحتفاظ اللاحقة وعائد الاستثمار للحملة تجريبيًا من خلال تجارب أتراب خاضعة للرقابة.

لاستكشاف تكامل لافتات الويب عبر المنصات وهندسات الروابط العميقة الديناميكية، راجع وثائق تكامل SDK.

مواد ذات صلة

Share this article