A kliensoldali eseménykövetés nagyjából három kategóriára osztható:

  • Kódalapú követés
  • Vizuális követés
  • Automatikus követés

Ez a cikk elsősorban az automatikus követés mögötti elképzeléseket és megvalósítási folyamatot mutatja be.

1. Kódalapú követés

A kódalapú követés során minden követni kívánt ponton meghívunk egy interfészt, és közvetlenül feltöltjük az eseményadatokat. A legtöbb külső elemzőszolgáltató kínál kódalapú követési api felületet, ami kényelmes.

Ez azonban problémát okoz: a követőkód szétszóródik az üzleti logikában, és szorosan összekapcsolódik vele. Ha a képernyők vagy az üzleti folyamatok megváltoznak, nem egyértelmű, mi legyen a régi eseményekkel, és kellenek-e újak. Az eseményszám növekedésével a szétszórt követőkód karbantartása rémálommá válik. Makrók és gyárosztályok egyszerűsíthetik a kódot, de a lényeget nem változtatják meg.

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

2. Vizuális követés

A vizuális követés egy vizuális eszközzel állítja be az adatgyűjtési pontokat. A kliens automatikusan feldolgozza a konfigurációt, és jelenti az eseményeket, így jön létre az úgynevezett „nyomtalan követés”. Egy jellemző nyílt forrású megoldás a Mixpanel.

3. Automatikus követés

Az automatikus, más néven teljes eseménykövetés valójában nem jelenti a műszerezés hiányát. Minden eseményt összegyűjt és jelent, a hasznos adatok kiválogatását pedig a szerverre bízza.

További optimalizálásként a szerver konfigurációs fájlt küldhet, hogy a kliens maga szűrje az eseményeket.

A KPI-k kedvéért újra feltaláltam a kereket ezen elképzelés alapján, és elkészítettem a WKTrackingData eszközt.

Itt feljegyzek néhány problémát és ötletet a WKTrackingData megvalósításából.

A megvalósítás alapötlete egyszerű. A kód így épül fel:

A WKTrackingData eredeti felépítése: kezelő, eseményútvonalak és AOP-modulok

Itt a WKTrackingDataManager kezeli az összes adatot és az eseménykövetés konfigurációját.

Az eseménykövetési konfiguráció használatának eredeti példája

A WKTrackingDataViewPathHelper állítja elő az event_path értékeket.

Az AOP modul felel az összes esemény követéséért.

A részletes használatot a GitHubon találod.

Hogyan azonosítható egyértelműen egy esemény?

Két kérdés merül fel: hogyan ábrázoljunk egy eseményt, és hogyan biztosítsuk az egyediségét?

Esemény ábrázolása

Az eseményeket az event_path segítségével ábrázoljuk. Az eseményt kiváltó responder esetében az alapötlet a válaszadólánc követése és a lánc path útvonalának felépítése.

Az így kapott válaszadói path így néz ki:

"event_path" = "#buttonClick:#UIButton#UIButton[0]#UIView[0]#ViewController#...#UIApplication#AppDelegate
Esemény egyértelmű azonosítása

Egyes üzleti helyzetekben ugyanaz a button vagy más vezérlőelem különböző üzleti eseményeket jelöl, amikor a tulajdonságai megváltoznak.

Például a kezdőképernyő egyik button eleme kijelentkezve a Koppints a bejelentkezéshez feliratot mutathatja, bejelentkezés után pedig a Személyazonosság igazolása feliratot, ha az ellenőrzés még nem fejeződött be.

Ugyanannál a button elemnél a nézetfa nem változott, ezért a generált válaszadói path is ugyanaz marad.

Hasonló eset a UISwitch be- és kikapcsolt állapota, a UISegmentedControl tulajdonsága, az indexSelect, a UIStepper, valamint a UITableView és a UICollectionView interakciói egy cell elemmel.

Ennek kezelésére a WKTrackingData úgy építi fel az event_path értéket, hogy szelektíven hozzáfűzi a vezérlő saját tulajdonságait. Az így kapott event_path ekkor így néz ki:

"event_path" = "#buttonClick:#UIButton#UIButton[0]#UIView[0]#ViewController#...#UIApplication#AppDelegate#currentTitle=Button#state=1#enabled=1#selected=0";
Üzleti bővítések

Nézzünk egy másik helyzetet: egy kezdőképernyős banner körhintát.

Minden banner helyhez tartozik a szervertől kapott kép és cél-url, a pozíciók pedig konfigurálhatók.

Itt az egyes banner elemek célja minden index esetén változhat. A 0 pozíció eseménye nem állapítható meg pusztán az event_path alapján.

Az esemény egyértelmű azonosításához üzleti paramétereket, például url értéket kell hozzáfűznünk.

A WKTrackingData az üzleti paraméterek bővítését is támogatja, így a hívó saját paramétereket fűzhet hozzá:

Eredeti kódpélda az események üzleti paraméterekkel való bővítésére

A nem követendő vezérlők a wk_ignoreTracking segítségével kizárhatók:

self.slider.wk_ignoreTracking = YES;

További problémák

A különböző események követéséhez a WKTrackingData aspektusorientált megközelítést használ, és a runtime segítségével közvetlenül cseréli a metódusokat.

A UIAlertView, UIActionSheet, UITableView és UICollectionView követéséhez viszont a delegate metódusaikat kell kicserélnünk.

  • Ha a delegate class már megvalósítja a metódust, egyszerűen cseréljük ki.

  • Ha a delegate class nem valósítja meg, és mégis követni szeretnénk az eseményt, kézzel kell hozzáadnunk a megfelelő delegate method megvalósítását.

- (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 megvalósítás itt található

A gyakorlatban azonban a UITableView és a UICollectionView esetében, ha a delegate objektum nem valósította meg a metódust, a tableView:didSelectRowAtIndexPath: kézi hozzáadása egy implementation megadásával sem váltott ki hívást.

Először nézzük meg a szokásos tableView:didSelectRowAtIndexPath: hívási vermet:

Az eredeti hibakereső hívási verme egy táblázatsor kiválasztásakor

Itt a UITableView egymás után ezt a négy metódust hívja meg egy cell elemre koppintás kezeléséhez:

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

A hívási sorrendből látszik, hogy a UITableView csak azután hívja a delegate class metódusát, a tableView:didSelectRowAtIndexPath: metódust, hogy meghívta a saját _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:isCellMultiSelect: metódusát.

Tegyél töréspontot a _selectRowAtIndexPath:animated:scrollPosition:notifyDelegate:isCellMultiSelect: metódusra, és vizsgáld meg az assembly kódot:

Eredeti assembly nézet a delegált metódus elágazásával

Ha a delegate class megvalósítja a tableView:didSelectRowAtIndexPath: metódust, minden működik, és a tableView:didSelectRowAtIndexPath: hívás végbemegy.

Ha viszont a delegate class nem valósítja meg a tableView:didSelectRowAtIndexPath: metódust, még a tableView:didSelectRowAtIndexPath: kézi hozzáadása egy implementation megadásával sem vezet ahhoz, hogy megpróbáljon válaszolni a tableView:didSelectRowAtIndexPath: hívásra.

Az assembly követése közvetlen jump utasítást mutat egy másik utasításra.

A WKTrackingData itt más megközelítést választ: közvetlenül kicseréli a [UITableView _userSelectRowAtPendingSelectionIndexPath:] metódust (hasonlóan a UICollectionView esetében). Így a UITableView és a UICollectionView akkor is követheti az interakciókat, ha a delegate objektum nem valósítja meg a megfelelő cell eseménymetódust.

4. Záró gondolatok

Több event_path érték ugyanahhoz az eseményhez

Tegyük fel, hogy több kiadott verzió létezik, apró képernyőeltérésekkel. Ugyanahhoz az eseményhez ekkor több event_path érték tartozhat.

A gyakorlatban ehhez a megfeleltetéshez együtt kell működni a szerveroldali kollégákkal. A háttérrendszerben konfigurálhatóvá tehető.

Az eseményelemzés optimalizálása

Az Üzleti bővítések rész bemutatta az üzleti paraméterek hozzáadását. A WKTrackingData ezt kategóriákkal valósítja meg.

A gyakorlatban a teljes eseménykövetés rengeteg sávszélességet pazarolt. Sok eseményre nem volt szükség [(ŏ_ŏ)].

További optimalizálásként a szerver olyan konfigurációs fájlt küldhet, amely felsorolja az összes jelenteni kívánt eseményt. Csak a konfigurációnak megfelelő event_path kerül feltöltésre.

Ezen a ponton az üzleti paraméterek a WKTrackingData nélkül is lekérhetők: közvetlenül a kvc használatával.

Például:

key_path = "viewController.banner.url"

Ha eddig eljutottál, nyisd meg a WKTrackingData projektet, és adj neki egy csillagot. Ha kérdésed van, nyiss egy issue-t a GitHubon, vagy szólj hozzá alább!