Urmărirea evenimentelor pe client se împarte, în linii mari, în trei categorii:

  • Urmărire prin cod
  • Urmărire vizuală
  • Urmărire automată

Acest articol prezintă în principal ideile și procesul de implementare din spatele urmăririi automate.

1. Urmărire prin cod

Urmărirea prin cod înseamnă apelarea unei interfețe în fiecare punct pe care vrei să-l urmărești, încărcând direct datele evenimentului. Majoritatea serviciilor terțe de analiză oferă un api pentru urmărire prin cod, ceea ce este comod.

Dar aceasta creează o problemă: codul de urmărire este împrăștiat în logica de business și strâns legat de ea. Când ecranele sau fluxurile de business se schimbă, nu este clar ce trebuie făcut cu evenimentele vechi sau dacă sunt necesare altele noi. Pe măsură ce numărul evenimentelor crește, întreținerea codului de urmărire dispersat devine un coșmar. Macrourile și clasele de tip fabrică pot simplifica acest cod, dar nu schimbă fundamental situația.

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

2. Urmărire vizuală

Urmărirea vizuală configurează punctele de colectare printr-un instrument vizual. Clientul interpretează automat configurația și raportează evenimentele, realizând așa-numita „urmărire fără urme”. O soluție reprezentativă cu sursă deschisă este Mixpanel.

3. Urmărire automată

Urmărirea automată, numită și urmărirea tuturor evenimentelor, nu înseamnă de fapt că instrumentarea lipsește. Ea colectează și raportează toate evenimentele, lăsând serverul să filtreze datele utile.

Ca optimizare suplimentară, serverul poate trimite un fișier de configurare pentru ca însuși clientul să filtreze evenimentele.

Pentru KPI-uri, am reinventat roata pornind de la această idee și am construit WKTrackingData.

Aici consemnez câteva probleme și idei din implementarea WKTrackingData.

Ideea implementării este simplă. Codul este organizat astfel:

Arhitectura originală WKTrackingData: manager, căi ale evenimentelor și module AOP

Aici, WKTrackingDataManager gestionează toate datele și configurația urmăririi evenimentelor.

Exemplul original de utilizare pentru configurarea urmăririi evenimentelor

WKTrackingDataViewPathHelper generează valorile event_path.

AOP este modulul responsabil de urmărirea fiecărui eveniment.

Pentru detalii de utilizare, vizitează GitHub.

Cum identificăm unic un eveniment?

Sunt două întrebări: cum reprezentăm un eveniment și cum îi asigurăm unicitatea?

Reprezentarea unui eveniment

Evenimentele sunt reprezentate folosind event_path. Pentru obiectul responder care declanșează un eveniment, ideea centrală este să-i urmărim lanțul de respondenți și să construim calea path a acelui lanț.

Calea path rezultată arată astfel:

"event_path" = "#buttonClick:#UIButton#UIButton[0]#UIView[0]#ViewController#...#UIApplication#AppDelegate
Identificarea unică a unui eveniment

În anumite scenarii de business, același button sau alt control reprezintă evenimente de business diferite pe măsură ce proprietățile sale se schimbă.

De exemplu, un button de pe ecranul principal poate afișa Atinge pentru autentificare când utilizatorul nu este autentificat, apoi Verifică identitatea după autentificare, dacă verificarea identității încă nu este completă.

Pentru același button, arborele vizual nu s-a schimbat, deci calea de respondenți path generată rămâne aceeași.

Cazuri similare includ starea pornit/oprit a UISwitch, proprietatea UISegmentedControl numită indexSelect, UIStepper și interacțiunile din UITableView sau UICollectionView cu o cell.

Pentru a trata această situație, WKTrackingData construiește event_path adăugând selectiv proprietăți ale controlului însuși. Valoarea event_path rezultată arată atunci astfel:

"event_path" = "#buttonClick:#UIButton#UIButton[0]#UIView[0]#ViewController#...#UIApplication#AppDelegate#currentTitle=Button#state=1#enabled=1#selected=0";
Extensii specifice logicii de business

Să luăm alt scenariu: un carusel banner pe ecranul principal.

Fiecare poziție banner are o imagine și un url de destinație furnizate de server, iar pozițiile sunt configurabile.

Aici, elementul banner de la fiecare index nu are o destinație fixă. Evenimentul de la poziția 0 nu poate fi determinat doar din event_path.

Pentru a identifica unic evenimentul, trebuie să adăugăm parametri specifici logicii de business, precum url.

WKTrackingData acceptă și extensii cu parametri de business, permițând apelantului să adauge parametri personalizați:

Exemplul original de cod pentru extinderea evenimentelor cu parametri de business

Controalele care nu trebuie urmărite pot fi excluse folosind wk_ignoreTracking:

self.slider.wk_ignoreTracking = YES;

Alte probleme

Pentru a urmări evenimente diferite, WKTrackingData adoptă o abordare orientată pe aspecte și folosește runtime pentru a schimba direct implementările metodelor.

Însă pentru urmărirea UIAlertView, UIActionSheet, UITableView și UICollectionView, trebuie să schimbăm implementările metodelor lor delegate.

  • Dacă delegate class implementează deja metoda, este suficient să-i schimbăm implementarea.

  • Dacă delegate class nu o implementează și vrem totuși să urmărim evenimentul, trebuie să adăugăm manual o implementare a metodei corespunzătoare 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_));
        }
    }
}

Implementarea este aici

În practică însă, pentru UITableView și UICollectionView, când obiectele lor delegate nu implementau metoda, adăugarea manuală a tableView:didSelectRowAtIndexPath: cu o implementation tot nu o făcea să se declanșeze.

Mai întâi, să privim stiva normală de apeluri pentru tableView:didSelectRowAtIndexPath::

Stiva originală de apeluri din depanator pentru selectarea unui rând de tabel

Aici, UITableView apelează aceste patru metode în ordine pentru a răspunde la atingerea unei cell:

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

Ordinea apelurilor arată că UITableView apelează metoda din delegate class numită tableView:didSelectRowAtIndexPath: abia după ce invocă propria metodă _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:isCellMultiSelect:.

Pune un punct de întrerupere pe _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:isCellMultiSelect: și inspectează codul de asamblare:

Vizualizarea originală a codului de asamblare cu ramificația metodei delegate

Dacă delegate class implementează tableView:didSelectRowAtIndexPath:, totul funcționează și apelul tableView:didSelectRowAtIndexPath: se finalizează.

Dar dacă delegate class nu implementează tableView:didSelectRowAtIndexPath:, nici adăugarea manuală a tableView:didSelectRowAtIndexPath: cu o implementation nu provoacă o încercare de a răspunde la tableView:didSelectRowAtIndexPath:.

Urmărirea codului de asamblare arată un jump direct la altă instrucțiune.

WKTrackingData adoptă aici altă abordare, schimbând direct implementarea [UITableView _userSelectRowAtPendingSelectionIndexPath:] (cu o abordare similară pentru UICollectionView). Aceasta permite UITableView și UICollectionView să urmărească interacțiunile chiar și atunci când obiectul delegate nu implementează metoda evenimentului corespunzător pentru cell.

4. Gânduri de încheiere

Mai multe valori event_path pentru același eveniment

Să luăm mai multe versiuni lansate, ale căror ecrane diferă ușor. Același eveniment poate avea atunci mai multe valori event_path.

În practică, această corespondență necesită colaborarea colegilor care lucrează la server. Poate fi făcută configurabilă în backend.

Optimizarea analizei evenimentelor

Secțiunea extensii specifice logicii de business a descris adăugarea parametrilor de business. WKTrackingData implementează acest lucru prin categorii.

În practică, urmărirea tuturor evenimentelor irosește multă lățime de bandă. Multe dintre acele evenimente nu erau necesare [(ŏ_ŏ)].

O optimizare suplimentară este ca serverul să trimită un fișier de configurare care enumeră toate evenimentele pe care le dorește raportate. Numai un event_path care corespunde configurației este încărcat.

În acest punct, parametrii de business pot fi preluați și fără WKTrackingData: folosește direct kvc.

De exemplu:

key_path = "viewController.banner.url"

Dacă ai citit până aici, deschide WKTrackingData și acordă-i o stea. Dacă ai întrebări, deschide un issue pe GitHub sau lasă un comentariu mai jos!