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:)
)
Click on project name —> Select Target —> Go To Signing & Capabilities —> Click on add —> Background Modes
Check Background Fetch and Background Processing
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) } }
In
AppDelegate
,didFinishLaunchingWithOptions
, call :BGTaskHelper.registerAutoRefreshToken()
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
insteadBGProcessingTaskRequest
? - And for request call should I switch to
URLSessionConfiguration.default
?