Swift apps kontra crash reporting
Není to tak dlouho, co jsme začali zkoumat možnosti různých crash reportingových služeb pro iOS. Dlouhodobě pro tyto účely využíváme HockeyApp, nicméně se nám již delší dobu zdálo, že nám velice často práci spíše komplikuje, místo toho, aby nám ji ulehčovala. Z tohoto důvodu jsme začali hledat službu, která by nám svým přístupem vyhovovala více. Reálně na scéně zůstaly dvě služby – Crashlytics od Fabricu a Firebase Crash Reporting od Google.
Ke Crashlytics jsme se dostali skrze požadavky některých našich klientů, zatímco Firebase byl v jiných aplikacích zvolen díky komplexnosti svých služeb, jelikož aplikace využívala i jiné služby než jen crash reporting (celá tato zkušenost započala ještě před ohlášeným spojením těchto služeb). Samozřejmě s využitím nových služeb vyvstaly nové problémy, které doposud nebylo třeba řešit.
Pohodlí nadevše
Na Crashlytics nám vadilo, že vytvoření verze aplikace ve webovém dashboardu probíhá až po prvním spuštění aplikace, což je spíše drobnost, kterou jsme vyřešili automatickým spuštěním aplikace v rámci nahrávání buildu ať už do iTunes Connect, nebo kamkoliv jinam.
Bez debug symbolů ani ránu
Daleko větší nepříjemnosti se objevily na Firebase. Primární problém je v poněkud netransparentním nahrávání debug symbolů. Upload skript v rámci Xcode build phase zní jako fajn nápad, nicméně pro vývoj se nejedná o šťastné řešení, jelikož nahrávání debug symbolů nějakou chvíli trvá, což prodlužuje už tak vysoký build time. Nezbylo tedy než nahrávání v development prostředí zrušit.
# Replace this with the GOOGLE_APP_ID from your GoogleService-Info.plist file
GOOGLE_APP_ID=…
if [ "$CONFIGURATION" != "Debug" ]; then # or whatever configuration works for you
# Replace the /Path/To/ServiceAccount.json with the path to the key you just downloaded
"${PROJECT_DIR}"/Libs/Firebase/Crash/upload-sym "/Path/To/ServiceAccount.json"
fi
Každopádně nahrávání debug symbolů úplně nepatří do build phase projektu, přeci jen preferujeme nahrávání debug symbolů v rámci skriptu, který pouští continous integration server. Tuto možnost Firebase také umožňuje, ale zatím se nejedná o šťastné řešení, jelikož využívaný skript nejspíš není úplně vyladěn a především mu nebylo možné předat archiv se všemi debug symboly, ale bylo třeba jej rozbalit a předávat jednotlivé soubory zvlášť, což prodlužovalo jeho trvání. Stejně tak je problém, že webové API neumožňuje vzít archiv se symboly a jednoduše je nahrát.
Dále je značně matoucí, že i v případě důsledného nastavení podle dokumentace, nejsou nahrány všechny debug symboly a webové rozhraní doporučuje jejich nahrání, přestože nejsou úplně snadno dohledatelné. Otázku chybějících debug symbolů jsme řešili i s podporou Firebase – neúspěšně.
Problém za problémem
Zásadním problémem obou služeb jak Firebase, tak Crashlytics, se ukázalo, že ani jedna z nich nedokázala osymbolovat některé typy crashů. Jelikož naše appky píšeme reaktivně, je náš kód občas “vyšperkovaný” pěkným množství closures, s čímž si ani jedna ze služeb nebyla schopna slušně poradit. Z crash reportu se dalo zjistit, v jakém vznikl souboru, ale to bylo vše, řádek už se vyčíst nedal. Ale světe div se, stejný crash, stejné debug symboly, byl v HockeyApp osymbolován správně a především s číslem řádku.
Každopádně jsme problém začali řešit s podporou obou služeb, za tímto účelem jsme vytvořili i example projekt, nicméně řešení problému působilo velice krkolomě a chvílemi se zdálo, jako by Crashlytics ani Firebase o vyřešení situace ani nestály. Běžný response time byl cca týden, někdy i více a řešení nebylo vlastně žádné, dokonce se zdálo, že example projekt byla zbytečná ztráta času už třeba proto, že se podpora opakovaně ptala na build settings, která se snadno dala vyčíst právě z example projektu. Nakonec z obou podpor přišla odpověď typu "inženýři se na to dívají, dáme vědět až bude něco nového", pochopitelně od té doby se nic nového neobjevilo.
Samozřejmě je example crash snadno identifikovatelný díky názvu metody Crashlytics.crash(), nicméně v případě reálné aplikace, která má za úkol více než jen spadnout, to již takto triviální být nemusí.
Alespoň částečné řešení
Metodou pokus-omyl jsme alespoň zjistili, jaké nastavení symbolování v těchto službách rozbíjí. Jedná se o nastavení SWIFT_OPTIMIZATION_LEVEL
. Pokud je optimalizace vypnutá, pak crash reporting funguje správně, v jiných případech ne.
Nicméně tuto variantu nepovažujeme za optimální, a tak nějak věříme, že se třeba s novou verzí Swiftu něco posune. Doufáme, že naše poznatky budou užitečné a případné další tipy jedině uvítáme.