How to run an async/await task when application will be terminated?

392 Views Asked by At

I want to update or end a Live Activity when the application is terminated, but those functions are async, and anything inside Task{ } is not get called in the applicationWillTerminate(_) function.

func applicationWillTerminate(_ application: UIApplication) {
    Task {
        if #available(iOS 16.2, *) {
            await LiveActivityManager.cancelAllRunningActivies()
        }
    }
}

I have tried passing a group user default to the live activity, and live activity uses a timer to check the new value for every seconds, but timer on live activity is not get called.

Is there any way to update the live activity when the application is about to be terminated?

2

There are 2 best solutions below

1
zgjie On

This works for me: https://developer.apple.com/forums/thread/732418

class func stopSessionTimeoutAsync()
{
    print("Ending Live Activities")
    let semaphore = DispatchSemaphore(value: 0)
    Task
    {
        for activity in Activity<TimeoutActivityAttributes>.activities
        {
            print("Ending Live Activity: \(activity.id)")
            await activity.end(nil, dismissalPolicy: .immediate)
        }
        semaphore.signal()
    }
    semaphore.wait()
}

Just remember to use Task.detached if the entire class is marked with @MainActor, otherwise anything inside the task will not be execute.

0
maxime On

I struggled a lot to understand how to use the previous answer, the method name was pretty confusing for a beginner.

This works on iOS 17.3, you have to put it on your App class

import ActivityKit
import SwiftUI

@main
struct SomeApp: App {
    @UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
    
    var body: some Scene {
        WindowGroup {
            ContentView()
        }
    }
}

class AppDelegate: NSObject, UIApplicationDelegate {
    func applicationWillTerminate(_ application: UIApplication) {
        print("App will terminate")
        let semaphore = DispatchSemaphore(value: 0)
        Task.detached
        {
            for activity in Activity<TimeoutActivityAttributes>.activities
            {
                print("Ending Live Activity: \(activity.id)")
                await activity.end(nil, dismissalPolicy: .immediate)
            }
            semaphore.signal()
        }
        semaphore.wait()
    }
}