Client-side ఈవెంట్ ట్రాకింగ్‌ను సాధారణంగా మూడు వర్గాలుగా విభజించవచ్చు:

  • కోడ్ ఆధారిత ట్రాకింగ్
  • దృశ్య ట్రాకింగ్
  • స్వయంచాలక ట్రాకింగ్

ఈ వ్యాసం ప్రధానంగా స్వయంచాలక ట్రాకింగ్ వెనుక ఉన్న ఆలోచనలు, అమలు ప్రక్రియను పరిచయం చేస్తుంది.

1. కోడ్ ఆధారిత ట్రాకింగ్

కోడ్ ఆధారిత ట్రాకింగ్ అంటే ట్రాక్ చేయాల్సిన ప్రతి చోట interfaceను పిలిచి, ఈవెంట్ డేటాను నేరుగా అప్‌లోడ్ చేయడం. చాలా మూడవ పక్ష analytics సేవలు దీనికి api అందిస్తాయి; అది సౌకర్యంగా ఉంటుంది.

కానీ దీనితో సమస్య వస్తుంది: ట్రాకింగ్ కోడ్ business logic అంతటా చెల్లాచెదురై, దానితో గట్టిగా ముడిపడుతుంది. తెరలు లేదా business flows మారినప్పుడు పాత ఈవెంట్‌లను ఏం చేయాలో, కొత్తవి అవసరమో స్పష్టంగా ఉండదు. ఈవెంట్‌ల సంఖ్య పెరిగినప్పుడు చెల్లాచెదురైన కోడ్ నిర్వహణ దుర్భరమవుతుంది. Macros, factory classes కోడ్‌ను సులభం చేయవచ్చు; కానీ ప్రాథమికంగా ఏమీ మారదు.

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

2. దృశ్య ట్రాకింగ్

దృశ్య ట్రాకింగ్ ఒక దృశ్య సాధనం ద్వారా సేకరణ స్థానాలను అమర్చుతుంది. Client ఆ అమరికను స్వయంచాలకంగా అర్థం చేసుకుని ఈవెంట్‌లను నివేదించి, “జాడలేని ట్రాకింగ్” అని పిలిచేదాన్ని అందిస్తుంది. ప్రముఖ open-source పరిష్కారాల్లో Mixpanel ఒకటి.

3. స్వయంచాలక ట్రాకింగ్

Full-event tracking అని కూడా పిలిచే స్వయంచాలక ట్రాకింగ్ అంటే instrumentation అసలు అవసరం లేదని కాదు. ఇది అన్ని ఈవెంట్‌లను సేకరించి నివేదిస్తుంది; ఉపయోగకరమైన డేటాను server వడపోస్తుంది.

మరింత మెరుగుపరచడానికి client స్వయంగా ఈవెంట్‌లను వడపోసేలా server ఒక configuration file పంపవచ్చు.

KPIల కోసం, ఈ ఆలోచన చుట్టూ చక్రాన్ని మళ్లీ తయారు చేసి WKTrackingData నిర్మించాను.

ఇక్కడ WKTrackingData అమలులో ఎదురైన కొన్ని సమస్యలు, ఆలోచనలను రాస్తున్నాను.

అమలు ఆలోచన సరళమైనది. కోడ్ ఈ విధంగా క్రమబద్ధం చేయబడింది:

అసలు WKTrackingData నిర్మాణం: manager, event paths, AOP modules

ఇక్కడ WKTrackingDataManager మొత్తం డేటా నిర్వహణ, ఈవెంట్ ట్రాకింగ్ అమరికను నిర్వహిస్తుంది.

ఈవెంట్ ట్రాకింగ్ అమరికకు అసలు వినియోగ ఉదాహరణ

WKTrackingDataViewPathHelper బాధ్యత event_path విలువలను రూపొందించడం.

ప్రతి ఈవెంట్‌ను ట్రాక్ చేసే బాధ్యత AOP moduleది.

వివరమైన వినియోగానికి GitHub చూడండి.

ఒక ఈవెంట్‌ను ప్రత్యేకంగా ఎలా గుర్తించాలి?

రెండు ప్రశ్నలు ఉన్నాయి: ఈవెంట్‌ను ఎలా చూపాలి, దాని ప్రత్యేకతను ఎలా నిర్ధారించాలి?

ఈవెంట్‌ను చూపడం

ఈవెంట్‌లను event_pathతో చూపుతాం. ఈవెంట్‌ను ప్రేరేపించే responder కోసం, దాని responder chainను అనుసరించి ఆ గొలుసు pathను నిర్మించడమే ప్రధాన ఆలోచన.

ఫలితంగా వచ్చే responder path ఇలా ఉంటుంది:

"event_path" = "#buttonClick:#UIButton#UIButton[0]#UIView[0]#ViewController#...#UIApplication#AppDelegate
ఈవెంట్‌ను ప్రత్యేకంగా గుర్తించడం

కొన్ని business సందర్భాల్లో ఒకే button లేదా control లక్షణాలు మారినప్పుడు వేర్వేరు business ఈవెంట్‌లను సూచిస్తుంది.

ఉదాహరణకు, హోమ్ తెర button సైన్ అవుట్‌లో సైన్ ఇన్ చేయడానికి నొక్కండి అని, సైన్ ఇన్ అయినా గుర్తింపు ధృవీకరణ పూర్తి కాకపోతే గుర్తింపును ధృవీకరించండి అని చూపవచ్చు.

అదే buttonకు view tree మారలేదు; కాబట్టి రూపొందిన responder path కూడా మారదు.

ఇలాంటి business సందర్భాల్లో UISwitch on/off స్థితి, UISegmentedControlకు చెందిన indexSelect లక్షణం, UIStepper, అలాగే UITableView లేదా UICollectionViewలో cell పరస్పర చర్యలు ఉంటాయి.

దీన్ని నిర్వహించడానికి WKTrackingData ఒక event_pathను నిర్మించేటప్పుడు control లక్షణాలను ఎంపిక చేసి జోడిస్తుంది. ఫలితంగా వచ్చే event_path ఇలా ఉంటుంది:

"event_path" = "#buttonClick:#UIButton#UIButton[0]#UIView[0]#ViewController#...#UIApplication#AppDelegate#currentTitle=Button#state=1#enabled=1#selected=0";
Businessకు ప్రత్యేకమైన విస్తరణలు

మరో సందర్భాన్ని చూడండి: హోమ్ తెరపై banner carousel.

ప్రతి banner స్థానానికి server అందించే చిత్రం, వెళ్లాల్సిన url ఉంటాయి; వాటి స్థానాలను అమర్చవచ్చు.

ఇక్కడ ప్రతి indexలోని bannerకు స్థిర గమ్యం లేదు. 0 స్థానంలో ఉన్న ఈవెంట్‌ను event_pathతో మాత్రమే నిర్ణయించలేం.

ఈవెంట్‌ను ప్రత్యేకంగా గుర్తించడానికి url వంటి business parametersను జోడించాలి.

WKTrackingData business parameter విస్తరణలకు కూడా మద్దతు ఇస్తుంది; caller అనుకూల parametersను జోడించవచ్చు:

Business parametersతో ఈవెంట్‌లను విస్తరించే అసలు కోడ్ ఉదాహరణ

ట్రాక్ చేయకూడని controlsను wk_ignoreTrackingతో మినహాయించవచ్చు:

self.slider.wk_ignoreTracking = YES;

ఇతర సమస్యలు

వేర్వేరు ఈవెంట్‌లను ట్రాక్ చేయడానికి WKTrackingData aspect-oriented విధానాన్ని అనుసరించి, runtimeతో methodsను నేరుగా swizzle చేస్తుంది.

అయితే UIAlertView, UIActionSheet, UITableView, UICollectionView ట్రాకింగ్ కోసం వాటి delegate methodsను swizzle చేయాలి.

  • ఆ delegate class ఇప్పటికే methodను అమలు చేస్తే, దాన్ని నేరుగా 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 methodను అమలు చేయనప్పుడు, tableView:didSelectRowAtIndexPath:కు implementationను చేతితో జోడించినా అది పనిచేయలేదు.

ముందుగా సాధారణ tableView:didSelectRowAtIndexPath: call stack చూడండి:

Table-view row ఎంపికకు అసలు debugger call stack

ఇక్కడ, UITableView ఒక cell tapకు స్పందించడానికి ఈ నాలుగు methodsను వరుసగా పిలుస్తుంది:

  • 1. [UITableView _userSelectRowAtPendingSelectionIndexPath:]
  • 2. [UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:]
  • 3. [UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:isCellMultiSelect:]
  • 4. [TableViewController tableView:didSelectRowAtIndexPath:]

Call క్రమం ప్రకారం, UITableView నిజంగా delegate classకు చెందిన tableView:didSelectRowAtIndexPath: methodను పిలిచే ముందు తన _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:isCellMultiSelect: methodను మొదట పిలుస్తుంది.

నేరుగా _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:isCellMultiSelect:పై breakpoint పెట్టి assemblyని చూడండి:

Delegate-method శాఖను చూపే అసలు 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కు కూడా ఇలాంటి విధానం). దీంతో delegate object సంబంధిత cell event methodను అమలు చేయకపోయినా UITableView, UICollectionView పరస్పర చర్యలను ట్రాక్ చేయగలవు.

4. ముగింపు ఆలోచనలు

ఒకే ఈవెంట్‌కు అనేక event_path విలువలు

తెరల్లో స్వల్ప తేడాలు ఉన్న అనేక విడుదలైన versionsను పరిగణించండి. ఒకే ఈవెంట్‌కు అనేక event_path విలువలు ఉండవచ్చు.

ఆచరణలో ఈ mappingకు server-side colleagues సహకారం అవసరం. Backendలో అమర్చగలిగేలా చేయవచ్చు.

ఈవెంట్ analyticsను మెరుగుపరచడం

పైన ఉన్న Businessకు ప్రత్యేకమైన విస్తరణలు విభాగం business parameters జోడించడాన్ని వివరించింది. WKTrackingData దీన్ని categoriesతో అమలు చేస్తుంది.

ఆచరణలో full-event tracking చాలా bandwidthను వృథా చేసింది. ఆ ఈవెంట్‌లలో చాలావి అవసరం లేదు [(ŏ_ŏ)].

మరింత మెరుగుపరచడానికి నివేదించాల్సిన ప్రతి ఈవెంట్‌ను జాబితా చేసే configuration fileను server పంపవచ్చు. దానికి సరిపోలే event_path ఈవెంట్‌లు మాత్రమే అప్‌లోడ్ అవుతాయి.

ఈ దశలో business parametersను WKTrackingData లేకుండానే పొందవచ్చు: నేరుగా kvc ఉపయోగించండి.

ఉదాహరణ:

key_path = "viewController.banner.url"

ఇంతవరకు చదివితే WKTrackingData తెరిచి ఒక star ఇవ్వండి. ప్రశ్నలు ఉంటే GitHubలో issue తెరవండి లేదా కింద comment చేయండి!