1

This is such an odd issue that I have run into. I am setting and reading 2 different boolean user preferences. I update the user preferences depending on what the user is doing. I then read them when the user opens the app again. When the user kills the app, I am seeing applicationWillTerminate is always correctly called and that the 2 setters are called with the correct values. When I open the app again, the 2 values are correct (what they were last set to in applicationWillTerminate). However, about 20% of the time, they are not correct. I will do the exact scenario over and over and I will see that applicationWillTerminate is called and the 2 variables set to the correct values. However, when I open the app again, they are not the updated values about 20% of the time. I have found no pattern as to why sometimes the values are saved permanently and sometimes they aren't.

I have been researching this for awhile and can't find anyone else who has run into this intermittent issue. I tried adding synchronize() even though it's going to be deprecated and it didn't make a difference. I know for a fact my setters aren't getting called any other time.

Any ideas?

//this method is called from didFinishLaunching and only that one time
private func checkForPriorCrash() {
    let appClosedProperly = UserDefaults.standard.bool(forKey: UserInterface.Keys.appClosedProperly)
    let lastStateWasActive = UserDefaults.standard.bool(forKey: UserInterface.Keys.lastStateWasActive)

    if !appClosedProperly && lastStateWasActive {
        print("**A CRASH HAPPENED LAST TIME")
    }
    UserDefaults.standard.removeObject(forKey: UserInterface.Keys.appClosedProperly) //reset
    UserDefaults.standard.removeObject(forKey: UserInterface.Keys.lastStateWasActive) //reset
}

private func setAppClosedProperly(_ value: Bool) {
    print("set did app closed properly")
    print(value)
    UserDefaults.standard.set(value, forKey: UserInterface.Keys.appClosedProperly)
}

private func setLastStateWasActive(_ value: Bool) {
    print("set last state was active")
    print(value)
    UserDefaults.standard.set(value, forKey: UserInterface.Keys.lastStateWasActive)
}

func applicationDidEnterBackground(_ application: UIApplication) {
    print("in applicationDidEnterBackground")
    setLastStateWasActive(false)
}

func applicationDidBecomeActive(_ application: UIApplication) {
    print("in applicationDidBecomeActive")
    setLastStateWasActive(true)
}

func applicationWillTerminate(_ application: UIApplication) {
    print("in applicationWillTerminate")
    setLastStateWasActive(false)
    setAppClosedProperly(true)
}
Kate M
  • 478
  • 4
  • 17
  • `applicationWillTerminate` is rarely called with a real app. – Sulthan Jun 27 '19 at 16:19
  • I realize that my code is slightly flawed in detecting if the app crashed. However, that is not my issue. My issue is applicationWillTerminate *is* getting called in 100% of my testing and those values *are definitely* getting set. I can see that from print logs. – Kate M Jun 27 '19 at 16:20
  • 1
    User defaults are saved asynchronously so I am not sure whether they will be saved correctly in `applicationWillTerminate. – Sulthan Jun 27 '19 at 16:28
  • Yes, Sulthan is on point, async UserDefault read/writes can cause flaky results. It's not related to the question but what is your goal of storing values for a previous crash? Do you display custom user interaction after a sudden crash, or what? – balazs630 Jun 27 '19 at 16:31
  • I think that is the issue I am likely running into. I haven't yet found a way to force them to definitely always save before the application terminates. It looks like the soon to be deprecated synchronize should do exactly that, but I tried it and it didn't work either. – Kate M Jun 27 '19 at 16:32
  • @balazs630 we are using software to track user actions within the app and I want to have a general idea if app crashes are happening. Nothing will be displayed to the user. If a crash is detected, we sent a POST to our REST service to log a possible crash happened with this user last time. I want to see if there is a pattern of crashes with certain actions, users, etc. for debugging purposes. With the code as it is right now, it is saying the app crashed when it actually didn't. – Kate M Jun 27 '19 at 16:36
  • Just asking because there are already really good solutions for this purpose. If you upload dSYM when submitting your app to App Store, you'll receive detailed crash logs with 2-3 day delay, pointing to the exact line that produced the crash (e.x. a force unwrap or something). Or if you'd like to get instant crash reports, use Crashlytics for example. In my point of view it's not worth the effort to implement custom logic on client side and develop a backend to connect to it. Like reinventing the wheel. – balazs630 Jun 27 '19 at 16:39
  • 1
    @balazs630 I looked into Crashlytics already (and some others) and it looked like a solution that costs money. This app is not submitted to the app store, it's an Enterprise app that is distributed in-house. I actually got the idea to do it this way from here: https://stackoverflow.com/questions/37220379 – Kate M Jun 27 '19 at 16:43
  • I see.. with enterprise distribution it's somewhat different/harder. – balazs630 Jun 27 '19 at 16:47
  • Try checking https://stackoverflow.com/questions/10885313/detect-app-crashed-during-load-last-time-it-was-run – Ankit Jayaswal Jun 27 '19 at 18:50
  • @Ankit Jayaswal I am aware of that solution and is how I initially implemented my item. I'm not going to get into the reasons as to why that didn't work because it's deviating from my question but I can tell you it is not a straightforward/easy solution. – Kate M Jun 27 '19 at 19:16

0 Answers0