Показывать SKStoreReviewController только тогда, когда в последнее время не было сбоев? - PullRequest
0 голосов
/ 06 сентября 2018

Я бы хотел позвонить SKStoreReviewController.requestReview() только в том случае, если в этой конкретной установке пользователя / приложения не было сбоя приложения (в последнее время).

Так что вопрос в том, откуда мне знать вприложение что само это приложение рухнуло хотя бы раз или за последнее время?

Я использую Crashlytics.Так что приложение может спросить Crashlytics, если я знал, как.Но ответ без Crashlytics также будет очень кстати.

1 Ответ

0 голосов
/ 12 сентября 2018

Я собрал класс для обработки этого на основе ссылки, которой я поделился в комментарии. Это разделяет логику, которая отслеживает сбои, в класс многоразового использования, который можно поместить в любой проект.

Несколько заметок:

  • Это не поймает сбои, которые происходят в фоновом режиме.
  • Это просто устанавливает bool в UserDefaults на «true», когда приложение запускается, и устанавливается на «false», когда приложение закрывается нормально. Мне это кажется хакерским, но оно должно выполнять то, что ты хочешь сделать.
  • Я поставил проверку на сбой приложения в коде ViewController. Вы можете проверить наличие сбоев в 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
    }

}

Настройте его в делегате приложения:

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) для каждого обработчика и сделав переменные глобальными. Либо я недостаточно хорошо понимаю DispatchSourceSignal, либо этот подход просто не работает.

...