How to use BGTaskScheduler to make API call in background in swift?

1.1k Views Asked by At

In my app, One service we have to call to refresh the token. So for that thinking to use background scheduler and schedule task for calling API.

In that, scheduled task I have to make silent call in background which provides new token. So when user opens the app, one will not face any kind of inconvenience.

For that, I made combination of background scheduler(BGTaskScheduler), using BGProcessingTask, to make API call (with URLSessionConfiguration.background(withIdentifier:))

  1. Click on project name —> Select Target —> Go To Signing & Capabilities —> Click on add —> Background Modes

  2. Check Background Fetch and Background Processing

  3. Add Identifiers in info.plist enter image description here

  4. Created custom class, named BGTaskHelper :

    import UIKit
    import Foundation
    import BackgroundTasks
    
    
    let TASK_ID_REFRESH_TOKEN = "com.sample.refresh"
    let SESSION_ID_REFRESH_CALL = "com.sample.refreshCall"
    
    class BGTaskHelper: NSObject, URLSessionDelegate {    
        static let shared = BGTaskHelper()
    
    private lazy var urlSession: URLSession = {
        let config = URLSessionConfiguration.background(withIdentifier: SESSION_ID_REFRESH_CALL)
        config.isDiscretionary = true
        config.sessionSendsLaunchEvents = true
        return URLSession(configuration: config, delegate: self, delegateQueue: nil)
    }()
    
    func requestWith(strURL: String, method: String, params: [String: Any]?, completion: @escaping((Data?, URLResponse?, Error?)) -> Void) {
    
        guard let url = URL(string: strURL) else { return }
        print("REQUEST URL : =============== ", strURL)
    
        var request = URLRequest(url: url)
        request.httpMethod = method
        request.addValue("application/json", forHTTPHeaderField: "content-type")
    
        if let postParams = params {
            print("REQUEST PARAMS : =============== ", postParams)
            guard let httpbody = try? JSONSerialization.data(withJSONObject: postParams, options: []) else { return }
            request.httpBody = httpbody
        }
    
        let task = urlSession.dataTask(with: request) { (data, response, error) in
    
            guard let response = response, let data = data else {
                completion((nil, nil, error))
                return
            }
    
            // ------- Added to just print response --------------------------------
            do {
                let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String:Any]
                print("RESPONSE JSON : =============== ", json ?? "")
            } catch let error{
                print("RESPONSE PARSING ERROR : =============== ", error)
            }
            // -----------------------------------------------------------------------
    
            completion((data, response, error))
        }
        task.resume()
    }
    
    static func registerAutoRefreshToken() {
    
        if #available(iOS 13.0, *) {
            BGTaskScheduler.shared.register(forTaskWithIdentifier: TASK_ID_REFRESH_TOKEN, using: nil) { task in
                task.setTaskCompleted(success: true)
                BGTaskHelper.shared.handleTokenRefresh(task: task)  //as! BGAppRefreshTask
            }
        } else {
            // Fallback on earlier versions
        }
    }
    
    static func scheduleTokenRefresh() {
    
        guard UserDefaults.standard.bool(forKey: "IS_LOGIN") == true else { return }
    
        if #available(iOS 13.0, *) {
    
            BGTaskScheduler.shared.cancelAllTaskRequests()
    
            let request = BGProcessingTaskRequest(identifier: TASK_ID_REFRESH_TOKEN)
            // Fetch no earlier than 15 minutes from now
            request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60)
            request.requiresNetworkConnectivity = true
    
            do {
                try BGTaskScheduler.shared.submit(request)
            } catch {
                print("Could not schedule token refresh : ", error)
            }
        }
    }
    
    @available(iOS 13.0, *)
    func handleTokenRefresh(task: BGTask) { //BGAppRefreshTask
    
        // 1. Schedule a new refresh task
        BGTaskHelper.scheduleTokenRefresh()
    
        let queue = OperationQueue()
        queue.maxConcurrentOperationCount = 1
    
        // 2. Create an operation that performs the main part of the background task
    
        let operation = BlockOperation()
        operation.addExecutionBlock {
            if UserDefaults.standard.bool(forKey: "IS_LOGIN") == true {
                let dict: [String: Any] = ["userID" : "xyzUser",
                                           "token" : "last_saved_token"]
                BGTaskHelper.shared.requestWith(strURL: "https://example.com/xyzUser/tokenrefresh", method: "POST", params: dict) { (data, response, error) in
                    task.setTaskCompleted(success: true)
                    // saving new token
                }
            }
        }
    
    
        // 3. Provide an expiration handler for the background task that cancels the operation
        task.expirationHandler = {
            operation.cancel()
        }
    
        // 4. Inform the system that the background task is complete when the operation completes
        operation.completionBlock = {
            task.setTaskCompleted(success: !operation.isCancelled)
        }
    
        // 5. Start the operation
        queue.addOperation(operation)
    }
    }
    
  5. In AppDelegate, didFinishLaunchingWithOptions, call : BGTaskHelper.registerAutoRefreshToken()

  6. In SceneDelegate, sceneDidEnterBackground, call : BGTaskHelper.scheduleTokenRefresh()

That’s what I have done. Still I have not tested it fully. But I am confused that this will work perfectly or not ?

  • Is this combination is correct ? : BGTaskScheduler, BGProcessingTask, URLSessionConfiguration.background(withIdentifier:)
  • OR should I use BGAppRefreshTaskRequest instead BGProcessingTaskRequest?
  • And for request call should I switch to URLSessionConfiguration.default ?
0

There are 0 best solutions below