କ୍ଲାଏଣ୍ଟ ପକ୍ଷ ଇଭେଣ୍ଟ ଟ୍ରାକିଂକୁ ମୋଟାମୋଟି ତିନି ପ୍ରକାରରେ ବାଣ୍ଟିହେବ:

  • କୋଡ୍-ଆଧାରିତ ଟ୍ରାକିଂ
  • ଦୃଶ୍ୟ ଟ୍ରାକିଂ
  • ସ୍ୱୟଂଚାଳିତ ଟ୍ରାକିଂ

ଏହି ଲେଖାରେ ମୁଖ୍ୟତଃ ସ୍ୱୟଂଚାଳିତ ଟ୍ରାକିଂର ଧାରଣା ଓ କାର୍ଯ୍ୟାନ୍ୱୟନ ପ୍ରକ୍ରିୟା ବର୍ଣ୍ଣନା କରାଯାଇଛି।

1. କୋଡ୍-ଆଧାରିତ ଟ୍ରାକିଂ

କୋଡ୍-ଆଧାରିତ ଟ୍ରାକିଂ ମାନେ ଟ୍ରାକ୍ କରିବାକୁ ଚାହୁଁଥିବା ପ୍ରତ୍ୟେକ ସ୍ଥାନରେ interface ଡାକି ସିଧାସଳଖ ଇଭେଣ୍ଟ ତଥ୍ୟ ଅପଲୋଡ୍ କରିବା। ଅଧିକାଂଶ ତୃତୀୟ-ପକ୍ଷ ବିଶ୍ଳେଷଣ ସେବା ଏହା ପାଇଁ ସୁବିଧାଜନକ api ଦିଅନ୍ତି।

କିନ୍ତୁ ଏହା ସମସ୍ୟା ସୃଷ୍ଟି କରେ: ଟ୍ରାକିଂ କୋଡ୍ ବ୍ୟବସାୟ ଯୁକ୍ତିର ସବୁଠାରେ ବିକ୍ଷିପ୍ତ ଓ ତାହା ସହ କଠୋର ଭାବେ ଯୋଡ଼ା ରହେ। ସ୍କ୍ରିନ୍ କିମ୍ବା ପ୍ରବାହ ବଦଳିଲେ ପୁରୁଣା ଇଭେଣ୍ଟର କଣ ହେବ କିମ୍ବା ନୂଆ ଦରକାର କି ନାହିଁ ଅସ୍ପଷ୍ଟ। ସଂଖ୍ୟା ବଢ଼ିଲେ ରକ୍ଷଣାବେକ୍ଷଣ ଦୁର୍ସ୍ୱପ୍ନ ହୁଏ। macro ଓ factory class କୋଡ୍ ସହଜ କରିପାରେ, କିନ୍ତୁ ମୂଳ ସମସ୍ୟା ବଦଳାଏ ନାହିଁ।

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

2. ଦୃଶ୍ୟ ଟ୍ରାକିଂ

ଦୃଶ୍ୟ ଟ୍ରାକିଂ ଏକ ଦୃଶ୍ୟ ଉପକରଣରେ ସଂଗ୍ରହ ସ୍ଥାନ ବିନ୍ୟାସ କରେ। କ୍ଲାଏଣ୍ଟ ଆପେ ବିନ୍ୟାସ ବୁଝି ଇଭେଣ୍ଟ ପଠାଏ, ଯାହାକୁ “ଚିହ୍ନହୀନ ଟ୍ରାକିଂ” କୁହାଯାଏ। ଏହାର ଏକ ପ୍ରତିନିଧି open-source ସମାଧାନ Mixpanel।

3. ସ୍ୱୟଂଚାଳିତ ଟ୍ରାକିଂ

ସ୍ୱୟଂଚାଳିତ କିମ୍ବା full-event ଟ୍ରାକିଂର ଅର୍ଥ ପ୍ରକୃତରେ instrumentation ନଥିବା ନୁହେଁ। ଏହା ସମସ୍ତ ଇଭେଣ୍ଟ ସଂଗ୍ରହ କରି ପଠାଏ, ପରେ server ଉପଯୋଗୀ ତଥ୍ୟ ଛାଣେ।

ଆଉ ସୁଧାର ପାଇଁ server ଏକ ବିନ୍ୟାସ ଫାଇଲ୍ ପଠାଇପାରେ, ଯାହାଦ୍ୱାରା client ନିଜେ ଇଭେଣ୍ଟ ଛାଣେ।

KPI ପାଇଁ, ଏହି ଧାରଣାରେ ମୁଁ ପୁଣି ଚକ ତିଆରି କରି WKTrackingData ଗଢ଼ିଲି।

ଏଠାରେ WKTrackingData କାର୍ଯ୍ୟାନ୍ୱୟନର କିଛି ସମସ୍ୟା ଓ ଧାରଣା ଲେଖୁଛି।

କାର୍ଯ୍ୟାନ୍ୱୟନ ଧାରଣା ସହଜ। କୋଡ୍ ଏଭଳି ସଜାଯାଇଛି:

WKTrackingDataର ମୂଳ ଗଠନ: manager, event path ଓ AOP module

ଏଠାରେ WKTrackingDataManager ସମସ୍ତ ତଥ୍ୟ ପରିଚାଳନା ଓ ଇଭେଣ୍ଟ ଟ୍ରାକିଂ ବିନ୍ୟାସ କରେ।

ଇଭେଣ୍ଟ ଟ୍ରାକିଂ ବିନ୍ୟାସର ମୂଳ ବ୍ୟବହାର ଉଦାହରଣ

WKTrackingDataViewPathHelper, event_path ମୂଲ୍ୟ ତିଆରି କରେ।

AOP ପ୍ରତ୍ୟେକ ଇଭେଣ୍ଟ ଟ୍ରାକ୍ କରୁଥିବା module।

ବିସ୍ତୃତ ବ୍ୟବହାର ପାଇଁ GitHub ଦେଖନ୍ତୁ।

ଏକ ଇଭେଣ୍ଟକୁ ଅନନ୍ୟ ଭାବେ କିପରି ଚିହ୍ନିବା?

ଦୁଇଟି ପ୍ରଶ୍ନ ଅଛି: ଇଭେଣ୍ଟକୁ କିପରି ଦର୍ଶାଇବା ଏବଂ ଏହାର ଅନନ୍ୟତା କିପରି ନିଶ୍ଚିତ କରିବା?

ଇଭେଣ୍ଟ ଦର୍ଶାଇବା

ଇଭେଣ୍ଟକୁ event_pathରେ ଦର୍ଶାଯାଏ। ଇଭେଣ୍ଟ ଆରମ୍ଭ କରୁଥିବା responder ପାଇଁ ମୂଳ ଧାରଣା ହେଉଛି ତାହାର responder chain ଅନୁସରଣ କରି ସେହି chainର path ତିଆରି କରିବା।

ଫଳର responder path ଏଭଳି:

"event_path" = "#buttonClick:#UIButton#UIButton[0]#UIView[0]#ViewController#...#UIApplication#AppDelegate
ଇଭେଣ୍ଟକୁ ଅନନ୍ୟ ଚିହ୍ନଟ

କିଛି ବ୍ୟବସାୟ ପରିସ୍ଥିତିରେ ସମାନ button କିମ୍ବା ନିୟନ୍ତ୍ରଣ ତାହାର property ବଦଳିଲେ ଭିନ୍ନ ବ୍ୟବସାୟ ଇଭେଣ୍ଟ ଦର୍ଶାଏ।

ଉଦାହରଣ ସ୍ୱରୂପ, home screenର button sign out ଥିଲେ ସାଇନ୍ ଇନ୍ ପାଇଁ ଟ୍ୟାପ୍, sign in ପରେ ପରିଚୟ ଯାଞ୍ଚ ଅସମ୍ପୂର୍ଣ୍ଣ ଥିଲେ ପରିଚୟ ଯାଞ୍ଚ ଦେଖାଇପାରେ।

ସମାନ buttonର view tree ବଦଳିନଥିବାରୁ ତିଆରି responder path ସମାନ ରହେ।

ଏହିପରି ଅନ୍ୟ ଉଦାହରଣ: UISwitchର on/off ଅବସ୍ଥା, UISegmentedControlର indexSelect property, UIStepper, ଏବଂ UITableView କିମ୍ବା UICollectionViewର cell ଅନ୍ତର୍କ୍ରିୟା।

ଏହା ପାଇଁ WKTrackingData ନିୟନ୍ତ୍ରଣର ନିଜ property ବାଛି event_pathରେ ଯୋଡ଼େ। ତେବେ ଫଳ event_path ଏଭଳି:

"event_path" = "#buttonClick:#UIButton#UIButton[0]#UIView[0]#ViewController#...#UIApplication#AppDelegate#currentTitle=Button#state=1#enabled=1#selected=0";
ବ୍ୟବସାୟ-ନିର୍ଦ୍ଦିଷ୍ଟ ବିସ୍ତାର

ଆଉ ଏକ ପରିସ୍ଥିତି ଭାବନ୍ତୁ: home screenରେ banner carousel।

ପ୍ରତ୍ୟେକ banner ସ୍ଥାନର ଚିତ୍ର ଓ ଲକ୍ଷ୍ୟ url serverରୁ ଆସେ ଏବଂ ସ୍ଥାନ ବିନ୍ୟାସଯୋଗ୍ୟ।

ପ୍ରତ୍ୟେକ bannerର index ଲକ୍ଷ୍ୟ ସ୍ଥିର ନୁହେଁ। 0 ସ୍ଥାନର ଇଭେଣ୍ଟ କେବଳ event_pathରୁ ନିର୍ଣ୍ଣୟ ହୁଏ ନାହିଁ।

ଅନନ୍ୟ ଚିହ୍ନଟ ପାଇଁ url ପରି ବ୍ୟବସାୟ parameter ଯୋଡ଼ିବା ଦରକାର।

WKTrackingData ମଧ୍ୟ ନିଜସ୍ୱ parameter ଯୋଡ଼ିବାକୁ ବ୍ୟବସାୟ ବିସ୍ତାର ସମର୍ଥନ କରେ:

ବ୍ୟବସାୟ parameter ସହ ଇଭେଣ୍ଟ ବିସ୍ତାରର ମୂଳ କୋଡ୍ ଉଦାହରଣ

ଟ୍ରାକ୍ ନକରିବାକୁ ଥିବା ନିୟନ୍ତ୍ରଣ wk_ignoreTrackingରେ ବାଦ ଦିଆଯାଇପାରେ:

self.slider.wk_ignoreTracking = YES;

ଅନ୍ୟ ସମସ୍ୟା

ଭିନ୍ନ ଇଭେଣ୍ଟ ଟ୍ରାକ୍ କରିବାକୁ WKTrackingData aspect-oriented ପଦ୍ଧତି ଅନୁସରଣ କରି runtimeରେ ସିଧା method swizzle କରେ।

କିନ୍ତୁ UIAlertView, UIActionSheet, UITableView ଓ UICollectionView ଟ୍ରାକ୍ କରିବାକୁ ସେମାନଙ୍କ delegate method swizzle କରିବାକୁ ହୁଏ।

  • ଯଦି delegate class methodଟି ପୂର୍ବରୁ implement କରିଥିଲେ କେବଳ ତାହାକୁ swizzle କରନ୍ତୁ।

  • ଯଦି delegate class implement ନକରିଥିଲେ ମଧ୍ୟ ଇଭେଣ୍ଟ ଟ୍ରାକ୍ କରିବାକୁ ଚାହିଲେ ସମ୍ବନ୍ଧିତ delegate methodର implementation ହାତରେ ଯୋଡ଼ିବାକୁ ହୁଏ।

- (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 object methodଟି implement ନକରିଥିଲେ, tableView:didSelectRowAtIndexPath:ର implementation ହାତରେ ଯୋଡ଼ିଲେ ମଧ୍ୟ ଏହା fire ହେଲା ନାହିଁ।

ପ୍ରଥମେ ସାଧାରଣ tableView:didSelectRowAtIndexPath: call stack ଦେଖନ୍ତୁ:

table-view row selection ପାଇଁ ମୂଳ debugger call stack

ଏଠାରେ UITableView ଏକ cell tapକୁ ଉତ୍ତର ଦେବାକୁ କ୍ରମେ ଚାରିଟି method ଡାକେ:

  • 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: method ଡାକେ।

ସିଧା _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:isCellMultiSelect:ରେ breakpoint ଲଗାଇ assembly ଦେଖନ୍ତୁ:

delegate method branch ଦେଖାଉଥିବା ମୂଳ 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 method ନଥିଲେ ମଧ୍ୟ ଅନ୍ତର୍କ୍ରିୟା ଟ୍ରାକ୍ ହୁଏ।

4. ଶେଷ ଚିନ୍ତା

ସମାନ ଇଭେଣ୍ଟ ପାଇଁ ଏକାଧିକ event_path

ସ୍କ୍ରିନ୍‌ରେ ସାମାନ୍ୟ ତଫାତ ଥିବା ଅନେକ ମୁକ୍ତି ସଂସ୍କରଣ ଭାବନ୍ତୁ। ସମାନ ଇଭେଣ୍ଟର ତେବେ ଏକାଧିକ event_path ହୋଇପାରେ।

ବ୍ୟବହାରରେ ଏହି mapping ପାଇଁ server-side ସହକର୍ମୀଙ୍କ ସହଯୋଗ ଦରକାର। backendରେ ଏହାକୁ ବିନ୍ୟାସଯୋଗ୍ୟ କରାଯାଇପାରେ।

ଇଭେଣ୍ଟ ବିଶ୍ଳେଷଣ ଉନ୍ନତି

ପୂର୍ବ ବ୍ୟବସାୟ-ନିର୍ଦ୍ଦିଷ୍ଟ ବିସ୍ତାର ଅଂଶରେ ବ୍ୟବସାୟ parameter ଯୋଡ଼ିବା କଥା ଥିଲା। WKTrackingData ଏହାକୁ categoryରେ କରେ।

ବ୍ୟବହାରରେ full-event ଟ୍ରାକିଂ ବହୁତ bandwidth ନଷ୍ଟ କଲା। ଅନେକ ଇଭେଣ୍ଟ ଦରକାର ନଥିଲା [(ŏ_ŏ)]।

ଆଉ ସୁଧାର ହେଉଛି server ଏକ ବିନ୍ୟାସ ଫାଇଲ୍‌ରେ ରିପୋର୍ଟ କରିବାକୁ ଚାହୁଁଥିବା ସମସ୍ତ ଇଭେଣ୍ଟ ପଠାଇବ। କେବଳ ବିନ୍ୟାସ ସହ ମେଳ ଥିବା event_path ଅପଲୋଡ୍ ହେବ।

ଏହି ସ୍ଥାନରେ ବ୍ୟବସାୟ parameter ପାଇଁ WKTrackingData ଦରକାର ନାହିଁ; ସିଧା kvc ବ୍ୟବହାର କରିହେବ।

ଉଦାହରଣ:

key_path = "viewController.banner.url"

ଏଯାଏଁ ପଢ଼ିଥିଲେ WKTrackingData ଖୋଲି ଗୋଟିଏ star ଦିଅନ୍ତୁ। ପ୍ରଶ୍ନ ଥିଲେ GitHubରେ issue ଖୋଲନ୍ତୁ କିମ୍ବା ତଳେ ମନ୍ତବ୍ୟ କରନ୍ତୁ!