Asiakaspuolen tapahtumaseuranta jakautuu yleisesti kolmeen ryhmään:
- Koodipohjainen seuranta
- Visuaalinen seuranta
- Automaattinen seuranta
Tämä artikkeli esittelee pääasiassa automaattisen seurannan ajatuksia ja toteutusprosessia.
1. Koodipohjainen seuranta
Koodipohjainen seuranta tarkoittaa rajapinnan kutsumista jokaisessa seurattavassa kohdassa ja tapahtumatietojen lähettämistä suoraan. Useimmat kolmannen osapuolen analytiikkapalvelut tarjoavat tähän kätevän api-rajapinnan.
Tästä syntyy kuitenkin ongelma: seurantakoodi hajaantuu liiketoimintalogiikkaan ja kytkeytyy siihen tiukasti. Kun näkymät tai prosessit muuttuvat, vanhojen tapahtumien kohtalo ja uusien tarve jäävät epäselviksi. Tapahtumamäärän kasvaessa hajanaisen seurantakoodin ylläpito muuttuu painajaiseksi. Makrot ja tehdasluokat voivat yksinkertaistaa koodia, mutta eivät muuta perusongelmaa.
- (void)viewDidDisappear:(BOOL)animated {
[super viewDidDisappear:animated];
// 埋点
APE(kEvent4001);
}
2. Visuaalinen seuranta
Visuaalisessa seurannassa keräyspisteet määritetään visuaalisella työkalulla. Asiakasohjelma jäsentää määrityksen automaattisesti ja raportoi tapahtumat, jolloin syntyy niin sanottu ”jäljetön seuranta”. Yksi tunnettu avoimen lähdekoodin ratkaisu on Mixpanel.
3. Automaattinen seuranta
Automaattinen eli kaikkien tapahtumien seuranta ei oikeasti tarkoita, ettei instrumentointia olisi. Se kerää ja raportoi kaikki tapahtumat, joista palvelin suodattaa hyödylliset tiedot.
Lisäoptimointina palvelin voi lähettää määritystiedoston, jonka avulla asiakasohjelma suodattaa tapahtumat itse.
KPI-mittareiden vuoksi keksin tämän ajatuksen pohjalta pyörän uudelleen ja rakensin WKTrackingDatan.
Kirjaan tässä joitakin WKTrackingDatan toteutuksessa kohtaamiani ongelmia ja ajatuksia.
Toteutusajatus on yksinkertainen. Koodi on järjestetty näin:

Tässä WKTrackingDataManager hoitaa kaiken tiedonhallinnan ja tapahtumaseurannan määritykset.

WKTrackingDataViewPathHelper luo event_path-arvot.
AOP on kaikkien tapahtumien seurannasta vastaava moduuli.
Tarkat käyttöohjeet ovat GitHubissa.
Miten tapahtuma voidaan yksilöidä?
Kysymyksiä on kaksi: miten tapahtuma esitetään ja miten sen yksilöllisyys varmistetaan?
Tapahtuman esittäminen
Tapahtumat esitetään event_path-arvolla. Tapahtuman laukaisevan responder-olion kohdalla perusajatus on seurata sen responder-ketjua ja muodostaa ketjun path.
Tuloksena oleva responder-path näyttää tältä:
"event_path" = "#buttonClick:#UIButton#UIButton[0]#UIView[0]#ViewController#...#UIApplication#AppDelegate
Tapahtuman yksilöinti
Joissakin liiketoimintatilanteissa sama button tai muu ohjain tarkoittaa ominaisuuksiensa muuttuessa eri liiketoimintatapahtumia.
Esimerkiksi etusivun button voi uloskirjautuneena näyttää tekstin Napauta kirjautuaksesi ja sisäänkirjautumisen jälkeen Vahvista henkilöllisyys, jos vahvistus on vielä kesken.
Saman button-ohjaimen näkymäpuu ei ole muuttunut, joten sen luoma responder-path pysyy samana.
Vastaavia tapauksia ovat UISwitch-kytkimen tila, UISegmentedControl-ominaisuus indexSelect, UIStepper sekä UITableView- tai UICollectionView-näkymän cell-vuorovaikutukset.
Tätä varten WKTrackingData lisää event_path-arvoon valikoidusti ohjaimen omia ominaisuuksia. Tuloksena oleva event_path näyttää tältä:
"event_path" = "#buttonClick:#UIButton#UIButton[0]#UIView[0]#ViewController#...#UIApplication#AppDelegate#currentTitle=Button#state=1#enabled=1#selected=0";
Liiketoimintakohtaiset laajennukset
Tarkastellaan toista tilannetta: etusivun banner-karusellia.
Palvelin toimittaa jokaisen banner-paikan kuvan ja kohde-url-osoitteen, ja paikat ovat määritettävissä.
Kunkin banner-kohdan index ei siis johda kiinteään kohteeseen. Paikan 0 tapahtumaa ei voi päätellä pelkästä event_path-arvosta.
Tapahtuman yksilöimiseksi on lisättävä liiketoimintakohtaisia parametreja, kuten url.
WKTrackingData tukee myös liiketoimintaparametrien laajennuksia, joten kutsuja voi lisätä omia parametrejaan:

Ohjaimet, joita ei pidä seurata, voidaan ohittaa wk_ignoreTracking-arvolla:
self.slider.wk_ignoreTracking = YES;
Muita ongelmia
Eri tapahtumien seurantaa varten WKTrackingData käyttää aspektisuuntautunutta lähestymistapaa ja vaihtaa menetelmiä suoraan runtime-ajon aikana.
Seurattaessa UIAlertView-, UIActionSheet-, UITableView- ja UICollectionView-tapahtumia on kuitenkin vaihdettava niiden delegate-menetelmiä.
Jos
delegate classtoteuttaa jo menetelmän, se vain vaihdetaan.Jos
delegate classei toteuta sitä mutta haluamme silti seurata tapahtumaa, vastaavandelegate method-menetelmän toteutus on lisättävä käsin.
- (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_));
}
}
}
Käytännössä huomasin kuitenkin, että kun UITableView- ja UICollectionView-näkymien delegate-oliot eivät toteuttaneet menetelmää, tableView:didSelectRowAtIndexPath:-menetelmän lisääminen käsin implementation-toteutuksella ei vieläkään saanut sitä käynnistymään.
Katsotaan ensin tavallista tableView:didSelectRowAtIndexPath:-kutsupinoa:

Tässä UITableView kutsuu nämä neljä menetelmää järjestyksessä vastatakseen cell-napautukseen:
- 1. [UITableView _userSelectRowAtPendingSelectionIndexPath:]
- 2. [UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:]
- 3. [UITableView _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:isCellMultiSelect:]
- 4. [TableViewController tableView:didSelectRowAtIndexPath:]
Kutsujärjestys osoittaa, että UITableView kutsuu delegate class -luokan tableView:didSelectRowAtIndexPath:-menetelmää vasta oman _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:isCellMultiSelect:-menetelmänsä jälkeen.
Aseta keskeytyskohta menetelmään _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:isCellMultiSelect: ja tarkastele assembly-koodia:

Jos delegate class toteuttaa tableView:didSelectRowAtIndexPath:-menetelmän, kaikki toimii ja tableView:didSelectRowAtIndexPath:-kutsu valmistuu.
Jos delegate class ei toteuta tableView:didSelectRowAtIndexPath:-menetelmää, edes tableView:didSelectRowAtIndexPath:-menetelmän lisääminen käsin implementation-toteutuksella ei johda yritykseen vastata tableView:didSelectRowAtIndexPath:-menetelmään.
Assembly-koodia seuraamalla näkyy suora jump toiseen käskyyn.
WKTrackingData käyttää tässä eri tapaa ja vaihtaa suoraan menetelmän [UITableView _userSelectRowAtPendingSelectionIndexPath:] (ja vastaavasti UICollectionView-näkymässä). Näin UITableView- ja UICollectionView-vuorovaikutukset voidaan jäljittää, vaikka delegate-olio ei toteuta vastaavaa cell-tapahtumamenetelmää.
4. Lopuksi
Samalla tapahtumalla useita event_path-arvoja
Ajattele useita julkaistuja versioita, joiden näkymissä on pieniä eroja. Samalla tapahtumalla voi silloin olla useita event_path-arvoja.
Käytännössä tämä vastaavuus vaatii yhteistyötä palvelinpuolen kollegojen kanssa. Se voidaan tehdä taustajärjestelmässä määritettäväksi.
Tapahtuma-analytiikan optimointi
Edellä Liiketoimintakohtaiset laajennukset -osio kuvasi liiketoimintaparametrien lisäämistä. WKTrackingData toteuttaa sen kategorioilla.
Käytännössä kaikkien tapahtumien seuranta tuhlasi paljon kaistanleveyttä. Monet tapahtumat olivat tarpeettomia [(ŏ_ŏ)].
Lisäoptimointina palvelin voi lähettää määritystiedoston, jossa luetellaan kaikki raportoitavaksi halutut tapahtumat. Vain määritystä vastaava event_path lähetetään.
Tässä vaiheessa liiketoimintaparametrit voi hakea myös ilman WKTrackingDataa: käytä suoraan kvc:tä.
Esimerkiksi:
key_path = "viewController.banner.url"
Jos luit tänne asti, avaa WKTrackingData ja anna sille tähti. Jos sinulla on kysymyksiä, avaa issue GitHubissa tai jätä kommentti alle!