El seguiment d’esdeveniments al client es divideix, a grans trets, en tres categories:
- Seguiment basat en codi
- Seguiment visual
- Seguiment automàtic
Aquest article presenta principalment les idees i el procés d’implementació del seguiment automàtic.
1. Seguiment basat en codi
El seguiment basat en codi consisteix a cridar una interfície a cada punt que es vol seguir i pujar directament les dades de l’esdeveniment. La majoria dels serveis d’analítica de tercers ofereixen una api de seguiment basat en codi, cosa que resulta pràctica.
Però això crea un problema: el codi de seguiment queda escampat per tota la lògica de negoci i fortament acoblat amb ella. Quan canvien les pantalles o els fluxos de negoci, no queda clar què cal fer amb els esdeveniments antics ni si en calen de nous. A mesura que creix el nombre d’esdeveniments, mantenir el codi de seguiment dispers es converteix en un malson. Les macros i les classes de fàbrica poden simplificar el codi, però no canvien el problema de fons.
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
// 埋点
APE(kEvent4001);
}
2. Seguiment visual
El seguiment visual configura els punts de recollida mitjançant una eina visual. El client analitza automàticament la configuració i informa dels esdeveniments, cosa que produeix l’anomenat «seguiment sense rastre». Una solució representativa de codi obert és Mixpanel.
3. Seguiment automàtic
El seguiment automàtic, també anomenat seguiment de tots els esdeveniments, no significa realment que no hi hagi instrumentació. Recull i informa de tots els esdeveniments i deixa que el servidor filtri les dades útils.
Com a optimització addicional, el servidor pot enviar un fitxer de configuració perquè el mateix client filtri els esdeveniments.
Pels KPI, vaig reinventar la roda a partir d’aquesta idea i vaig crear WKTrackingData.
Aquí recullo alguns problemes i idees sorgits en implementar WKTrackingData.
La idea d’implementació és senzilla. El codi s’organitza així:

Aquí, WKTrackingDataManager gestiona totes les dades i la configuració del seguiment d’esdeveniments.

WKTrackingDataViewPathHelper genera els valors event_path.
AOP és el mòdul responsable de seguir tots els esdeveniments.
Per consultar-ne l’ús detallat, visita GitHub.
Com es pot identificar un esdeveniment de manera única?
Hi ha dues preguntes: com representem un esdeveniment i com en garantim la unicitat?
Representar un esdeveniment
Els esdeveniments es representen mitjançant event_path. Per al responder que activa un esdeveniment, la idea principal és seguir la seva cadena de responsors i construir el path d’aquesta cadena.
El path del responsor resultant té aquest aspecte:
"event_path" = "#buttonClick:#UIButton#UIButton[0]#UIView[0]#ViewController#...#UIApplication#AppDelegate
Identificar un esdeveniment de manera única
En alguns escenaris de negoci, el mateix button o un altre control representa esdeveniments de negoci diferents a mesura que canvien les seves propietats.
Per exemple, un button de la pantalla d’inici pot mostrar Toca per iniciar la sessió quan no hi ha cap sessió iniciada i, després d’iniciar-la, mostrar Verifica la identitat si la verificació d’identitat encara no s’ha completat.
Per a aquest mateix button, l’arbre de vistes no ha canviat, de manera que el path del responsor generat continua sent el mateix.
Altres casos semblants inclouen l’estat activat o desactivat d’un UISwitch, la propietat indexSelect de UISegmentedControl, UIStepper i les interaccions amb una cell de UITableView o UICollectionView.
Per resoldre-ho, WKTrackingData construeix event_path afegint selectivament propietats del mateix control. El event_path resultant té aquest aspecte:
"event_path" = "#buttonClick:#UIButton#UIButton[0]#UIView[0]#ViewController#...#UIApplication#AppDelegate#currentTitle=Button#state=1#enabled=1#selected=0";
Extensions específiques del negoci
Considerem un altre escenari: un carrusel de banner a la pantalla d’inici.
Cada espai de banner té una imatge i un url de destinació proporcionats pel servidor, i les seves posicions són configurables.
Aquí, el banner de cada index no té una destinació fixa. L’esdeveniment de la posició 0 no es pot determinar només a partir de event_path.
Per identificar l’esdeveniment de manera única, cal afegir paràmetres específics del negoci, com ara el url.
WKTrackingData també admet extensions de paràmetres de negoci i permet que el codi que el crida afegeixi paràmetres personalitzats:

Els controls que no s’han de seguir es poden excloure mitjançant wk_ignoreTracking:
self.slider.wk_ignoreTracking = YES;
Altres qüestions
Per seguir diferents esdeveniments, WKTrackingData adopta un enfocament orientat a aspectes i utilitza el runtime per intercanviar directament els mètodes.
Tanmateix, per seguir UIAlertView, UIActionSheet, UITableView i UICollectionView, cal intercanviar els seus mètodes de delegate.
Si la
delegate classja implementa el mètode, només cal intercanviar-lo.Si la
delegate classno l’implementa i tot i així volem seguir l’esdeveniment, cal afegir manualment una implementació deldelegate methodcorresponent.
- (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_));
}
}
}
A la pràctica, però, amb UITableView i UICollectionView, quan els objectes delegate no implementaven el mètode, afegir manualment tableView:didSelectRowAtIndexPath: amb una implementation continuava sense fer que s’activés.
Primer, observem la pila de crides normal de tableView:didSelectRowAtIndexPath::

Aquí, UITableView crida aquests quatre mètodes en seqüència per respondre al toc d’una cell:
- 1. [UITableView _userSelectRowAtPendingSelectionIndexPath:]
- 2. [UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:]
- 3. [UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:isCellMultiSelect:]
- 4. [TableViewController tableView:didSelectRowAtIndexPath:]
L’ordre de crida mostra que UITableView només crida el mètode tableView:didSelectRowAtIndexPath: de la delegate class després d’invocar primer el seu propi mètode _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:isCellMultiSelect:.
Posa un punt d’interrupció a _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:isCellMultiSelect: i examina l’assemblador:

Si la delegate class implementa tableView:didSelectRowAtIndexPath:, tot funciona i la crida a tableView:didSelectRowAtIndexPath: es completa.
Però si la delegate class no implementa tableView:didSelectRowAtIndexPath:, fins i tot afegir manualment tableView:didSelectRowAtIndexPath: amb una implementation no provoca cap intent de respondre a tableView:didSelectRowAtIndexPath:.
En seguir l’assemblador, es veu un jump directe a una altra instrucció.
WKTrackingData adopta un enfocament diferent i intercanvia directament [UITableView _userSelectRowAtPendingSelectionIndexPath:] (amb un enfocament semblant per a UICollectionView). Això permet que UITableView i UICollectionView segueixin les interaccions fins i tot quan l’objecte delegate no implementa el mètode d’esdeveniment de cell corresponent.
4. Reflexions finals
Diversos valors event_path per al mateix esdeveniment
Considerem diverses versions publicades amb petites diferències a les pantalles. El mateix esdeveniment pot tenir aleshores diversos valors event_path.
A la pràctica, aquesta correspondència requereix la cooperació dels companys de servidor. Es pot fer configurable al rerefons.
Optimitzar l’analítica d’esdeveniments
La secció Extensions específiques del negoci explicava com afegir paràmetres de negoci. WKTrackingData ho implementa mitjançant categories.
A la pràctica, el seguiment de tots els esdeveniments malgastava una gran quantitat d’amplada de banda. Molts d’aquests esdeveniments eren innecessaris [(ŏ_ŏ)].
Una optimització addicional és que el servidor enviï un fitxer de configuració amb la llista de tots els esdeveniments dels quals vol rebre informes. Només es puja un event_path que coincideixi amb la configuració.
En aquest punt, els paràmetres de negoci també es poden obtenir sense WKTrackingData: es pot utilitzar directament kvc.
Per exemple:
key_path = "viewController.banner.url"
Si has arribat fins aquí, obre WKTrackingData i dona-li una estrella. Si tens preguntes, obre una incidència a GitHub o deixa un comentari a sota!