How to Make an API Call When the User Terminates the App

Can I make an api call when the user terminates the app?

This is a two fold question

Phase 1: Ensuring API Call starts every time user terminates the app/ before it turns in active

You can always make use of expiration handler background mode of iOS application In your appdelegate

declare
var bgTask: UIBackgroundTaskIdentifier = UIBackgroundTaskIdentifier(rawValue: 0);

and in your appdelegate

 func applicationDidEnterBackground(_ application: UIApplication) {

// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
// If your application supports background execution, this method is called instead of applicationWillTerminate: when the user quits.

bgTask = application.beginBackgroundTask(withName:"MyBackgroundTask", expirationHandler: {() -> Void in
// Do something to stop our background task or the app will be killed
application.endBackgroundTask(self.bgTask)
self.bgTask = UIBackgroundTaskIdentifier.invalid
})

DispatchQueue.global(qos: .background).async {
//make your API call here
}
// Perform your background task here
print("The task has started")
}

Background expiration handler will ensure you will get enough time to start your API call every time you put your application turns inactive or gets terminated

Phase 2: Ensuring API call started finishes successfully

Though expiration handler might ensure that you get enough time to start your API call it can't ensure the successful completion of API call. What if API call takes longer and while the request is in flight and time runs out??

The only way you to ensure that API call gets successful once started is to make sure to use proper configuration for URLSession

As per docs

Background sessions let you perform uploads and downloads of content
in the background while your app isn't running.

link: https://developer.apple.com/documentation/foundation/nsurlsession?language=objc

So make use of Background session and use upload task. Rather than having a plain get/post API which you will hit with some parameter, ask your backend developer to accept a file and put all your param data in that file (if you have any) and start an upload task with background session.

Once the upload task starts with background session iOS will take care of its completion (unless u end up in a authentication challenge obviously) even after your app is killed.

This I believe is the closest you can get to ensure starting a API call and ensuring it finishes once app gets inactive/terminated. I kind a had a discussion with a apple developer regarding the same, and they agreed that this can be a probable solution :)

hope it helps

How to Call an API in applicationWillTerminate( ) and get some time for response from the server

You can't call API if your app is in applicationWillTerminate state or application in killed state.

Application only allows to save instant changes in applicationWillTerminate state

func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
// Saves changes in the application's managed object context before the application terminates.
CoreDataManager.sharedManager.saveContext()
}

How to call API when app is terminated in push notifcation iOS Swift?

As per Apple guidelines, you can get push notification for the background as well as on foreground state but when it comes to
Terminate state apple don't allow you to automatically open the app or
do any kind of operation unless you launch the app through notification.

Though you can handle notification during the Terminated state using Launch Options at the time of app launch.

Coding Example:

In your AppDelegate.swift import firebase library

import Firebase
import FirebaseInstanceID
import FirebaseMessaging
import UserNotifications

Whenever app launch register for push notification service, add the following lines of code into your didFinishLaunchingWithOptions

func application(_ application: UIApplication,
didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
registerForPushNotifications(application: application)
handleNotificationWhenAppIsKilled(launchOptions)
return true
}

func handleNotificationWhenAppIsKilled(_ launchOptions: [UIApplicationLaunchOptionsKey: Any]?) {
// Check if launched from the remote notification and application is close
if let remoteNotification = launchOptions?[.remoteNotification] as? [AnyHashable : Any] {
// Handle your app navigation accordingly and update the webservice as per information on the app.
}
}

Add extension methods of appDelegate to register for remote notification and to get device token from APNS

//MARK: - Notifications related...
extension AppDelegate {
func registerForPushNotifications(application: UIApplication) {
if #available(iOS 10.0, *) {
// For iOS 10 display notification (sent via APNS)
UNUserNotificationCenter.current().delegate = self
let authOptions: UNAuthorizationOptions = [.alert, .badge, .sound]
UNUserNotificationCenter.current().requestAuthorization(
options: authOptions,
completionHandler: {_, _ in })
// For iOS 10 data message (sent via FCM
Messaging.messaging().delegate = self
} else {
let settings: UIUserNotificationSettings =
UIUserNotificationSettings(types: [.alert, .badge, .sound], categories: nil)
application.registerUserNotificationSettings(settings)
}

application.registerForRemoteNotifications()
}

func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
let token = deviceToken.map { String(format: "%02.2hhx", $0) }.joined()
let savedAPNSToken = UserDefaults.standard.object(forKey: "savedAPNSToken") as? String
if savedAPNSToken != token {
UserDefaults.standard.set(token, forKey: "savedAPNSToken")
UserDefaults.standard.synchronize()
Messaging.messaging().apnsToken = deviceToken
}
}

func application(_ application: UIApplication, didFailToRegisterForRemoteNotificationsWithError error: Error) {
print(error.localizedDescription)
}

func application(_ application: UIApplication, didReceiveRemoteNotification userInfo: [AnyHashable: Any],
fetchCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
completionHandler(UIBackgroundFetchResult.newData)
}

}

Use the following methods of notificationCenter to handle notification in the foreground and background states :

// MARK: - UNUserNotificationCenterDelegate
@available(iOS 10, *)
extension AppDelegate : UNUserNotificationCenterDelegate {

// Receive displayed notifications for iOS 10 devices.
func userNotificationCenter(_ center: UNUserNotificationCenter,
willPresent notification: UNNotification,
withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
let userInfo = notification.request.content.userInfo
completionHandler([.alert])
}

/// Handle tap on the notification banner
///
/// - Parameters:
/// - center: Notification Center
/// - response: Notification response
func userNotificationCenter(_ center: UNUserNotificationCenter,
didReceive response: UNNotificationResponse,
withCompletionHandler completionHandler: @escaping () -> Void) {

let userInfo = response.notification.request.content.userInfo
completionHandler()
}

Firebase token renewel:

extension AppDelegate : MessagingDelegate {
func messaging(_ messaging: Messaging, didReceiveRegistrationToken fcmToken: String) {
// Note: This callback is fired at each app startup and whenever a new token is generated.
let savedFCMToken = UserDefaults.standard.object(forKey: "savedFCMToken") as? String
if savedFCMToken != fcmToken {
UserDefaults.standard.set(fcmToken, forKey: "savedFCMToken")
UserDefaults.standard.synchronize()
// Update FCMToken to server by doing API call...
}
}
}

Swift send request to server when app killed

If you want to contact the server when the app is going into the background, that is certainly possible. Implement UIApplicationDelegate.applicationDidEnterBackground. See Strategies for Handling App State Transitions for full details. This is a very common and supported operation and should be what you need.

However, it is not possible to contact the server when the app is killed in the background. applicationWillTerminate is generally never called in any case since iOS 4. Prior to iOS 4, there was no "background" mode. When the user pressed the home button, the app was immediately terminated after calling this method, and that's why it still exists. But it's all-but-useless in modern apps. (You can still get this ancient behavior by setting UIApplicationExitsOnSuspend in your Info.plist, but this does nothing to help your problem.)

When your application is killed in the background it is just killed. It is not woken up, and no methods are called on it. It is unceremoniously terminated and removed from memory. You cannot respond to this event. The same happens when the user force-quits your app from the app-switcher.

I'm a little curious about why you would want a push notification when the app goes into the background, and particularly when the app terminates. When I've seen people try to do this in the past, they often were trying to use push notifications to keep the app launched at all times. This is not possible. Even if you found a work-around to make it technically possible, it is explicitly forbidden by Apple.



Related Topics



Leave a reply



Submit