إعلانات بانر في تطبيق iOS



نفتح اليوم سلسلة من المقالات حول ما لا يتم التحدث عنه عادةً في المؤتمرات والاجتماعات الفنية. ستخبرك هذه المشاركات والمشاركات اللاحقة بكيفية عمل آلية تحقيق الدخل في تطبيق iFunny iOS الترفيهي الشهير في الولايات المتحدة الأمريكية ، والذي نقوم بتطويره.

يعد الإعلان أحد الطرق الرئيسية لتحقيق الدخل من التطبيقات المجانية. ولكن الآن ، ما هي الخيارات التي كانت موجودة في عام 2011 عندما ظهر iFunny؟ تم إنشاء الخدمة في الأصل كعمل تجاري قوي ومستدام ، لذلك قررت الشركة منذ اليوم الأول عدم مغازلة المستخدمين وعدم الانخراط في ألعاب ذات رأس مال مشروط.

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

أظهرت الرياضيات البسيطة أنه مع تحويل بنسبة 10 ٪ ، فإن الحصول على متوسط ​​العائد لكل مستخدم (ARPU) بأكثر من 10 سنتات يعد مهمة مستحيلة تقريبًا.

ثم كان عليّ التفكير في طريقة أخرى لتحقيق الدخل من المنتج. لقد عمل النموذج الإعلاني بشكل جيد للغاية على الويب ، ويمكن افتراض أنه سيزهر قريبًا على الهواتف أيضًا.
بشكل عام ، يمكن اعتبار بداية نموذج الإعلان المتحرك لتحقيق الدخل مظهر AdWhirl - وهي خدمة تسمح لك بدمج SDK للشبكات الإعلانية وتدويرها. سمح مظهرها برفع FillRate إلى متوسط ​​50 ٪ في السوق وجعل الدخل من نموذج الإعلان قابل للمقارنة على الأقل بمبيعات دولار واحد. لقد أصبح مبدأ تنفيذ جميع مصادر الطلب الممكنة وتنظيم المنافسة بينها هو المحرك الرئيسي للنمو في صناعة الإعلانات ولا يزال يتم استغلاله حتى يومنا هذا.

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

MoPub وشركاه


منذ عام 2012 ، انتقلنا من AdWhirl إلى MoPub.

MoPub عبارة عن منصة إعلانية للجوال مع إمكانية إضافة وحدات خاصة بها ، والتي تتضمن العديد من الأدوات الرائعة:

  • سوق MoPub - تبادل الإعلانات الخاصة ؛
  • وسيط الشبكات الإعلانية للعمل مع الشبكات الخارجية ؛
  • آلية ترتيب تسمح لك بوضع لافتات بشكل مستقل في التطبيق الخاص بك وتخصيص شاشاتها.

المزايا الرئيسية ل MoPub:

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

لدى MoPub أيضًا عيوب:

  • لا يتم قبول طلبات التجمع على GitHub ولا يوجد رد فعل عليها على الإطلاق ؛
  • لوحة التحكم معقدة للغاية ، وبالنسبة للمطور ، أثناء التصحيح ، يستغرق الأمر بعض الوقت للخوض في هيكلها.

قوة الحقيقة


كما قال بطل أحد الأفلام الروسية: "القوة في الحقيقة". في هذا الجزء ، سأتحدث عن الصعوبات التي كان علينا ، كمطوري التطبيقات ، مواجهتها بعد أول مليون تنزيل لـ iFunny ، ونمو الجمهور وزيارات الإعلانات من أكثر من 100 شريك.

المحتوى


إن سوق الإعلانات عبارة عن "طبقة" مغلقة للغاية لشركات التكنولوجيا ، ولكن في نفس الوقت ، يمتلك المجمِّعون شبكة كبيرة من الشركاء: من الشركات الكبيرة التي تعمل بملايين الميزانيات إلى الشركات الصغيرة المصممة خصيصًا لجماهير مستهدفة محددة.

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

هناك عدة فئات رئيسية من المحتوى "الفاحش" في إعلانات البانر:

  • محتوى إباحي. في الآونة الأخيرة ، يبدو أقل وأقل ، ولكن مع ذلك يحدث. لا يمكننا نشر هذا المحتوى في المقالة ، لذا لن تكون الصورة هنا
  • تنبيهات النظام في اللافتات ، يمكن مشاهدة مثال على أحد المستخدمين twitter.com/IfunnyStates/status/1029393804749668352
  • محتوى بالصوت. لا تُحظر الأصوات من قبل شبكات الإعلانات ، وكذلك الرسوم المتحركة ، ولكن إذا تم تشغيل الصوت دون التفاعل مع الواجهة ، فإن المستخدمين يعتبرون ذلك خطأ في التطبيق ويؤثر سلبًا على تجربة المستخدم
  • جذب الانتباه. يجب أن يجذب لافتة جيدة انتباه المستخدم ، ولكن هذا لا يحدث دائمًا بطريقة صادقة: في بعض الأحيان تقع مقاطع الفيديو الخفقان في اللافتات. هناك طريقة أخرى غير شريفة تجعل المستخدم ينقر على البانر هي محاكاة واجهة التطبيق ، على سبيل المثال:


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

نقرات تلقائية


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

لتكرار هذه المشكلة باستخدام مثال MoPub ، ما عليك سوى إضافة رمز جافا سكريبت للمحتوى التالي إلى البانر:

<a href="https://ifunny.co" id="testbutton">test</a> <script>document.getElementById('testbutton').click(); </script> 

عمل هذا لفترة طويلة في العديد من إصدارات MoPub ، حتى الإصدار 4.13.

من خلال استكشاف تطبيق MoPub ، كان من الممكن إنشاء روابط أكثر تعقيدًا لا تسمح فقط بفتح الإعلانات بملء الشاشة ، ولكن أيضًا إرسال المستخدم إلى AppStore إلى تطبيق معين ولا حتى مراعاة عرض البانر.

بالمناسبة ، في ملاحظات الإصدار للإصدار 4.13.0 من MoPub SDK لنظام iOS لا توجد معلومات حول هذا الإصلاح ، نظرًا لأنه كان ثقبًا خطيرًا إلى حد ما في SDK ، وقد استغله شركاء MoPub غير النزيهين بشكل نشط. كما تظهر السجلات ، التي سأناقشها لاحقًا ، كل يوم كان عليّ منع ما يصل إلى مليوني محاولة لفتح اللافتة دون تفاعل المستخدم معها.

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

تحطم


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

  • النظام

تتضمن هذه الاستثناءات في مكتبة الشبكة ، WKWebView (UIWebView) ، OpenGL.
من الصعب جدًا التأثير بشكل مباشر على هذا النوع من الأعطال ، ولكن كان لا يزال من الممكن التأثير على بعضها ، بعد أن درست سابقًا تشغيل مكون WebView مع WebGL.

هذه هي الطريقة التي يبدو بها تكديس مثل هذه الأعطال:

1 libGPUSupportMercury.dylib gpus_ReturnNotPermittedKillClient + 12
2 AGXGLDriver gldUpdateDispatch + 7132
3 libGPUSupportMercury.dylib gpusSubmitDataBuffers + 172
4 AGXGLDriver gldUpdateDispatch + 12700
5 WebCore WebCore::GraphicsContext3D::reshape(int, int) + 524
6 WebCore WebCore::WebGLRenderingContextBase::initializeNewContext() + 712
7 WebCore WebCore::WebGLRenderingContextBase::WebGLRenderingContextBase(WebCore::HTMLCanvasElement*, WTF::RefPtr<WebCore::GraphicsContext3D>&&, WebCore::GraphicsContext3D::Attributes) + 512
8 WebCore WebCore::WebGLRenderingContext::WebGLRenderingContext(WebCore::HTMLCanvasElement*, WTF::PassRefPtr<WebCore::GraphicsContext3D>, WebCore::GraphicsContext3D::Attributes) + 36
9 WebCore WebCore::WebGLRenderingContextBase::create(WebCore::HTMLCanvasElement*, WebCore::WebGLContextAttributes*, WTF::String const&) + 1272
10 WebCore WebCore::HTMLCanvasElement::getContext(WTF::String const&, WebCore::CanvasContextAttributes*) + 520
11 WebCore WebCore::JSHTMLCanvasElement::getContext(JSC::ExecState&) + 212
12 JavaScriptCore llint_entry + 27340
13 JavaScriptCore llint_entry + 24756
14 JavaScriptCore llint_entry + 24756
15 JavaScriptCore llint_entry + 24756
16 JavaScriptCore llint_entry + 25676
17 JavaScriptCore llint_entry + 24756
18 JavaScriptCore llint_entry + 24656
19 JavaScriptCore vmEntryToJavaScript + 260
20 JavaScriptCore JSC::JITCode::execute(JSC::VM*, JSC::ProtoCallFrame*) + 164
21 JavaScriptCore JSC::Interpreter::executeCall(JSC::ExecState*, JSC::JSObject*, JSC::CallType, JSC::CallData const&, JSC::JSValue, JSC::ArgList const&) + 348
22 JavaScriptCore JSC::profiledCall(JSC::ExecState*, JSC::ProfilingReason, JSC::JSValue, JSC::CallType, JSC::CallData const&, JSC::JSValue, JSC::ArgList const&, WTF::NakedPtr<JSC::Exception>&) + 160
23 WebCore WebCore::JSEventListener::handleEvent(WebCore::ScriptExecutionContext*, WebCore::Event*) + 980
24 WebCore WebCore::EventTarget::fireEventListeners(WebCore::Event&, WebCore::EventTargetData*, WTF::Vector<WebCore::RegisteredEventListener, 1ul, WTF::CrashOnOverflow, 16ul>&) + 616
25 WebCore WebCore::EventTarget::fireEventListeners(WebCore::Event&) + 324
26 WebCore WebCore::EventContext::handleLocalEvents(WebCore::Event&) const + 108
27 WebCore WebCore::EventDispatcher::dispatchEvent(WebCore::Node*, WebCore::Event&) + 876
28 WebCore non-virtual thunk to WebCore::HTMLScriptElement::dispatchLoadEvent() + 80
29 WebCore WebCore::ScriptElement::execute(WebCore::CachedScript*) + 360
30 WebCore WebCore::ScriptRunner::timerFired() + 456
31 WebCore WebCore::ThreadTimers::sharedTimerFiredInternal() + 144
32 WebCore WebCore::timerFired(__CFRunLoopTimer*, void*) + 24
33 CoreFoundation __CFRUNLOOP_IS_CALLING_OUT_TO_A_TIMER_CALLBACK_FUNCTION__ + 24
34 CoreFoundation __CFRunLoopDoTimer + 868
35 CoreFoundation __CFRunLoopDoTimers + 240
36 CoreFoundation __CFRunLoopRun + 1568
37 CoreFoundation CFRunLoopRunSpecific + 440
38 WebCore RunWebThread(void*) + 452
39 libsystem_pthread.dylib _pthread_body + 236
40 libsystem_pthread.dylib _pthread_start + 280
41 libsystem_pthread.dylib thread_start + 0


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

تبين أن الإصلاح هنا بسيط للغاية:

عند المغادرة في الخلفية ، يتعين عليك التقاط لقطة شاشة للشعار.

إزالة عرض الإعلان من الشاشة بحيث يتوقف مكون WebView باستخدام OpenGL.
عند الخروج من الخلفية ، أعد كل شيء كما كان.

في كود الهدف- C يبدو كالتالي:

 - (void)onWillResignActive { if (self.adView.superview) { UIGraphicsBeginImageContext(self.adView.bounds.size); [self.adView.layer renderInContext:UIGraphicsGetCurrentContext()]; UIImage *adViewScreenShot = UIGraphicsGetImageFromCurrentImageContext(); UIGraphicsEndImageContext(); adViewThumbView = [[UIImageView alloc] initWithImage:adViewScreenShot]; adViewThumbView.backgroundColor = [UIColor clearColor]; adViewThumbView.frame = self.adView.frame; NSInteger adIndex = [self.adView.superview.subviews indexOfObject:self.adView]; [self.adView.superview insertSubview:adViewThumbView atIndex:adIndex]; [self.adView removeFromSuperview]; } } - (void)onDidBecomeActive { if (self.adView && adViewThumbView) { NSInteger adIndex = [adViewThumbView.superview.subviews indexOfObject:adViewThumbView]; [adViewThumbView.superview insertSubview:self.adView atIndex:adIndex]; [adViewThumbView removeFromSuperview]; adViewThumbView = nil; } } 

  • التكامل

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

وكانت آخر حالة من هذا النوع في يونيو من هذا العام ، بعد التحديث التالي لإحدى المكتبات المستخدمة. اقترحت طريقة جديدة لتهيئة المكتبة استخدام singleton لتكوين إعدادات الشبكة.

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

إن قسم iFunny QA قادر على اختبار مكتبات الإعلانات جيدًا ، لذلك تم العثور على هذه المشكلة أثناء اختبار التحديث.

  • غير متوقع

لا يمكن التحكم في هذا النوع من الأعطال على الإطلاق ، لأنه يحدث دون أي تغييرات في العميل.

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

كانت هناك حالات عندما انخفض iFunny الخالي من الحوادث في اليوم من المستوى القياسي 99.8 ٪ إلى 80 ٪ ، وكان عدد التعليقات الغاضبة في القصة في العشرات.

الأداء


تستخدم إعلانات البانر ، كقاعدة عامة ، مكونات WebView لعرض الإعلانات ، لذا فإن كل بانر يظهر هو تهيئة لبرنامج WebView جديد بكل تبعياته.

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

يحدث بعد الترقية وجود تسرب للذاكرة داخل المكتبة الجديدة. بعد ظهور أداة Memory Graph في Xcode ، أصبح من السهل جدًا العثور على تسريبات في مكتبات الجهات الخارجية ، لذلك يمكن الآن إبلاغ الشركاء عنها بسرعة.

أدناه هو صورة GIF من iFunny خاملاً عندما لا يكون هناك إعلان للمستخدم:



الحلول


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

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

نظام التسجيل


الآن انتشر نظام تسجيل الاستثناءات في iFunny إلى التطبيق بأكمله: لهذا ، نستخدم الواجهة الخلفية الخاصة بنا مع قاعدة على ClickHouse والعرض في Grafana.

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

هناك العديد من المكونات ذات الصلة لتحديد ما إذا تم تحويل المكالمة إلى iFunny. سأخبركم المزيد عن كل منهم.

IFAdView


هذا هو سليل فئة MPAdView (وهو مسؤول عن عرض الإعلانات على MoPub).

تم تجاوز أسلوب hitTest: withEvent في هذه الفئة:

 - (UIView *)hitTest:(CGPoint)point withEvent:(UIEvent *)event { UIView *hitView = [super hitTest:point withEvent:event]; if (hitView) { [[IFAdsExceptionManager instance] triggerTouchView]; } return hitView; } 

وبالتالي ، وضعنا الزناد على حقيقة تفاعل المستخدم مع الإعلان.

بروتوكول IFURL


نرث من NSURLProtocol ووصف الطريقة:

 + (BOOL)canInitWithRequest:(NSURLRequest *)request { __weak NSString *wRequestURL = request.URL.absoluteString; dispatch_async(dispatch_get_main_queue(), ^{ if (wRequestURL == nil) return; if ([wRequestURL hasPrefix:@"itms-appss://itunes.apple.com"] || [wRequestURL hasPrefix:@"itms-apps://itunes.apple.com"] || [wRequestURL hasPrefix:@"itmss://itunes.apple.com"] || [wRequestURL hasPrefix:@"http://itunes.apple.com"] || [wRequestURL hasPrefix:@"https://itunes.apple.com"]) { [[IFAdsExceptionManager instance] adsTriggerItunesURL:wRequestURL]; } }); return NO; } 

هذا هو مشغل لفتح AppStore من التطبيق ، نقوم بإدراج جميع عناوين URL المتاحة لهذا.

IFAdsExceptionManager


فئة تقوم بجمع المشغلات وإنشاء سجل استثناء في السجل.

لتوضيح نوع المحفزات ، سأصف كل طريقة من واجهات هذه الفئة.

 - (void)triggerTouchView;       . <source lang="objectivec">- (void)triggerItunesURL:(NSString *)itunesURL; 

مشغل يحدد ما إذا كانت إعادة التوجيه تحدث في iTunes.

 - (void)triggerResignActive; 

محفز لتحديد فقدان النشاط بواسطة التطبيق. يقارن المحفزين السابقين.

 - (void)resetTriggers; 

إعادة تشغيل الزناد. نسميها عند مغادرة الخلفية أو عندما نفتح AppStore بأنفسنا ، على سبيل المثال ، عندما نرسل المستخدم للتقييم في الإصدارات القديمة من iOS.

 @property (nonatomic, strong) FNAdConfigurationInfo *lastRequestedConfiguration; @property (nonatomic, strong) FNAdConfigurationInfo *lastLoadedConfiguration; @property (nonatomic, strong) FNAdConfigurationInfo *lastFailedConfiguration; 

خصائص تسجيل آخر الإعلانات التي تم طلبها وتنزيلها بنجاح أو دون جدوى. مطلوب لتشكيل رسالة في السجل.

يمكن ملاحظة أن الخوارزمية اتضح أنها بسيطة للغاية ولكنها فعالة. يسمح لنا بتتبع الاكتشافات التلقائية من MoPub ، ولكن أيضًا من الشبكات الأخرى.

في الآونة الأخيرة ، غالبًا ما تفتح الإعلانات ذات الفتح التلقائي SKStoreProductViewController ، لذلك نعمل الآن على تعريف الفتح التلقائي لوحدة التحكم هذه. ستكون خوارزمية تحديد هذا الاستثناء أكثر تعقيدًا بعض الشيء ، ولكن Objective-C Runtime ستساعدنا هنا.

موقف محلي


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

يتكون الجناح من:

  • عامل البناء
  • الأجهزة
  • مجموعة اختبار لكل موفر

أحد الحلول المثيرة للاهتمام المستخدمة في المنصة هو IDFA من شكاوى المستخدمين للإعلان الحقيقي.

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

يتم ذلك بسهولة إلى حد ما باستخدام Objective-C Runtime و swizzling.
تحتاج إلى استبدال طريقة advertisingIdentifier لفئة ASIdentifierManager.

هنا نقوم بذلك من خلال الفئة:

 @interface ASIdentifierManager (IDFARewrite) @end @implementation ASIdentifierManager (IDFARewrite) + (void)load { static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{ if (AdsMonitorTests.customIDFA != nil) { [self swizzleIDFA]; } }); } + (void)swizzleIDFA { Class class = [self class]; SEL originalSelector = @selector(advertisingIdentifier); SEL swizzledSelector = @selector(swizzled_advertisingIdentifier); Method originalMethod = class_getInstanceMethod(class, originalSelector); Method swizzledMethod = class_getInstanceMethod(class, swizzledSelector); BOOL didAddMethod = class_addMethod(class, originalSelector, method_getImplementation(swizzledMethod), method_getTypeEncoding(swizzledMethod)); if (didAddMethod) { class_replaceMethod(class, swizzledSelector, method_getImplementation(originalMethod), method_getTypeEncoding(originalMethod)); } else { method_exchangeImplementations(originalMethod, swizzledMethod); } } #pragma mark - Method Swizzling - (NSUUID *)swizzled_advertisingIdentifier { NSUUID *result = AdsMonitorTests.customIDFA; return result; } @end 

يتم استخدام الطريقة الموضحة في المقالة لنقل معرّف المستخدم إلى البنية من وكيل البناء.

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

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

بشكل عام ، هناك شيء نقوله.

Source: https://habr.com/ru/post/ar426553/


All Articles