swiftswiftui

Run async function when before app has been terminated


I want to perform some action (scheduling notification, updating widgets data) before user closing the app (like swiping up from multitask view).

SomeView().onReceive(
    NotificationCenter.default.publisher(
        for: UIApplication.willTerminateNotification
    ),
    perform: { _ in
        Task {
            await unMount()
        }
    }
)

The problem is that system doesn't wait for my function to be executed. According to Apple documentation

Your implementation of this method has approximately five seconds to perform any tasks and return.

My function perform several fetch request to Core Data, so it is definitely under the 5 seconds bound. When I use sync code everything works fine. How can I force the system to wait for my function to finish?


Solution

  • This is not possible. It's not even promised this notification will be posted at all.

    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.

    You cannot build anything based on the assumption this will be called. Perform whatever processing you need when the app moves to the background (didEnterBackgroundNotification). Do not assume you can do any meaningful work "just before" termination.

    That said, whenever you being work that you would like extra time to complete before termination, add a background task around it. This is typically done when the work needs to be done; not immediately before suspension (because the app may not get an opportunity). For details, see beginBackgroundTask(expirationHandler:). The general pattern is that you call beginBackgroundTask before you begin your work, and endBackgroundTask when you have completed it. It is normal and expected for that work to be asynchronous. But it is not promised that the system will give you time, and you must deal with that.

    Making fetch requests at this point is very unusual. Typically the goal would be to save information, not fetch it. It's possible that what you really need to do is fetch this information during the next launch rather than attempting it during termination.