Sledenje dogodkom na strani odjemalca lahko grobo razdelimo v tri skupine:

  • Sledenje s kodo
  • Vizualno sledenje
  • Samodejno sledenje

Ta članek predstavlja predvsem zamisli in postopek izvedbe samodejnega sledenja.

1. Sledenje s kodo

Sledenje s kodo pomeni klic vmesnika na vsakem mestu, ki mu želimo slediti, in neposredno nalaganje podatkov dogodka. Večina zunanjih analitičnih storitev ponuja priročen api za takšno sledenje.

To pa povzroči težavo: sledilna koda je raztresena po poslovni logiki in tesno povezana z njo. Ko se zasloni ali poslovni tokovi spremenijo, ni jasno, kaj narediti s starimi dogodki in ali so potrebni novi. Z večanjem števila dogodkov postane vzdrževanje raztresene kode nočna mora. Makri in tovarniški razredi lahko kodo poenostavijo, vendar tega v osnovi ne spremenijo.

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

2. Vizualno sledenje

Vizualno sledenje z vizualnim orodjem nastavi točke zbiranja. Odjemalec samodejno razčleni konfiguracijo in sporoča dogodke, kar ustvari tako imenovano »sledenje brez sledi«. Predstavnik odprtokodnih rešitev je Mixpanel.

3. Samodejno sledenje

Samodejno sledenje, imenovano tudi sledenje vsem dogodkom, v resnici ne pomeni odsotnosti instrumentacije. Zbira in sporoča vse dogodke, strežnik pa nato izbere uporabne podatke.

Pri nadaljnji optimizaciji lahko strežnik pošlje konfiguracijsko datoteko, da dogodke filtrira odjemalec sam.

Zaradi KPI-jev sem na osnovi te zamisli znova izumil kolo in izdelal WKTrackingData.

Tukaj beležim težave in zamisli iz izvedbe WKTrackingData.

Zamisel izvedbe je preprosta. Koda je urejena takole:

Izvirna arhitektura WKTrackingData: upravitelj, poti dogodkov in moduli AOP

Tukaj WKTrackingDataManager skrbi za vse upravljanje podatkov in konfiguracijo sledenja dogodkom.

Izvirni primer uporabe konfiguracije sledenja dogodkom

WKTrackingDataViewPathHelper ustvarja vrednosti event_path.

Modul AOP je odgovoren za sledenje vsem dogodkom.

Podrobnosti uporabe najdete na GitHubu.

Kako enolično prepoznamo dogodek?

Obstajata dve vprašanji: kako predstavimo dogodek in kako zagotovimo njegovo enoličnost?

Predstavitev dogodka

Dogodke predstavljamo z event_path. Pri objektu responder, ki sproži dogodek, je osnovna zamisel slediti njegovi verigi odzivnikov in zgraditi path te verige.

Nastala odzivnikova path je videti takole:

"event_path" = "#buttonClick:#UIButton#UIButton[0]#UIView[0]#ViewController#...#UIApplication#AppDelegate
Enolično prepoznavanje dogodka

V nekaterih poslovnih scenarijih isti button ali drug gradnik ob spremembi lastnosti predstavlja različne poslovne dogodke.

Na primer, button na domačem zaslonu lahko neprijavljenemu uporabniku kaže Tapnite za prijavo, po prijavi pa Preverite identiteto, če preverjanje še ni končano.

Pri istem button se drevo pogledov ni spremenilo, zato je ustvarjena odzivnikova path enaka.

Podobni primeri vključujejo vklopljeno/izklopljeno stanje UISwitch, pri UISegmentedControl lastnost indexSelect, UIStepper in interakcije UITableView ali UICollectionView s cell.

Za obravnavo tega WKTrackingData zgradi event_path s selektivnim dodajanjem lastnosti samega gradnika. Nastala event_path je potem takšna:

"event_path" = "#buttonClick:#UIButton#UIButton[0]#UIView[0]#ViewController#...#UIApplication#AppDelegate#currentTitle=Button#state=1#enabled=1#selected=0";
Razširitve poslovne logike

Poglejmo še en scenarij: vrtiljak banner na domačem zaslonu.

Vsako mesto banner ima sliko in ciljni url, ki ju pošlje strežnik, položaje pa je mogoče nastavljati.

Tukaj element banner pri vsakem index nima stalnega cilja. Dogodka na mestu 0 ni mogoče določiti samo iz event_path.

Za enolično prepoznavanje dogodka moramo dodati konkretne poslovne parametre, kot je url.

WKTrackingData podpira tudi razširitve poslovnih parametrov, s katerimi lahko klicatelj doda lastne parametre:

Izvirni primer kode za razširjanje dogodkov s poslovnimi parametri

Gradnike, ki jim ne želimo slediti, lahko izključimo z wk_ignoreTracking:

self.slider.wk_ignoreTracking = YES;

Druge težave

Za sledenje različnim dogodkom WKTrackingData uporablja vidikovno usmerjen pristop in prek runtime neposredno zamenjuje metode.

Pri sledenju UIAlertView, UIActionSheet, UITableView in UICollectionView pa moramo zamenjati metode njihovega delegate.

  • Če delegate class metodo že izvaja, jo preprosto zamenjamo.

  • Če delegate class metode ne izvaja in še vedno želimo slediti dogodku, moramo ročno dodati izvedbo ustrezne 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_));
        }
    }
}

Izvedba je tukaj

V praksi pa pri UITableView in UICollectionView, kadar objekti delegate niso izvajali metode, ročno dodajanje tableView:didSelectRowAtIndexPath: z implementation še vedno ni sprožilo klica.

Najprej si oglejmo običajen klicni sklad metode tableView:didSelectRowAtIndexPath::

Izvirni klicni sklad razhroščevalnika pri izbiri vrstice tabele

Tukaj UITableView za odziv na dotik cell zaporedoma pokliče te štiri metode:

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

Vrstni red kaže, da UITableView pokliče metodo razreda delegate class z imenom tableView:didSelectRowAtIndexPath: šele po klicu lastne metode _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:isCellMultiSelect:.

Nastavite prekinitveno točko na _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:isCellMultiSelect: in preglejte zbirno kodo:

Izvirni prikaz zbirne kode z vejo metode delegata

Če delegate class izvaja tableView:didSelectRowAtIndexPath:, vse deluje in klic tableView:didSelectRowAtIndexPath: se konča.

Če pa delegate class ne izvaja tableView:didSelectRowAtIndexPath:, niti ročno dodajanje tableView:didSelectRowAtIndexPath: z implementation ne povzroči poskusa odziva na tableView:didSelectRowAtIndexPath:.

Sledenje zbirni kodi razkrije neposreden jump na drug ukaz.

WKTrackingData tukaj uporabi drug pristop in neposredno zamenja [UITableView _userSelectRowAtPendingSelectionIndexPath:] (podobno pri UICollectionView). Tako lahko UITableView in UICollectionView sledita interakcijam, tudi če objekt delegate ne izvaja ustrezne metode dogodka cell.

4. Sklepne misli

Več vrednosti event_path za isti dogodek

Predstavljajmo si več izdanih različic z majhnimi razlikami med zasloni. Isti dogodek ima lahko več vrednosti event_path.

V praksi to preslikovanje zahteva sodelovanje strežniških sodelavcev. Nastavljati ga je mogoče v zaledju.

Optimizacija analitike dogodkov

Razdelek Razširitve poslovne logike je opisal dodajanje poslovnih parametrov. WKTrackingData to izvaja s kategorijami.

V praksi je sledenje vsem dogodkom porabilo ogromno pasovne širine. Veliko teh dogodkov ni bilo potrebnih [(ŏ_ŏ)].

Nadaljnja optimizacija je, da strežnik pošlje konfiguracijsko datoteko s seznamom vseh dogodkov, o katerih želi poročila. Naloži se le event_path, ki ustreza konfiguraciji.

Na tej točki lahko poslovne parametre pridobimo tudi brez WKTrackingData: namesto tega neposredno uporabimo kvc.

Na primer:

key_path = "viewController.banner.url"

Če ste prebrali do sem, odprite WKTrackingData in mu dajte zvezdico. Če imate vprašanja, odprite prijavo na GitHubu ali pustite komentar spodaj!