Swift: Natively Detect if App has Crashed

2020-02-09 04:47发布

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 NSUserDefaults so when the app opens next, I can display an alert saying the app has crashed.

Thanks.

3条回答
干净又极端
2楼-- · 2020-02-09 05:38

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

    enter image description here

查看更多
时光不老,我们不散
3楼-- · 2020-02-09 05:41

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)
}
查看更多
对你真心纯属浪费
4楼-- · 2020-02-09 05:42

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)

查看更多
登录 后发表回答