Swift:本机检测应用程序是不是崩溃

Posted

技术标签:

【中文标题】Swift:本机检测应用程序是不是崩溃【英文标题】:Swift: Natively Detect if App has CrashedSwift:本机检测应用程序是否崩溃 【发布时间】:2016-09-10 06:19:01 【问题描述】:

我已经四处搜索,但尚未找到不将我引导至 3rd 方服务的答案。我不需要任何复杂的东西,只需在NSUserDefaults 中保存一个值,这样当应用程序下次打开时,我可以显示一个警告,说明应用程序已崩溃。

谢谢。

【问题讨论】:

看看:***.com/questions/10885313/… 【参考方案1】:

感谢@RyanCollins 的一点帮助,我自己解决了这个问题。 App Delegate 中的applicationWillTerminate 函数仅在应用正常关闭时运行。原生检测应用崩溃的代码如下所示。

全局定义的变量

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

应用代理

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

根视图控制器

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)

【讨论】:

没有测试过,但我认为这里有一个错误。在应用程序 didFinishLaunchingWithOptions 函数中检查 crashedLastTime 的值后,始终将存储的布尔值设置为 true。例如:prefs.setBool(true, forKey: "crash") @Awesomeness 是的!当我们从后台关闭应用时applicationWillTerminate 被调用。 我观察到applicationWilTerminate notios 11 上强制退出应用程序时被调用。 applicationWillResignActive 在用户能够强制退出之前被调用,因此应用在用户能够强制退出之前处于非活动状态。我跟踪应用程序状态,并且仅在最后一个已知状态处于活动状态时将崩溃计为崩溃。当状态为非活动或后台时,它不会过多打扰用户,即使它真的崩溃了,而不仅仅是强制退出。 在这段代码中我发现了错误:如果应用程序崩溃并且我们在方法 didFinishLaunchingWithOptions 中捕获了这个,那么在此会话中应用程序再次崩溃,那么将错过对崩溃的处理,因为 key 的属性“崩溃”设置为假。【参考方案2】:

详情

swift 4.2 Xcode 10.1 (10B61)

解决方案 1

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

解决方案 1 的完整示例

!!!不要忘记复制(或安装 POD)解决方案代码!!!

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]
    

解决方案 2. Crashlytics

Install Crashlytics

解决方案 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]
    

结果

    启动应用程序 按下“Booom”按钮(应用程序将崩溃)

    再次午餐应用

【讨论】:

【参考方案3】:

问题是,如果应用程序崩溃了,那么它就无法运行代码来写入 NSUserDefaults。

我知道的最佳解决方案是使用 PLCrashReporter (https://plcrashreporter.org)

【讨论】:

如果应用程序正常关闭,是否有功能或任何东西可以运行?例如,applicationWillResignActive,但即使应用程序崩溃,它也会运行。 是的,在您的应用委托中,有像 applicationWillTerminateapplicationDidEnterBackground 这样的函数可以做到这一点。 我会调查并回复您。【参考方案4】:

FirebaseCrashlytics

如果您不使用Fabric,因为它已被弃用,您需要在ViewController 中使用Updagte to the Firebase Crashlytics SDK 中提到的以下代码。

我是这样使用的:

// 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()
    


【讨论】:

我在找这条线Crashlytics.crashlytics().didCrashDuringPreviousExecution()

以上是关于Swift:本机检测应用程序是不是崩溃的主要内容,如果未能解决你的问题,请参考以下文章

在将应用程序从 swift 迁移到本机反应后,是不是可以在应用程序中检索用户令牌?

Swift 中的 Xcode 调试/崩溃报告是不是损坏?

检查 swift 中是不是有可用的互联网连接 [重复]

在 TextReconiger 上实例化期间,我如何处理由于 Google Ocr lib 而导致的应用程序中的本机崩溃?

如何检测是不是在本机反应中显示警报?

Swift 4 相机视图,为啥这会在 iPad 而不是 iPhone 上崩溃?