square/SuperDelegate

App Delegate Life Cycle Protocol

benjohnde opened this issue ยท 6 comments

Hi. We are in need of the following delegate calls for analytics as well as some core data handling:

Transitioning to the foreground:
- applicationDidBecomeActive(_:)

Transitioning to the background:
- applicationDidEnterBackground(_:)

Transitioning to the inactive state:
- applicationWillResignActive(_:) (Called when leaving the foreground state.)
- applicationWillEnterForeground(_:) (Called when transitioning out of the background state.)

Termination:
- applicationWillTerminate(_:) (Called only when the app is running. This method is not called if the app is suspended.)

Hence, I propose some sort of LifeCycleProtocol, maybe LifeCycleAware, in order to retrieve those delegate calls.

protocol LifeCycleAware {
  func transitionedToForeground()
  func transitionedToBackground()

  func transitioningToBackground()
  func transitioningToForeground()

  func terminatingApplication()
}

Cheers!

dfed commented

SuperDelegate specifically doesn't override the lifecycle methods: your subclass is free to implement applicationDidBecomeActive, etc. without any affect on SuperDelegate.

SuperDelegate's goal is to simplify the UIApplicationProtocol methods that are hard to grok, or that interact with the client (your app) weirdly. The lifecycle methods are pretty darned simple, and don't have any weird side effects, so there's really no need for SuperDelegate to override them. Note that SuperDelegate.swift specifically uses NSNotification listeners to track these events to allow for them to be implemented by a subclass easily ๐Ÿ™‚.

As such, I'm closing this issue. We're currently behaving as intended here.

I am going to rethink what I did here.

My intent was to create some sort of convenience method (which is not really the case in the proposed PR, I can see that) for handling our core data stack and some additional analytics.

We currently use both:

  • func applicationDidEnterBackground(),
  • func applicationWillTerminate().

There exist some cases in which both methods are getting triggered. So maybe we could implement some sort of func applicationInBackgroundOrTerminated() thingy.

My first approach was really naive (as written in the commit message) and maybe a bit too fast to hand in a PR.

I just love small classes which only contain my main business logic...and nothing more.

Does it sound feasible to you to implement some kind of:

protocol ApplicationClosed {
    func applicationInBackgroundOrTerminated()
}

implementation?

Args, I cannot reopen this issue :) If you are open to discuss a bit about that (and share some ideas) we may switch to a new issue?

Additionally, I guess that many developers are in need of such a protocol and have already implemented some sort of checking, if the termination method was already invoked or not.

With SuperDelegate, we could remove those boilerplate lines. For instance, without rating the code, I have seen the following a lot:

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
[...]
    var isInBackgroundOrTerminated = false
    
    func applicationDidBecomeActive(_ application: UIApplication) {
        isInBackgroundOrTerminated = false
    }

    func applicationDidEnterBackground(_ application: UIApplication) {
        applicationInBackgroundOrTerminated()
    }

    func applicationWillTerminate(_ application: UIApplication) {
        applicationInBackgroundOrTerminated()
    }
    
    func applicationInBackgroundOrTerminated() {
        guard !isInBackgroundOrTerminated else { return }
        isInBackgroundOrTerminated = true
        // do some cleanup, save core data context, some network call, background activities?
    }
}

The reason for such code is that applicationWillTerminate is invoked after the user exits the application (for instance from multi-tasking).

"For apps that support background execution, this method is generally not called when the user quits the app because the app simply moves to the background in that case. However, this method may be called in situations where the app is running in the background (not suspended) and the system needs to terminate it for some reason." [1]

Most times applicationDidEnterBackground will be called, like a user hits the home-button.

We could realize this implementation using notifications like SuperDelegate is already consuming.

Maybe we can find some sort of feasible naming and usage for this use case. :) At least, I would use it a lot.

Cheers!

[1] https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1623111-applicationwillterminate

dfed commented

Thank you for the additional context! It sounds like what you're looking for is a hook for "persist data before I die", which I'm not sure is a pattern I'd want to encourage (if I'm wrong about the use case please let me know!) โ€” I've found applicationWillResignActive to be the best hook for such things since it's always called before the OS wants to kill us. Though even better is constantly persisting, but doing it on a background queue.

If you help me understand the use case you're solving for here, that'll help us come up with a good name / protocol / method. I'm still not convinced that we need to provide API here, since each app's needs around lifecycle is unique. But, I'm open to a good discussion ๐Ÿ™‚. Reopening.

Ah nice, thanks a lot for pointing that out! I have never thought about that method.

"If your app has unsaved user data, you can save it here to ensure that it is not lost. However, it is recommended that you save user data at appropriate points throughout the execution of your app, usually in response to specific actions. For example, save data when the user dismisses a data entry screen. Do not rely on specific app state transitions to save all of your appโ€™s critical data." [1]

I am not using it as a last option to persist data before app is terminated, but I am using it as proposed by Apple, but somehow the wrong way :)

[1] https://developer.apple.com/documentation/uikit/uiapplicationdelegate/1622950-applicationwillresignactive