仅在最近没有崩溃时才显示SKStoreReviewController吗?

时间:2018-09-05 21:31:40

标签: ios crash crashlytics

仅当此特定用户/应用安装没有(最近)发生应用崩溃时,我才想致电SKStoreReviewController.requestReview()

问题是,我如何在应用程序中知道该应用程序本身至少一次或最近崩溃了?

我正在使用Crashlytics。因此,该应用可以询问Crashlytics我是否知道。但是没有Crashlytics的答案也将非常受欢迎。

1 个答案:

答案 0 :(得分:0)

我根据评论中共享的链接整理了一个类来处理此问题。这将跟踪“崩溃”的逻辑分离为可放入任何项目的可重用类。

一些注意事项:

  • 这不会捕获在后台发生的崩溃。
  • 这只是在应用启动时将UserDefaults中的bool设置为“ true”,而在应用正常关闭时将其设置为“ false”。对我来说,这有点hacky,但它应该可以完成您想要的操作。
  • 我将检查应用程序崩溃的信息放入了ViewController代码中。您可能想在 A B C Group Want last FakeC value final 8 9 13 22 3 22.00 1 22 22.00 22.00 7 8 12 0 3 65.80 0 20 65.80 65.80 6 7 11 0 3 104.95 0 18 59.15 104.95 5 6 10 16 2 16.00 1 16 16.00 16.00 4 5 9 0 2 45.85 0 14 45.85 45.85 3 4 8 0 2 71.05 0 12 39.20 71.05 2 3 7 10 2 91.60 0 10 32.55 59.75 1 2 6 0 1 8.00 1 8 8.00 8.00 0 1 5 0 1 19.25 0 6 19.25 19.25 中检查崩溃,以防应用在视图控制器加载之前崩溃。选中applicationDidFinishLaunching可以在UserDefaults中重置跟踪器。

话虽如此,它将捕获用户在使用应用程序时看到的崩溃。

appDidCrash()

在应用程序委托中进行设置:

import Foundation

class CrashTracker {

    // variable to hold the key used to store the crash record in UserDefaults
    static let defaultsKey = "com.YOUR_BUNDLE_ID.crashRecord"

    init() {
        registerListeners()
    }

    // sets up listeners for the app entering the background or terminating
    func registerListeners() {
        NotificationCenter.default.addObserver(self, selector: #selector(enteredBackground), name: .UIApplicationDidEnterBackground
        , object: nil)
        NotificationCenter.default.addObserver(self, selector: #selector(willTerminate), name: .UIApplicationWillTerminate, object: nil)
    }

    @objc func enteredBackground() {
        // app didn't crash, user closed the app so update UserDefaults
        UserDefaults.standard.set(false, forKey: CrashTracker.defaultsKey)
    }

    @objc func willTerminate() {
        // app didn't crash, user closed the app so update UserDefaults
        UserDefaults.standard.set(false, forKey: CrashTracker.defaultsKey)
    }

    static func appDidCrash() -> Bool {
        // get the current value
        let storedValue = UserDefaults.standard.bool(forKey: CrashTracker.defaultsKey)
        // reset to true to track current launch
        UserDefaults.standard.set(true, forKey: CrashTracker.defaultsKey)
        return storedValue
    }

}

然后在您的视图控制器中(或您想要显示警报的任何地方,也许在应用启动后... class AppDelegate: UIResponder, UIApplicationDelegate { var crashTracker = CrashTracker() ...

applicationDidFinishLaunching

我尝试了override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) if CrashTracker.appDidCrash() { print("caught crash ---------") } else { print("No crash... all good") SKStoreReviewController.requestReview() } } ,因为它似乎是跟踪崩溃的更好/更可靠的方法,但无法使其与Swift 4一起使用。我无法触发任何事件处理程序。

作为参考,这里是一个小示例实现。

这对我不起作用 出于完整性考虑,我将其包括在此处,以防其他人想进一步尝试。

DispatchSourceSignal

我尝试了第二种解决方案,为处理程序使用了不同的队列,为每个处理程序删除了忽略调用class TrackCrashViaDispatch { var sigtrap: DispatchSourceSignal? var sigint: DispatchSourceSignal? var sigabrt: DispatchSourceSignal? var sigill: DispatchSourceSignal? var sigseg: DispatchSourceSignal? var sigfpe: DispatchSourceSignal? var sigbus: DispatchSourceSignal? var sigpipe: DispatchSourceSignal? init() { // handle obj-c exceptions NSSetUncaughtExceptionHandler { exception in TrackCrashViaDispatch.registerCrash() } setupHandlers() } func setupHandlers() { print("Setting up handlers...") signal(SIGTRAP, SIG_IGN) // // Make sure the signal does not terminate the application. sigtrap = DispatchSource.makeSignalSource(signal: SIGTRAP, queue: .main) sigtrap?.setEventHandler { print("Got SIGTRAP") TrackCrashViaDispatch.registerCrash() exit(0) } sigtrap?.resume() signal(SIGINT, SIG_IGN) // Make sure the signal does not terminate the application. sigint = DispatchSource.makeSignalSource(signal: SIGINT, queue: .main) sigint?.setEventHandler { print("Got SIGINT") TrackCrashViaDispatch.registerCrash() exit(0) } sigint?.resume() signal(SIGABRT, SIG_IGN) sigabrt = DispatchSource.makeSignalSource(signal: SIGABRT, queue: .main) sigabrt?.setEventHandler { print("Got SIGABRT") TrackCrashViaDispatch.registerCrash() exit(0) } sigabrt?.resume() signal(SIGILL, SIG_IGN) sigill = DispatchSource.makeSignalSource(signal: SIGILL, queue: .main) sigill?.setEventHandler { print("Got SIGILL") TrackCrashViaDispatch.registerCrash() exit(0) } sigill?.resume() signal(SIGSEGV, SIG_IGN) sigseg = DispatchSource.makeSignalSource(signal: SIGSEGV, queue: .main) sigseg?.setEventHandler { print("Got SIGSEGV") TrackCrashViaDispatch.registerCrash() exit(0) } sigseg?.resume() signal(SIGFPE, SIG_IGN) sigfpe = DispatchSource.makeSignalSource(signal: SIGFPE, queue: .main) sigfpe?.setEventHandler { print("Got SIGFPE") TrackCrashViaDispatch.registerCrash() exit(0) } sigfpe?.resume() signal(SIGBUS, SIG_IGN) sigbus = DispatchSource.makeSignalSource(signal: SIGBUS, queue: .main) sigbus?.setEventHandler { print("Got SIGBUS") TrackCrashViaDispatch.registerCrash() exit(0) } sigbus?.resume() signal(SIGPIPE, SIG_IGN) sigpipe = DispatchSource.makeSignalSource(signal: SIGPIPE, queue: .main) sigpipe?.setEventHandler { print("Got SIGPIPE") TrackCrashViaDispatch.registerCrash() exit(0) } sigpipe?.resume() } static func registerCrash() { print("Registering crash") UserDefaults.standard.set(true, forKey: "com.YOUR_BUNDLE_ID.crashRecord") } static func appDidCrash() -> Bool { let defaults = UserDefaults.standard // get the current value let storedValue = defaults.value(forKey: "com.YOUR_BUNDLE_ID.crashRecord") // set to nil to track next time defaults.set(nil, forKey: "com.YOUR_BUNDLE_ID.crashRecord") return storedValue != nil } } ,并使vars为全局变量。我可能对signal(SIGILL, SIG_IGN)不够了解,或者这种方法行不通。