Swift: Natively Detect if App has Crashed

后端 未结 4 480
猫巷女王i
猫巷女王i 2020-12-08 23:31

I have searched around, but have yet to find an answer that doesn\'t direct me towards a 3rd party service. I do not need anything intricate, just to save a value in N

相关标签:
4条回答
  • 2020-12-09 00:07

    Thanks to a little help from @RyanCollins, I was able to solve the problem myself. The function applicationWillTerminate in the App Delegate only runs when the app closes properly. The code to natively detecting an app crash looks like this.

    Globally Defined Variables

    let crashedNotificationKey = "com.stackoverflow.crashNotificationKey"
    var crashedLastTime = true
    

    App Delegate

    func applicationWillTerminate(application: UIApplication) {
        crashedLastTime = false
        prefs.setBool(crashedLastTime, forKey: "crash")
    }
    
    func application(application: UIApplication, didFinishLaunchingWithOptions launchOptions: [NSObject: AnyObject]?) -> Bool {
        crashedLastTime = prefs.boolForKey("crash")
        if crashedLastTime == true {
    
            crashedLastTime = false
            prefs.setBool(crashedLastTime, forKey: "crash")
            NSNotificationCenter.defaultCenter().postNotificationName(crashedNotificationKey, object: self)
    
        } else {
    
            crashedLastTime = true
            prefs.setBool(crashedLastTime, forKey: "crash")
    
        }
    
        return true
    }
    

    Root View Controller

    override func awakeFromNib() {
        NSNotificationCenter.defaultCenter().addObserver(self, selector: "crashedAlert", name: crashedNotificationKey, object: nil)
    }
    
    func crashedAlert() {
        let alert = UIAlertController(title: "The app has crashed!", message: "Sorry about that! I am just a 17 year old highschooler making my first game!", preferredStyle: UIAlertControllerStyle.Alert)
        alert.addAction(UIAlertAction(title: "It's cool bro.", style: UIAlertActionStyle.Default, handler: nil))
        self.presentViewController(alert, animated: true, completion: nil)
    }
    
    0 讨论(0)
  • 2020-12-09 00:08

    FirebaseCrashlytics

    If you don't use Fabric because it is deprecated, You need to use following code in your ViewController that was mentioned in Updagte to the Firebase Crashlytics SDK.

    I use it like this:

    // 1. import Crashlytics
    import FirebaseCrashlytics
    
    class YOUR_ROOT_VIEW_CONTROLLER {
    
       override viewDidLoad(){
           //...
    
           // 2. register to get the notifications
           self.configCrashlytics()
    
           // ...
       }
    
        func configCrashlytics() {
            /* You must set setCrashlyticsCollectionEnabled to false in order to use
            checkForUnsentReportsWithCompletion. */
    
            Crashlytics.crashlytics().setCrashlyticsCollectionEnabled(false)
    
            Crashlytics.crashlytics().checkForUnsentReports { hasUnsentReport in
              let hasUserConsent = false
              // ...get user consent.
    
              if hasUserConsent && hasUnsentReport {
                Crashlytics.crashlytics().sendUnsentReports()
              } else {
                Crashlytics.crashlytics().deleteUnsentReports()
              }
            }
    
            // Detect when a crash happens during your app's last run.
            if Crashlytics.crashlytics().didCrashDuringPreviousExecution() {
              // ...notify the user.
                DispatchQueue.main.async {
                    let alert = Utils.shared.errorAlert(title: "Sorry!", message: "This App was crashed during last run")
                    self.present(alert, animated: true, completion: nil)
                }
            }
            
        }
        // 4. Add a button that run fatallError()
        @IBAction func crashApp(_ sender: UIButton) {
            fatalError()
        }
    }
    
    
    0 讨论(0)
  • 2020-12-09 00:16

    Details

    • swift 4.2
    • Xcode 10.1 (10B61)

    Solution 1

    https://github.com/zixun/CrashEye/blob/master/CrashEye/Classes/CrashEye.swift

    Full sample of solution 1

    !!! DO NOT FORGET TO COPY (or INSTALL POD) SOLUTION CODE !!!

    AppDelegate.swift

    import UIKit
    
    @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {
    
        var window: UIWindow?
    
        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
            CrashEye.add(delegate: self)
            return true
        }
    }
    
    extension AppDelegate: CrashEyeDelegate {
        func crashEyeDidCatchCrash(with model: CrashModel) {
            UserDefaults.standard.set(model.reason + "(\(Date()))", forKey: "crash")
        }
    }
    

    ViewController.swift

    import UIKit
    
    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            let button = UIButton(frame: CGRect(x: 40, y: 40, width: 80, height: 44))
            button.setTitle("BOOOM", for: .normal)
            button.setTitleColor(.blue, for: .normal)
            button.addTarget(self, action: #selector(boomButtonTouchedUpInside), for: .touchUpInside)
            view.addSubview(button)
        }
    
        override func viewDidAppear(_ animated: Bool) {
            if let date = UserDefaults.standard.string(forKey: "crash") {
                let alert = UIAlertController(title: "", message: date, preferredStyle: .alert)
                alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
                present(alert, animated: true)
            }
        }
    
        @objc func boomButtonTouchedUpInside(_ sender: Any) {
            let arr = [1, 2, 3]
            let elem = arr[4]
        }
    }
    

    Solution 2. Crashlytics

    Install Crashlytics

    Full sample of solution 2

    AppDelegate.swift

    import UIKit
    import Fabric
    import Crashlytics
    
    @UIApplicationMain
    class AppDelegate: UIResponder, UIApplicationDelegate {
    
        var window: UIWindow?
    
        func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
            Crashlytics.sharedInstance().delegate = self
            Fabric.with([Crashlytics.self])
            return true
        }
    }
    
    extension AppDelegate: CrashlyticsDelegate {
        func crashlyticsDidDetectReport(forLastExecution report: CLSReport) {
            let alert = UIAlertController(title: "\(report.dateCreated)", message: report.identifier, preferredStyle: .alert)
            alert.addAction(UIAlertAction(title: "Cancel", style: .cancel, handler: nil))
            DispatchQueue.global(qos: .background).async {
                sleep(3)
                DispatchQueue.main.async {
                    self.window?.rootViewController?.present(alert, animated: true)
                }
            }
        }
    }
    

    ViewController.swift

    import UIKit
    
    class ViewController: UIViewController {
    
        override func viewDidLoad() {
            super.viewDidLoad()
            let button = UIButton(frame: CGRect(x: 40, y: 40, width: 80, height: 44))
            button.setTitle("BOOOM", for: .normal)
            button.setTitleColor(.blue, for: .normal)
            button.addTarget(self, action: #selector(boomButtonTouchedUpInside), for: .touchUpInside)
            view.addSubview(button)
        }
    
        @objc func boomButtonTouchedUpInside(_ sender: Any) {
            let arr = [1, 2, 3]
            let elem = arr[4]
        }
    }
    

    Results

    1. Launch app
    2. Push "Booom" button (app will crash)
    3. Lunch app again

    0 讨论(0)
  • 2020-12-09 00:20

    The problem is, if the app has crashed, then it can't run code to write to NSUserDefaults.

    The best solution I know of is to use PLCrashReporter (https://plcrashreporter.org)

    0 讨论(0)
提交回复
热议问题