Wie oft verwenden Sie
Swift.assert()
in Ihrem Code? Ich benutze es ehrlich gesagt ziemlich oft (Wenn dies eine schlechte Praxis ist, dann schreibe bitte in die Kommentare - warum ist es schlecht?). In meinem Code sieht man zum Beispiel oft einen solchen Aufruf:
Swift.assert(Thread.isMainThread)
Vor nicht allzu langer Zeit habe ich beschlossen, dass es schön wäre, die Ergebnisse dieser Aufrufe weiterhin zu beobachten, nicht nur als Teil des Starts der Anwendung im Simulator / Gerät, sondern auch aufgrund der Aktionen realer Benutzer. Übrigens können wir hier über
Swift.precondition()
,
Swift.fatalError()
usw. sprechen, obwohl ich versuche, sie zu vermeiden. Ich habe
in dieser Veröffentlichung mehr über nicht
behebbare Fehler in Swift gelesen und es hat sich als sehr informativ herausgestellt.
Näher am Punkt:
im code habe ich ca. 300 solche anrufe gefunden. Keiner von ihnen arbeitete während der üblichen lokalen Tests, und es hat mir gefallen. Ich schlug jedoch vor, dass Benutzeraktionen dennoch einige der Aufrufe auslösen könnten.
Aus Sicht des Benutzers sollten Abstürze nicht auftreten (meiner Meinung nach auch hier). In extremen Fällen muss der Benutzer verstehen, dass ein Szenario fehlgeschlagen ist und das Entwicklungsteam bereits daran arbeitet, es zu beheben. Ähnliche Ausnahmen wurden von mir immer behandelt, und für den Benutzer könnte es in der harmlosesten Form beeinflussen. Beispielsweise war eine von Hunderten von Tabellenzellen einfach unsichtbar.
Mit dem Benutzer ist mehr oder weniger alles klar. Es bleibt die Zustellung der Protokolle an den Entwickler zu erledigen. Erstens war es mit minimalem Aufwand erforderlich, aktuelle Anrufe im Code durch Anrufe zu ersetzen, die Protokolle irgendwo außerhalb der Anwendung senden. Zweitens musste der Ort des Vorfalls genau lokalisiert werden, da es sonst praktisch unmöglich wäre, die Ausnahme mit dem tatsächlichen Code zu korrelieren. Drittens sollte beachtet werden, dass geänderte Aufrufe während des Unit-Tests funktionieren können, wobei
Thread.isMainThread
bereits ignoriert werden sollte. Ich verwende das
RxTest- Framework für bestimmte Testarten (hier bin ich auch bereit, Ratschläge und Kritik zu hören). Der Hauptpunkt blieb, dass lokal alle Ausnahmen wie zuvor ausgelöst werden sollten, d.h.
Loggin.assert()
sollte zur gleichen Zeit
Swift.assert()
wie
Swift.assert()
Fabric (
Crashlytics ) bietet eine hervorragende Möglichkeit, Ereignisse zu senden. Es sieht so aus:
Crashlytics.sharedInstance().recordCustomExceptionName("", reason: ""...
Es bleibt
Crashlytics in ein Framework zu packen, das
vollständig in die Anwendung und in abgeschnittener Form (ohne
Crashlytics- Abhängigkeit) in Testzielen geladen werden kann.
Ich entschied mich für das „
Verpacken “ mit
CocoaPods :
Pod::Spec.new do |s| s.name = 'Logging' ... s.subspec 'Base' do |ss| ss.source_files = 'Source/Logging+Base.swift' ss.dependency 'Crashlytics' end s.subspec 'Test' do |ss| ss.source_files = 'Source/Logging+Test.swift' end end
Der Code für das "Kampfziel" lautet wie folgt:
import Crashlytics public enum Logging { public static func send(_ reason: String? = nil, __file: String = #file, __line: Int = #line) { let file = __file.components(separatedBy: "/").last ?? __file let line = "\(__line)" let name = [line, file].joined(separator: "_") Crashlytics.sharedInstance().recordCustomExceptionName(name, reason: reason ?? "no reason", frameArray: []) } public static func assert(_ assertion: @escaping @autoclosure () -> Bool, reason: String? = nil, __file: String = #file, __line: Int = #line) { if assertion() == false { self.assertionFailure(reason, __file: __file, __line: __line) } } public static func assert(_ assertion: @escaping () -> Bool, reason: String? = nil, __file: String = #file, __line: Int = #line) { if assertion() == false { self.assertionFailure(reason, __file: __file, __line: __line) } } public static func assertionFailure(_ reason: String? = nil, __file: String = #file, __line: Int = #line) { Swift.assertionFailure(reason ?? "") self.send(reason, __file: __file, __line: __line) } }
Für das "Testziel" dh ohne
Crashlytics- Abhängigkeit:
import Foundation public enum Logging { public static func send(_ reason: String? = nil, __file: String = #file, __line: Int = #line) {
Ergebnisse:
Ausnahmen begannen wirklich zu wirken. Die meisten von ihnen meldeten ein falsches Format für die empfangenen Daten:
Decodable
manchmal empfangene Daten mit dem
Decodable
Typ. Manchmal
Thread.isMainThread
die Protokolle für
Thread.isMainThread
, die in den nächsten Releases sehr schnell behoben wurden. Die interessantesten Fehler wurden auf wundersame Weise von
NSException abgefangen .
Vielen Dank für Ihre Aufmerksamkeit.
PS Wenn Sie solche Protokolle zu oft an
Crashlytics senden,
erkennt der Dienst Ihre Aktionen möglicherweise als Spam. Und Sie werden die folgende Meldung sehen:
Aufgrund nicht bestimmungsgemäßer Verwendung wurde die nicht schwerwiegende Berichterstellung für mehrere Builds deaktiviert. Weitere Informationen zum erneuten Aktivieren der Berichterstellung finden Sie in unserer Dokumentation.
Daher ist es empfehlenswert, die Häufigkeit des Versendens von Protokollen im Voraus zu berücksichtigen. Andernfalls besteht die Gefahr, dass alle Build-Protokolle von
Crashlytics ignoriert werden
.