क्लायंट-साइड इव्हेंट ट्रॅकिंगचे ढोबळमानाने तीन प्रकार आहेत:
- कोड-आधारित ट्रॅकिंग
- दृश्य ट्रॅकिंग
- स्वयंचलित ट्रॅकिंग
या लेखात प्रामुख्याने स्वयंचलित ट्रॅकिंगमागील कल्पना आणि अंमलबजावणीची प्रक्रिया सांगितली आहे.
1. कोड-आधारित ट्रॅकिंग
कोड-आधारित ट्रॅकिंग म्हणजे ट्रॅक करायच्या प्रत्येक ठिकाणी इंटरफेस कॉल करून इव्हेंट डेटा थेट अपलोड करणे. बहुतेक तृतीय-पक्ष विश्लेषण सेवा कोड-आधारित ट्रॅकिंग api देतात, त्यामुळे ते सोयीचे आहे.
पण यामुळे एक प्रश्न निर्माण होतो: ट्रॅकिंगचा कोड व्यावसायिक तर्कात सगळीकडे विखुरतो आणि त्याच्याशी घट्ट जोडला जातो. स्क्रीन किंवा प्रक्रिया बदलली की जुन्या इव्हेंटचे काय करायचे, नवे इव्हेंट हवेत का, हे स्पष्ट राहत नाही. इव्हेंटची संख्या वाढली की विखुरलेल्या कोडची देखभाल दुःस्वप्न ठरते. मॅक्रो आणि फॅक्टरी क्लास कोड सोपा करू शकतात, पण मूळ प्रश्न बदलत नाही.
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
// 埋点
APE(kEvent4001);
}
2. दृश्य ट्रॅकिंग
दृश्य ट्रॅकिंगमध्ये दृश्य साधनातून संकलन-बिंदू कॉन्फिगर केले जातात. क्लायंट ही मांडणी आपोआप वाचून इव्हेंट नोंदवतो आणि तथाकथित “ठसे नसलेले ट्रॅकिंग” तयार होते. याचे एक प्रातिनिधिक मुक्त-स्रोत समाधान म्हणजे Mixpanel.
3. स्वयंचलित ट्रॅकिंग
स्वयंचलित ट्रॅकिंग, म्हणजेच पूर्ण-इव्हेंट ट्रॅकिंग, खरे तर कोणतीही नोंदणी नसणे नव्हे. ते सर्व इव्हेंट गोळा करून नोंदवते आणि उपयुक्त डेटा गाळण्याचे काम सर्व्हरकडे सोपवते.
पुढील सुधारणा म्हणून सर्व्हर एक कॉन्फिगरेशन फाइल पाठवू शकतो, ज्यामुळे क्लायंट स्वतःच इव्हेंट गाळेल.
KPI साठी, या कल्पनेभोवती मी पुन्हा चाक शोधले आणि WKTrackingData तयार केले.
येथे WKTrackingData बनवताना आलेल्या काही अडचणी आणि कल्पना नोंदवल्या आहेत.
अंमलबजावणीची कल्पना साधी आहे. कोडची रचना अशी:

इथे 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 कॅरुसेल.
प्रत्येक banner जागेचे चित्र आणि गंतव्य url सर्व्हरकडून येते आणि त्यांची स्थाने कॉन्फिगर करता येतात.
इथे प्रत्येक indexवरील bannerचे गंतव्य कायम नसते. 0 क्रमांकाच्या जागेचा इव्हेंट फक्त event_pathवरून ठरवता येत नाही.
इव्हेंट वेगळा ओळखण्यासाठी urlसारखे व्यवसाय-विशिष्ट पॅरामीटर्स जोडावे लागतात.
WKTrackingData व्यवसाय पॅरामीटर्सचा विस्तारही समर्थित करते, त्यामुळे कॉल करणारा सानुकूल पॅरामीटर्स जोडू शकतो:

ज्या नियंत्रणांचे ट्रॅकिंग नको असेल त्यांना wk_ignoreTracking वापरून वगळता येते:
self.slider.wk_ignoreTracking = YES;
इतर अडचणी
वेगवेगळे इव्हेंट ट्रॅक करण्यासाठी WKTrackingData aspect-oriented पद्धत वापरते आणि मेथड्स थेट swizzle करण्यासाठी runtime वापरते.
मात्र 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 ऑब्जेक्ट्समध्ये मेथड नसताना tableView:didSelectRowAtIndexPath:चे implementation हाताने जोडले, तरी ते चालत नव्हते.
प्रथम सामान्य tableView:didSelectRowAtIndexPath: कॉल स्टॅक पाहू:

इथे 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 classने tableView:didSelectRowAtIndexPath: अंमलात आणले असेल, तर सर्व काही सुरळीत होते आणि tableView:didSelectRowAtIndexPath: कॉल पूर्ण होतो.
पण delegate classने tableView:didSelectRowAtIndexPath: अंमलात आणले नसेल, तर tableView:didSelectRowAtIndexPath:चे implementation हाताने जोडूनही tableView:didSelectRowAtIndexPath:ला प्रतिसाद देण्याचा प्रयत्न होत नाही.
assembly मधून पुढे गेल्यावर थेट दुसऱ्या सूचनेकडे jump होताना दिसते.
इथे WKTrackingData वेगळी पद्धत वापरते: ते थेट [UITableView _userSelectRowAtPendingSelectionIndexPath:]ला swizzle करते; UICollectionViewसाठीही अशीच पद्धत आहे. त्यामुळे delegate ऑब्जेक्टने संबंधित cell इव्हेंट मेथड अंमलात आणले नसले तरी UITableView आणि UICollectionView परस्परक्रिया ट्रॅक होतात.
4. शेवटचे विचार
एका इव्हेंटसाठी अनेक event_path मूल्ये
प्रकाशित झालेल्या अनेक आवृत्त्यांच्या स्क्रीनमध्ये छोटे फरक असू शकतात. त्यामुळे त्याच इव्हेंटसाठी अनेक event_path मूल्ये तयार होऊ शकतात.
प्रत्यक्ष कामात या मॅपिंगसाठी सर्व्हर-साइड सहकाऱ्यांची मदत लागते. ते बॅकएंडमध्ये कॉन्फिगर करता येईल असे बनवता येते.
इव्हेंट विश्लेषणात सुधारणा
वरील व्यवसायाशी संबंधित विस्तार या भागात व्यवसाय पॅरामीटर्स जोडण्याचे वर्णन केले. WKTrackingData हे categories मधून अंमलात आणते.
प्रत्यक्षात पूर्ण-इव्हेंट ट्रॅकिंगमुळे खूप bandwidth वाया गेली. त्यातले अनेक इव्हेंट गरजेचेच नव्हते [(ŏ_ŏ)].
पुढील सुधारणा म्हणजे सर्व्हरने नोंदवायच्या प्रत्येक इव्हेंटची कॉन्फिगरेशन फाइल पाठवणे. मांडणीशी जुळणारा event_path असेल तरच तो अपलोड केला जाईल.
या टप्प्यावर व्यवसाय पॅरामीटर्स WKTrackingDataशिवायही मिळवता येतात: थेट kvc वापरा.
उदाहरणार्थ:
key_path = "viewController.banner.url"
इथपर्यंत वाचले असेल, तर WKTrackingData उघडून star द्या. प्रश्न असल्यास GitHub वर issue उघडा किंवा खाली टिप्पणी करा!