¿Con qué frecuencia usa
Swift.assert()
en su código? Honestamente, lo uso con bastante frecuencia (si esta es una mala práctica, escriba los comentarios, ¿por qué es mala?). En mi código, a menudo puede ver, por ejemplo, una llamada de este tipo:
Swift.assert(Thread.isMainThread)
No hace mucho tiempo, decidí que sería bueno seguir observando los resultados de estas llamadas, no solo como parte del lanzamiento de la aplicación en el simulador / dispositivo, sino también de las acciones de usuarios reales. Por cierto, aquí podemos hablar sobre
Swift.precondition()
,
Swift.fatalError()
, etc., aunque trato de evitarlos. Leí más sobre
Errores irrecuperables en Swift en esta publicación y resultó ser muy informativo.
Más cerca del punto:
en el código encontré unas 300 llamadas de este tipo. Ninguno de ellos trabajó durante las pruebas locales habituales, y me agradó. Pero sugerí que las acciones del usuario aún podrían activar algunas de las llamadas.
Desde el punto de vista del usuario, los bloqueos no deberían ocurrir (de nuevo, mi opinión). En casos extremos, el usuario debe comprender que algún escenario salió mal y el equipo de desarrollo ya está trabajando para solucionarlo. Siempre he manejado excepciones similares, y para el usuario podría afectar de la forma más inofensiva. Por ejemplo, uno de los cientos de celdas de la tabla era simplemente invisible.
Con el usuario más o menos todo está claro. Queda por hacer frente a la entrega de registros al desarrollador. En primer lugar, fue necesario con un mínimo esfuerzo reemplazar las llamadas actuales en el código con llamadas que envían registros en algún lugar fuera de la aplicación. En segundo lugar, era necesario localizar con precisión la escena del incidente, de lo contrario sería prácticamente imposible correlacionar la excepción con el código real. En tercer lugar, debe tenerse en cuenta que las llamadas modificadas pueden funcionar durante las pruebas de la Unidad, donde
Thread.isMainThread
ya debería ignorarse. Utilizo el marco
RxTest para ciertos tipos de pruebas (aquí también estoy listo para escuchar consejos y críticas). El punto principal seguía siendo que localmente todas las excepciones deberían activarse como antes, es decir.
Loggin.assert()
debería dispararse al mismo tiempo que
Swift.assert()
dispararía
Fabric (
Crashlytics ) proporciona una excelente manera de enviar eventos. Se ve así:
Crashlytics.sharedInstance().recordCustomExceptionName("", reason: ""...
Queda por empaquetar
Crashlytics en algún marco que se puede cargar en su
totalidad en la aplicación y en forma truncada (sin dependencia de
Crashlytics ) en los objetivos de prueba.
Decidí hacer el "
empaque " a través de
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
El código para el "objetivo de combate" es el siguiente:
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) } }
Para el "objetivo de prueba" es decir sin dependencia de
Crashlytics :
import Foundation public enum Logging { public static func send(_ reason: String? = nil, __file: String = #file, __line: Int = #line) {
Resultados:
Las excepciones realmente comenzaron a funcionar. La mayoría de ellos informaron un formato incorrecto para los datos recibidos:
Decodable
veces recibieron datos con el tipo
Decodable
. A veces, los registros de
Thread.isMainThread
funcionaban, lo que se solucionó muy rápidamente en las próximas versiones. Los errores más interesantes fueron detectados milagrosamente por
NSException .
Gracias por su atencion
PD Si de repente envía dichos registros a
Crashlytics con demasiada frecuencia, el servicio puede reconocer sus acciones como spam. Y verá el siguiente mensaje:
Debido al uso incorrecto, los informes no fatales se han deshabilitado para varias compilaciones. Aprenda a volver a habilitar los informes en nuestra documentación.
Por lo tanto, vale la pena considerar de antemano la frecuencia de envío de registros. De lo contrario, todos los registros de compilación pueden estar en peligro de ser ignorados por
Crashlytics.