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

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了只有在最近没有崩溃时才显示SKStoreReviewController?相关的知识,希望对你有一定的参考价值。

我只想在这个特定的用户/应用程序安装没有应用程序崩溃(最近)时调用SKStoreReviewController.requestReview()

所以问题是,我如何在应用程序中知道这个应用程序本身至少崩溃过一次或最近?

我正在使用Crashlytics。因此,如果我知道如何,应用程序可以询问Crashlytics。但是没有Crashlytics的答案也会非常受欢迎。

答案

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

几点说明:

  • 这不会发现在后台发生的崩溃。
  • 当应用程序启动时,这会简单地将UserDefaults中的bool设置为“true”,当应用程序正常关闭时,将其设置为“false”。对我来说这感觉有些不好,但它应该完成你想做的事情。
  • 我在ViewController代码中检查了app崩溃。如果应用程序在视图控制器加载之前崩溃,您可能需要检查applicationDidFinishLaunching中的崩溃情况。检查appDidCrash()会重置UserDefaults中的跟踪器。

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

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
    }

}

在app delegate中设置:

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

我尝试使用DispatchSourceSignal,因为它似乎是一种更好/更可靠的方法来跟踪崩溃,但是永远无法使用Swift 4.我永远无法启动任何事件处理程序。

这里参考一个小样本实现。

这不适合我,我把它包括在这里是为了完整,以防其他人想要进一步试验它。

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

我尝试了第二个解决方案,为处理程序使用不同的队列,删除每个处理程序的忽略调用signal(SIGILL, SIG_IGN)并使vars全局化。要么我不太了解DispatchSourceSignal或这种方法不起作用。

以上是关于只有在最近没有崩溃时才显示SKStoreReviewController?的主要内容,如果未能解决你的问题,请参考以下文章

怎么可能只有在第一次启动应用程序时才显示插页式广告

锁定 Windows 8.1 时 Visual Studio 2013 和 2015 崩溃

Xcode iOS 仪器:

不显示视频,只有在 react-native-video 中播放真正的 android 设备时才会发出声音

div 上的滚动条只有在它有超过 30 个项目时才会出现

Sencha touch/cordova 应用程序在 ios8 启动时随机崩溃