Η καταγραφή συμβάντων στην πλευρά του πελάτη χωρίζεται γενικά σε τρεις κατηγορίες:
- Καταγραφή με κώδικα
- Οπτική καταγραφή
- Αυτόματη καταγραφή
Αυτό το άρθρο παρουσιάζει κυρίως τις ιδέες και τη διαδικασία υλοποίησης της αυτόματης καταγραφής.
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 που ενεργοποιεί ένα συμβάν, η βασική ιδέα είναι να ακολουθήσουμε την αλυσίδα απόκρισης και να δημιουργήσουμε τη path αυτής της αλυσίδας.
Η διαδρομή path του αποκρινόμενου αντικειμένου που προκύπτει είναι η εξής:
"event_path" = "#buttonClick:#UIButton#UIButton[0]#UIView[0]#ViewController#...#UIApplication#AppDelegate
Μοναδικός προσδιορισμός συμβάντος
Σε ορισμένα επιχειρησιακά σενάρια, το ίδιο button ή άλλο στοιχείο ελέγχου αναπαριστά διαφορετικά επιχειρησιακά συμβάντα όταν αλλάζουν οι ιδιότητές του.
Για παράδειγμα, ένα button στην αρχική οθόνη μπορεί να δείχνει Πατήστε για σύνδεση πριν από τη σύνδεση και μετά Επαληθεύστε την ταυτότητα, αν η επαλήθευση δεν έχει ακόμη ολοκληρωθεί.
Για το ίδιο button, το δέντρο προβολών δεν έχει αλλάξει, άρα η παραγόμενη 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 που παρέχει ο διακομιστής, ενώ οι θέσεις ρυθμίζονται.
Εδώ, το banner σε κάθε index δεν έχει σταθερό προορισμό. Το συμβάν στη θέση 0 δεν μπορεί να καθοριστεί μόνο από το event_path.
Για μοναδικό προσδιορισμό του συμβάντος πρέπει να προσθέσουμε επιχειρησιακές παραμέτρους, όπως το url.
Το WKTrackingData υποστηρίζει επίσης επεκτάσεις επιχειρησιακών παραμέτρων, επιτρέποντας στον καλούντα να προσθέσει προσαρμοσμένες παραμέτρους:

Στοιχεία ελέγχου που δεν πρέπει να καταγράφονται μπορούν να εξαιρεθούν με το wk_ignoreTracking:
self.slider.wk_ignoreTracking = YES;
Άλλα προβλήματα
Για την καταγραφή διαφορετικών συμβάντων, το WKTrackingData ακολουθεί προσέγγιση προσανατολισμένη στις όψεις και χρησιμοποιεί το runtime για άμεση ανταλλαγή μεθόδων.
Για την καταγραφή των UIAlertView, UIActionSheet, UITableView και UICollectionView, όμως, πρέπει να ανταλλάξουμε τις μεθόδους του delegate τους.
Αν η
delegate classυλοποιεί ήδη τη μέθοδο, απλώς την ανταλλάσσουμε.Αν η
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: και εξετάστε τον κώδικα assembly:

Αν η delegate class υλοποιεί το tableView:didSelectRowAtIndexPath:, όλα λειτουργούν και η κλήση του tableView:didSelectRowAtIndexPath: ολοκληρώνεται.
Αν όμως η delegate class δεν υλοποιεί το tableView:didSelectRowAtIndexPath:, ακόμη και η χειροκίνητη προσθήκη του tableView:didSelectRowAtIndexPath: με ένα implementation δεν προκαλεί προσπάθεια απόκρισης στο tableView:didSelectRowAtIndexPath:.
Ακολουθώντας τον κώδικα assembly αποκαλύπτεται άμεσο jump σε άλλη εντολή.
Το WKTrackingData επιλέγει εδώ άλλη προσέγγιση, ανταλλάσσοντας απευθείας το [UITableView _userSelectRowAtPendingSelectionIndexPath:] (με παρόμοια προσέγγιση για το UICollectionView). Έτσι τα UITableView και UICollectionView καταγράφουν αλληλεπιδράσεις ακόμη κι όταν το αντικείμενο delegate δεν υλοποιεί την αντίστοιχη μέθοδο συμβάντος του cell.
4. Τελικές σκέψεις
Πολλές τιμές event_path για το ίδιο συμβάν
Σκεφτείτε πολλές κυκλοφορημένες εκδόσεις με μικρές διαφορές στις οθόνες. Το ίδιο συμβάν μπορεί τότε να έχει πολλές τιμές event_path.
Στην πράξη, αυτή η αντιστοίχιση χρειάζεται συνεργασία με τους συναδέλφους του διακομιστή. Μπορεί να γίνει ρυθμιζόμενη στο backend.
Βελτιστοποίηση ανάλυσης συμβάντων
Η ενότητα Επιχειρησιακές επεκτάσεις περιέγραψε την προσθήκη επιχειρησιακών παραμέτρων. Το WKTrackingData το υλοποιεί μέσω κατηγοριών.
Στην πράξη, η καταγραφή όλων των συμβάντων σπαταλούσε πολύ εύρος ζώνης. Πολλά από αυτά τα συμβάντα δεν χρειάζονταν [(ŏ_ŏ)].
Μια επιπλέον βελτιστοποίηση είναι ο διακομιστής να στέλνει αρχείο διαμόρφωσης με λίστα όλων των συμβάντων που θέλει να αναφέρονται. Μεταφορτώνεται μόνο ένα event_path που ταιριάζει στη διαμόρφωση.
Σε αυτό το σημείο, οι επιχειρησιακές παράμετροι μπορούν να ανακτηθούν και χωρίς το WKTrackingData: χρησιμοποιήστε απευθείας το kvc.
Για παράδειγμα:
key_path = "viewController.banner.url"
Αν διαβάσατε μέχρι εδώ, ανοίξτε το WKTrackingData και δώστε του ένα αστέρι. Αν έχετε ερωτήσεις, ανοίξτε ένα issue στο GitHub ή αφήστε σχόλιο παρακάτω!