Swift.assert - kehidupan setelah rilis

Seberapa sering Anda menggunakan Swift.assert() dalam kode Anda? Jujur saya cukup sering menggunakannya (Jika ini adalah praktik yang buruk, maka silakan tulis di komentar - mengapa itu buruk?). Dalam kode saya, Anda sering dapat melihat, misalnya, panggilan seperti itu:

 Swift.assert(Thread.isMainThread) 

Belum lama ini, saya memutuskan bahwa akan menyenangkan untuk terus mengamati hasil dari panggilan ini, tidak hanya sebagai bagian dari peluncuran aplikasi di simulator / perangkat, tetapi juga dari tindakan pengguna nyata. Ngomong-ngomong, di sini kita dapat berbicara tentang Swift.precondition() , Swift.fatalError() , dll., Walaupun saya mencoba menghindarinya. Saya membaca lebih lanjut tentang Kesalahan yang Tidak Dapat Dipulihkan di Swift dalam publikasi ini dan ternyata sangat informatif.

Lebih dekat ke titik: dalam kode saya menemukan sekitar 300 panggilan seperti itu. Tak satu pun dari mereka bekerja selama pengujian lokal yang biasa, dan itu menyenangkan saya. Tetapi saya menyarankan bahwa tindakan pengguna mungkin masih memicu beberapa panggilan.

Dari sudut pandang pengguna, crash seharusnya tidak terjadi (sekali lagi, menurut saya). Dalam kasus yang ekstrim, pengguna harus memahami bahwa beberapa skenario salah dan tim pengembang sudah bekerja untuk memperbaikinya. Pengecualian serupa selalu ditangani oleh saya, dan bagi pengguna itu bisa berdampak pada bentuk yang paling tidak berbahaya. Sebagai contoh, salah satu dari ratusan sel tabel tidak terlihat.

Dengan pengguna lebih atau kurang semuanya jelas. Masih berurusan dengan pengiriman log ke pengembang. Pertama, diperlukan upaya minimal untuk mengganti panggilan saat ini dalam kode dengan panggilan mengirim log di suatu tempat di luar aplikasi. Kedua, itu perlu untuk secara akurat melokalisasi tempat kejadian, jika tidak praktis tidak mungkin untuk menghubungkan pengecualian dengan kode nyata. Ketiga, harus dicatat bahwa panggilan yang dimodifikasi dapat bekerja selama pengujian Unit, di mana Thread.isMainThread seharusnya sudah diabaikan. Saya menggunakan kerangka kerja RxTest untuk jenis pengujian tertentu (di sini saya juga siap mendengarkan saran dan kritik). Poin utama tetap bahwa secara lokal semua pengecualian harus dipicu seperti sebelumnya, yaitu Loggin.assert() harus memecat pada saat yang sama bahwa Swift.assert() akan memecat

Fabric ( Crashlytics ) menyediakan cara hebat untuk mengirim acara. Ini terlihat seperti ini:

 Crashlytics.sharedInstance().recordCustomExceptionName("", reason: ""... 

Masih mengemas Crashlytics ke dalam beberapa kerangka kerja yang dapat dimuat secara keseluruhan ke dalam aplikasi dan dalam bentuk terpotong (tanpa ketergantungan Crashlytics ) dalam target pengujian.

Saya memutuskan untuk melakukan " pengemasan " melalui 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 

Kode untuk "target tempur" adalah sebagai berikut:

 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) } } 

Untuk "target pengujian" yaitu tanpa ketergantungan Crashlytics :

 import Foundation public enum Logging { public static func send(_ reason: String? = nil, __file: String = #file, __line: Int = #line) { // } public static func assert(_ assertion: @escaping @autoclosure () -> Bool, reason: String? = nil, __file: String = #file, __line: Int = #line) { // } public static func assert(_ assertion: @escaping () -> Bool, reason: String? = nil, __file: String = #file, __line: Int = #line) { // } public static func assertionFailure(_ reason: String? = nil, __file: String = #file, __line: Int = #line) { // } } 

Hasil:


Pengecualian benar-benar mulai bekerja. Sebagian besar dari mereka melaporkan format yang salah untuk data yang diterima: Decodable terkadang menerima data dengan tipe yang Decodable . Terkadang log untuk Thread.isMainThread berfungsi, yang sangat cepat diperbaiki di rilis berikutnya. Kesalahan paling menarik secara ajaib ditangkap oleh NSException .

Terima kasih atas perhatian anda

NB Jika Anda terlalu sering mengirim log seperti itu ke Crashlytics , layanan ini dapat mengenali tindakan Anda sebagai spam. Dan Anda akan melihat pesan berikut:
Karena penggunaan yang tidak tepat, pelaporan non-fatal telah dinonaktifkan untuk beberapa bangunan. Pelajari cara mengaktifkan kembali pelaporan dalam dokumentasi kami.
Oleh karena itu, perlu mempertimbangkan terlebih dahulu frekuensi pengiriman log. Jika tidak, semua log bangunan mungkin dalam bahaya diabaikan oleh Crashlytics.

Source: https://habr.com/ru/post/id476792/


All Articles