کلائنٹ کی طرف ایونٹ ٹریکنگ عمومی طور پر تین اقسام میں آتی ہے:

  • کوڈ پر مبنی ٹریکنگ
  • بصری ٹریکنگ
  • خودکار ٹریکنگ

یہ مضمون بنیادی طور پر خودکار ٹریکنگ کے خیال اور نفاذ کے عمل کا تعارف ہے۔

1. کوڈ پر مبنی ٹریکنگ

کوڈ پر مبنی ٹریکنگ کا مطلب ہر مطلوبہ مقام پر انٹرفیس بلا کر ایونٹ ڈیٹا براہِ راست اپ لوڈ کرنا ہے۔ زیادہ تر فریقِ ثالث تجزیاتی خدمات آسان کوڈ ٹریکنگ api دیتی ہیں۔

مگر اس سے مسئلہ پیدا ہوتا ہے: ٹریکنگ کوڈ کاروباری منطق میں پھیلا اور اس سے مضبوطی سے جڑا ہوتا ہے۔ اسکرین یا کاروباری بہاؤ بدلنے پر پرانے ایونٹ کا عمل اور نئے کی ضرورت غیر واضح ہوتی ہے۔ تعداد بڑھنے پر بکھرے کوڈ کی دیکھ بھال مشکل بن جاتی ہے۔ میکرو اور فیکٹری کلاس کوڈ مختصر کر سکتے ہیں، بنیادی مسئلہ نہیں بدلتے۔

- (void)viewDidDisappear:(BOOL)animated {
    [super viewDidDisappear:animated];
    // 埋点
    APE(kEvent4001);
}

2. بصری ٹریکنگ

بصری ٹریکنگ بصری ٹول سے جمع کرنے کے مقامات ترتیب دیتی ہے۔ کلائنٹ ترتیب خود پڑھ کر ایونٹ رپورٹ کرتا ہے، جسے ”بے نشان ٹریکنگ“ کہا جاتا ہے۔ ایک معروف اوپن سورس حل ⁨Mixpanel⁩ ہے۔

3. خودکار ٹریکنگ

خودکار یا تمام ایونٹ ٹریکنگ کا مطلب واقعی بغیر ⁨instrumentation⁩ نہیں۔ یہ تمام ایونٹ جمع و رپورٹ کرتی اور مفید ڈیٹا چھانٹنے کا کام سرور کو دیتی ہے۔

مزید بہتری میں سرور ترتیب فائل بھیج سکتا ہے تاکہ کلائنٹ خود ایونٹ چھانٹے۔

⁨KPI⁩ کے لیے، میں نے اسی خیال پر پہیہ دوبارہ ایجاد کیا اور ⁨WKTrackingData⁩ بنایا۔

یہاں ⁨WKTrackingData⁩ کے نفاذ میں پیش آنے والے مسائل اور خیالات درج ہیں۔

نفاذ کا خیال سادہ ہے۔ کوڈ یوں منظم ہے:

اصل ⁨WKTrackingData⁩ ساخت: مینیجر، ایونٹ راستے اور ⁨AOP⁩ ماڈیول

یہاں، WKTrackingDataManager تمام ڈیٹا انتظام اور ایونٹ ٹریکنگ ترتیب سنبھالتا ہے۔

ایونٹ ٹریکنگ ترتیب کے استعمال کی اصل مثال

WKTrackingDataViewPathHelper، event_path قدر بناتا ہے۔

AOP ماڈیول ہر ایونٹ کی ٹریکنگ کا ذمہ دار ہے۔

تفصیلی استعمال کے لیے ⁨GitHub⁩ دیکھیں۔

ایونٹ کی منفرد شناخت کیسے ہو؟

دو سوال ہیں: ایونٹ کیسے ظاہر کریں اور اس کی انفرادیت کیسے یقینی بنائیں؟

ایونٹ کی نمائندگی

ایونٹ event_path سے ظاہر ہوتے ہیں۔ ایونٹ چلانے والے responder کے لیے بنیادی خیال اس کی ⁨responder chain⁩ کی پیروی کر کے اس کا path بنانا ہے۔

نتیجے کا ⁨responder⁩ path یوں ہے:

"event_path" = "#buttonClick:#UIButton#UIButton[0]#UIView[0]#ViewController#...#UIApplication#AppDelegate
ایونٹ کی منفرد شناخت

کچھ کاروباری حالات میں ایک ہی button یا کنٹرول خصوصیات بدلنے پر مختلف ایونٹ ظاہر کرتا ہے۔

مثلاً ہوم button سائن آؤٹ پر سائن ان کے لیے ٹیپ اور سائن ان کے بعد نامکمل تصدیق پر شناخت کی تصدیق دکھا سکتا ہے۔

اسی button کی ⁨view tree⁩ نہیں بدلی، اس لیے بنا ہوا ⁨responder⁩ path یکساں رہتا ہے۔

ایسی مثالوں میں UISwitch کی آن/آف حالت، UISegmentedControl کا indexSelect، UIStepper، اور UITableView یا UICollectionView کے cell تعامل شامل ہیں۔

اس کے لیے ⁨WKTrackingData⁩ کنٹرول کی منتخب خصوصیات event_path میں شامل کرتا ہے۔ نتیجے کا event_path یوں ہے:

"event_path" = "#buttonClick:#UIButton#UIButton[0]#UIView[0]#ViewController#...#UIApplication#AppDelegate#currentTitle=Button#state=1#enabled=1#selected=0";
کاروبار کے مخصوص اضافے

ایک اور صورت: ہوم اسکرین banner ⁨carousel⁩۔

ہر banner جگہ کی تصویر اور منزل url سرور دیتا ہے، اور مقامات قابلِ ترتیب ہیں۔

ہر index پر banner کی منزل مقرر نہیں۔ مقام 0 کا ایونٹ صرف event_path سے معلوم نہیں ہو سکتا۔

منفرد شناخت کے لیے url جیسے کاروباری پیرامیٹر شامل کرنا ہوں گے۔

⁨WKTrackingData⁩ کاروباری پیرامیٹر اضافے بھی دیتا ہے، تاکہ ⁨caller⁩ مخصوص پیرامیٹر شامل کرے:

کاروباری پیرامیٹر سے ایونٹ بڑھانے کی اصل کوڈ مثال

جن کنٹرولز کو ٹریک نہ کرنا ہو انہیں ⁨wk_ignoreTracking⁩ سے خارج کریں:

self.slider.wk_ignoreTracking = YES;

دیگر مسائل

مختلف ایونٹ کے لیے ⁨WKTrackingData⁩ ⁨aspect-oriented⁩ طریقہ اپناتا اور runtime سے براہِ راست طریقے ⁨swizzle⁩ کرتا ہے۔

ٹریکنگ کے لیے UIAlertView، UIActionSheet، UITableView اور UICollectionView کے delegate طریقے ⁨swizzle⁩ کرنا ضروری ہیں۔

  • اگر delegate class پہلے طریقہ نافذ کرتی ہے تو اسے سیدھا ⁨swizzle⁩ کریں۔

  • اگر delegate class نے نافذ نہیں کیا اور پھر بھی ٹریک کرنا ہو تو متعلقہ delegate method کا نفاذ دستی طور پر شامل کریں۔

- (void)wk_swizzleInstanceSelector:(SEL)origSel_ fromClass:(Class)fromClass replaceSelector:(SEL)replaceSel_ originNotImp:(SEL)notImpSel_ {
    Method originalMethod = class_getInstanceMethod([self class], origSel_);
    if (originalMethod) {
        [self wk_swizzleInstanceSelector:origSel_ fromClass:fromClass replaceSelector:replaceSel_];
    } else {
        Method notImpMethod = class_getInstanceMethod(fromClass, notImpSel_);
        // 如果delegateClass没有实现 origSel_ 方法
        // 则给delegateClass的 origSel_ 添加 orginReplaceMethod 的实现
        BOOL didAddNotImpMethod =
        class_addMethod([self class],
                        origSel_,
                        method_getImplementation(notImpMethod),
                        method_getTypeEncoding(notImpMethod));
        if (didAddNotImpMethod) {
            NSLog(@"%@ did add not imp method %@" , NSStringFromClass([self class]) , NSStringFromSelector(notImpSel_));
        }
    }
}

نفاذ یہاں ہے

عملی طور پر UITableView اور UICollectionView کے delegate ⁨objects⁩ نے طریقہ نافذ نہ کیا ہو تو tableView:didSelectRowAtIndexPath: کا implementation دستی طور پر شامل کرنے پر بھی یہ نہیں چلتا۔

پہلے معمول کا tableView:didSelectRowAtIndexPath: ⁨call stack⁩ دیکھیں:

⁨table-view⁩ قطار انتخاب کے لیے اصل ⁨debugger call stack⁩

یہاں، UITableView، cell ٹیپ کے جواب میں یہ چار طریقے ترتیب سے بلاتا ہے:

  • 1. [⁨UITableView⁩ _⁨userSelectRowAtPendingSelectionIndexPath:⁩]
  • 2. [⁨UITableView⁩ _⁨selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:⁩]
  • 3. [⁨UITableView⁩ _⁨selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:isCellMultiSelect:⁩]
  • 4. [⁨TableViewController tableView:didSelectRowAtIndexPath:⁩]

ترتیب سے واضح ہے کہ UITableView، delegate class کا tableView:didSelectRowAtIndexPath: بلانے سے پہلے اپنا _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:isCellMultiSelect: چلاتا ہے۔

اس طریقے پر _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:isCellMultiSelect: ⁨breakpoint⁩ لگا کر ⁨assembly⁩ دیکھیں:

⁨delegate⁩ طریقے کی شاخ دکھانے والی اصل ⁨assembly⁩

اگر delegate class نے tableView:didSelectRowAtIndexPath: نافذ کیا ہو تو سب درست اور tableView:didSelectRowAtIndexPath: ⁨call⁩ مکمل ہوتا ہے۔

مگر delegate class نے tableView:didSelectRowAtIndexPath: نافذ نہ کیا ہو تو tableView:didSelectRowAtIndexPath: کا implementation دستی طور پر شامل کرنے کے باوجود tableView:didSelectRowAtIndexPath: کے جواب کی کوشش نہیں ہوتی۔

⁨assembly⁩ کی پیروی براہِ راست دوسری ⁨instruction⁩ تک jump دکھاتی ہے۔

⁨WKTrackingData⁩ مختلف طریقہ لیتا اور [UITableView _userSelectRowAtPendingSelectionIndexPath:] کو براہِ راست ⁨swizzle⁩ کرتا ہے (UICollectionView کے لیے بھی ملتا جلتا طریقہ)۔ یوں UITableView اور UICollectionView کے delegate ⁨object⁩ میں متعلقہ cell ⁨event⁩ طریقہ نہ ہونے پر بھی تعامل ٹریک ہوتے ہیں۔

4. اختتامی باتیں

ایک ایونٹ کے لیے متعدد ⁨event_path⁩ قدریں

تھوڑی مختلف اسکرینوں والے متعدد جاری نسخوں میں ایک ایونٹ کے کئی event_path ہو سکتے ہیں۔

عملی طور پر اس ⁨mapping⁩ کے لیے سرور ساتھیوں کا تعاون چاہیے۔ اسے ⁨backend⁩ میں قابلِ ترتیب بنایا جا سکتا ہے۔

ایونٹ تجزیات بہتر کرنا

اوپر کاروبار کے مخصوص اضافے میں پیرامیٹر شامل کرنا بیان ہوا۔ ⁨WKTrackingData⁩ اسے ⁨categories⁩ سے نافذ کرتا ہے۔

عملی طور پر تمام ایونٹ ٹریکنگ نے بہت ⁨bandwidth⁩ ضائع کی۔ بہت سے ایونٹ غیر ضروری تھے [(ŏ_ŏ)]۔

مزید بہتری میں سرور مطلوبہ تمام ایونٹ کی ترتیب فائل بھیجے۔ صرف ترتیب سے ملتا event_path اپ لوڈ ہو۔

اب کاروباری پیرامیٹر ⁨WKTrackingData⁩ کے بغیر بھی لیے جا سکتے ہیں: براہِ راست kvc استعمال کریں۔

مثلاً:

key_path = "viewController.banner.url"

یہاں تک پڑھ لیا تو ⁨WKTrackingData⁩ کھول کر ⁨star⁩ دیں۔ سوال ہو تو ⁨GitHub⁩ پر ⁨issue⁩ بنائیں یا نیچے تبصرہ چھوڑیں!