-
[iOS] BackgroundTasks Framework ๊ฐ๋จ ์ ๋ฆฌ๊ณต๋ถ/iOS 2020. 2. 24. 07:55
๋ชฉ์ฐจ
- BackgroundTasks Framework?
- ๋ฌด์์ ์ง์ํ๋๊ฐ
- ์ฃผ์ ์ฌํญ
- ๊ฐ๋จํ๊ฒ ์จ๋ณด์!
- ํ ์คํธ ํด๋ณด๊ธฐ
- ์ฐธ๊ณ ๊ธ๊ณผ ๋ ๋ณด๋ฉด ์ข์ ๊ฒ๋ค
1. BackgroundTasks Framework?
https://developer.apple.com/documentation/backgroundtasks
์ฑ์ด ๋ฐฑ๊ทธ๋ผ์ด๋์์ ์์ ์ ์ํํ ์ ์๊ฒ ์์คํ ์๊ฒ ์์ฒญํ๋ ๊ฑธ ๋์์ฃผ๋ ํ๋ ์์ํฌ์์.
๊ฐ๋จํ ์๋ฒ ํธ์ถ > ๋ฆฌํ๋ ์ฌ ์์ ๋ถํฐ, ์๋์ง๋ฅผ ๋ง์ด ์ฌ์ฉํ๊ณ ๋คํธ์ํฌ ์ฐ๊ฒฐ์ ์๊ตฌํ๋ ์์ ๋ ์์ฒญํ ์ ์๋ค๊ณ ํด์.
iOS 13๋ถํฐ ์ง์. 13 ์ดํ์ ๋ฒ์ ์ ์ด ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํด์ผํ๋ค๊ณ ํด์.
setMinimumFetchInterval๋ deprecated ๋์ด๋ฒ๋ ธ๋ค์ ๐ฅบ
2. ๋ฌด์์ ์ง์ํ๋๊ฐ
์ฑ์ด ๋ฐฑ๊ทธ๋ผ์ด๋์์ ํน์ ์์ ์ ์ํํ ์ ์๊ฒ ํด์ค๋๋ค.
์์ ์ ๋ณต์ก๋์ ์๋์ง ์์ ๋ฐ๋ผ ๋ ๊ฐ์ง ๋ชจ๋๋ฅผ ์ง์ํฉ๋๋ค.
1. App Refresh Task
๊ฐ๋ฒผ์ด ์์ . ๋จ์ API ํธ์ถ ๋ฐ ์ ์ฅ. ์ฌ์ฉ์๊ฐ ํฐ์ ์ฌ์ฉํ๊ณ ์๋ ์๊ฐ(๋ณดํต ๋ฎ)์๋ ์คํํด์ค๋ค๊ณ ํฉ๋๋ค.
2. Processing Task
DB๋ฑ ํฌ๊ณ ๋ฌด๊ฑฐ์ด ์์ ํ ๋ ์ฌ์ฉ. ์ต์ ์ค ์ถ๊ฐ ๋ฐฐํฐ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋๊ฐ, ๋คํธ์ํฌ๋ฅผ ์ฌ์ฉํ๋๊ฐ ๋ฑ์ ์์๋ ์ง์ ํด์ค ์ ์์ต๋๋ค.
๋ฌด๊ฑฐ์ด ์์ ์ด๊ธฐ ๋๋ฌธ์, ๋ณดํต ์ฑ์ด ์ถฉ์ ์ค์ด๊ณ , idle ํ ๋ (์ ๊ฒ๋ ์ ํ๊ณ ๋๊ณ ์์ ๋) ์คํ์์ผ ์ค๋ค๊ณ ํฉ๋๋ค.
3. ์ฃผ์ ์ฌํญ
- ์ผ์ ํ ์๊ฐ์ ํธ์ถ๋์ง ์์ (์์คํ ์์ ํ๋จํ์ ๋ ๋ฐฐํฐ๋ฆฌ, ๋ฉ๋ชจ๋ฆฌ ์ํฉ์ด ๊ด์ฐฎ์ ๋, ๊ทธ๋ฆฌ๊ณ ์ฌ์ฉ์๊ฐ ์ด ์ฑ์ ์ธ์ ๋ณดํต ์ผ๋์ง ํ์ตํด ๊ทธ ์ ์ ํธ์ถํจ = ์ ์ ์์)
- ์ฌ์ฉ์๊ฐ ์ผ์ฃผ์ผ ๋์ ์ฑ์ ์ผ์ง ์์ผ๋ฉด ๋ ์ด์ Background Fetch๊ฐ ์คํ๋์ง ์์.
- UI ์กฐ์ ๋ ธ๋ ธ! ๋ฉ์ธ ์ค๋ ๋๊ฐ ์๋๋ผ์ ํฌ๋์ ๋จ. (๋คํธ์ํฌ ์ธ๋์ผ์ดํฐ๋ ์๋จ... ์ด๋ป๊ฒ ์๋๊ณ ์ฌ? ์ ๋ ์๊ณ ์ถ์ง ์์์ด์..)
4. ๊ตฌํํด๋ณด์!
์ ํ ์์ ๊ฐ ์ ๋์ด ์์ด์ ์ฝ๋์ ์์ ๋ ๊ทธ๋๋ก ๋ค๊ณ ์์ต๋๋ค.
1. ์ฑ์๊ฒ Background Fetch, Background Processing ๊ถํ์ ์ค๋๋ค.
2. Info.plist์ BGTaskSchedulerPermittedIdentifiers๋ฅผ ์ถ๊ฐํฉ๋๋ค.
์๋์ ๋ณด์ด๋ Permitted background task scheduler Identifiers ์ ๋๋ค. ๋ฌธ์์ด ๋ฐฐ์ด๋ก, ๊ฐ ์์์ ์คํํ task์ Identifier๋ฅผ ์ ๋ ฅํด์ฃผ๋ฉด ๋ฉ๋๋ค.
3. ์์ ์ ์ธํ task์ Identifier๋ก task๋ฅผ ๋ฑ๋กํฉ๋๋ค.
์์ ์์์ ์์ ์ didFinishLaunchingWithOptions๋ค์.
//AppDelegate //์ฑ ๋ฐ์นญ func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool { //์์ ์๋ฒ์์ ๋ทฐ์ปจํธ๋กค๋ฌ๋ฅผ ๊ฐ๊ณ ์ค๊ณ ํ๋ ๋ถ๋ถ. ์๋ตํจ. // ์ฌ๊ธฐ์๋ ํ์คํฌ๋ฅผ ๋ฑ๋กํด์ค๋๋ค. ์ค์ผ์คํ๋ ๊ฑด ๋ณ๋ ์์! BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.example.apple-samplecode.ColorFeed.refresh", using: nil) { task in //์ค์ ๋ก ์ํํ ๋ฐฑ๊ทธ๋ผ์ด๋ ๋์ ๊ตฌํ //๋์ค์ ์ค์ผ์ฅด ํ ๋, ํด๋น ํ์คํฌ์ ํด๋์ค๋ฅผ ์๊ณ ๋ฑ๋ก์ ํ๊ฒ ๋ฉ๋๋ค. ๊ทธ๋์ ๋ค์ด ์บ์คํ ์ด ๊ฐ๋ฅํด์. self.handleAppRefresh(task: task as! BGAppRefreshTask) } BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.example.apple-samplecode.ColorFeed.db_cleaning", using: nil) { task in // Downcast the parameter to a processing task as this identifier is used for a processing request. self.handleDatabaseCleaning(task: task as! BGProcessingTask) } return true } //์ค์ ๋ก ํ์คํฌ๋ฅผ ์ํํ๋ ๊ณณ์์๋ ๋ ๊ฐ์ง๋ฅผ ์ ๊ฒฝ์จ ์ฃผ์ธ์. // task.expirationHandler ๊ตฌํ // ์์ ์ ๊ฒฐ๊ณผ๊ฐ ์ด๋ป๊ฒ ๋์๋ , task.setTaskCompleted(success: ์์ ์ฑ๊ณต ์ฌ๋ถ) ํธ์ถํ๊ธฐ. func handleAppRefresh(task: BGAppRefreshTask) { //๋ค์ ๋์์ ๋ ์ค์ผ์ฅด ํฉ๋๋ค. ๋ฐ๋ณต ์ํ ์ ํ์ํ๊ฒ ์ฃ ? //์ค์ผ์ฅด ํ๋ ๋ฉ์๋๋ ์ด ์ฝ๋ ๋ธ๋ญ ์ดํ์ ๋์ต๋๋ค! scheduleAppRefresh() let queue = OperationQueue() queue.maxConcurrentOperationCount = 1 let context = PersistentContainer.shared.newBackgroundContext() let operations = Operations.getOperationsToFetchLatestEntries(using: context, server: server) let lastOperation = operations.last! //๋์ ๋ฐ๋ผ์๋ ๋๋ฌด ์ค๋๊ฑธ๋ฆฐ๋ค๋๊ฐ ํด์ ์ข ๋ฃ๋ ๋๊ฐ ์์ฃ . ๊ทธ ๋๋ฅผ ์ํ ์ ๋ฆฌ ํธ๋ค๋ฌ์์. task.expirationHandler = { //์์ ์์๋ OperationQueue๋ฅผ ์ฌ์ฉํ๋๊น, ๋ชจ๋ ๋์์ ์ทจ์ํ๋ค์. queue.cancelAllOperations() } //์ด ๋ถ๋ถ์ Alamofire์ api ressponse ์ฒ๋ฆฌ ๋ธ๋ก ๋ฑ์ด ๋ ์ ์๊ฒ ์ฃ ? ์์ ์์๋ Operation Queue๋ฅผ ์ฌ์ฉํ๋ค์. lastOperation.completionBlock = { //์์ ์๋ฃ ์, setTaskCompleted๋ฅผ ๋ฐ๋์ ํธ์ถํด์ฃผ์ธ์. ๋๊ธฐ๋ก ํธ์ถ๋ ํ์๋ ์์ด์! task.setTaskCompleted(success: !lastOperation.isCancelled) } queue.addOperations(operations, waitUntilFinished: false) } //BGProcessingTask์์ ์ํํ ๊ฒ์ ๊ตฌํํฉ๋๋ค. ์์ ๋๊ฐ์์! func handleDatabaseCleaning(task: BGProcessingTask) { let queue = OperationQueue() queue.maxConcurrentOperationCount = 1 let context = PersistentContainer.shared.newBackgroundContext() let predicate = NSPredicate(format: "timestamp < %@", NSDate(timeIntervalSinceNow: -24 * 60 * 60)) let cleanDatabaseOperation = DeleteFeedEntriesOperation(context: context, predicate: predicate) task.expirationHandler = { // After all operations are cancelled, the completion block below is called to set the task to complete. queue.cancelAllOperations() } cleanDatabaseOperation.completionBlock = { let success = !cleanDatabaseOperation.isCancelled if success { // Update the last clean date to the current time. PersistentContainer.shared.lastCleaned = Date() } task.setTaskCompleted(success: success) } queue.addOperation(cleanDatabaseOperation) }
4. ํ์คํฌ๋ฅผ ๋ฑ๋กํ์ผ๋ฉด, ์ค์ผ์ฅด ํ์!
์ฑ์์์ ์์ ์ AppDelegate์ applicationDidEnterBackground, ํน์ ์์ ํ์คํฌ์ ๊ตฌํ ๋ด๋ถ๋ค์.
ํ์์ ๊ฒฝ์ฐ๋ ๋ฐ๋ณต ์ํ์ ์ํด ๋ค์ด๊ฐ๋ ๊ฒ์ ๋๋ค. (๋ฌผ๋ก ์ฃผ๊ธฐ๋ ์์คํ ์ด ๋ง๋๋ฃจ..)
์ด๋, submit์ synchronousํ ๋ฉ์๋์ด๋ฏ๋ก, launch ์์ ์์ submit์ ํ๊ณ ์๋ค๋ฉด, ๋ค๋ฅธ concurrent queue์์ ๋ฑ๋ก ๋์์ ์ํํ๊ธธ ๊ถ์ฅํ๋ค๊ณ ํ๋ค์!
//AppDelegate //์ฑ์ด ๋ฐฑ๊ทธ๋ผ์ด๋์ ๋ค์ด๊ฐ์ ๋ ํธ์ถ ๋จ. func applicationDidEnterBackground(_ application: UIApplication) { scheduleAppRefresh() scheduleDatabaseCleaningIfNeeded() } // ์ค์ ๋ก ํ์คํฌ๋ฅผ ์ค์ผ์ค ํ๋ ๋ถ๋ถ. //BGAppRefreshTaskRequest๋ฅผ ๋ง๋ค์ด ๋ด ๋๋ค func scheduleAppRefresh() { //1. ์ํ๋ ํํ์ TaskRequest๋ฅผ ๋ง๋ญ๋๋ค. ์ด ๋, ์ฌ์ฉ๋๋ identifier๋ ์์ 1, 2๊ณผ์ ์์ ๋ฑ๋กํ info.plist์ identifier์ฌ์ผ ํด์! let request = BGAppRefreshTaskRequest(identifier: "com.example.apple-samplecode.ColorFeed.refresh") //2. ๋ฆฌํ์คํธ๊ฐ ์ธ์ ์คํ๋๋ฉด ์ข๊ฒ ๋์ง ์ง์ ํฉ๋๋ค. ๊ธฐ์กด์ setMinimumFetchInterval๊ณผ ๋์ผํ๋ค๊ณ ํฉ๋๋ค. //์ฌ์ ํ, ์ธ์ ์คํ๋ ์ง๋ ์์คํ ์ ๋ง์์ ๋๋ค... request.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60) //3. ์ค์ ๋ก task๋ฅผ submit ํฉ๋๋ค. //์ด ๋ ์ฃผ์์ฌํญ์, submit์ synchronousํ ํจ์๋ผ, launching ๋ ์คํํ๋ฉด ๋ฉ์ธ ์ค๋ ๋๊ฐ ๋ธ๋ฝ ๋ ์ ์์ผ๋ //OperationQueue, GCD๋ฑ์ ์ด์ฉํด ๋ค๋ฅธ ์ค๋ ๋์์ ํธ์ถํ๋ ๊ฒ์ ๊ถ์ฅํ๋ค๊ณ ํ๋ค์. do { try BGTaskScheduler.shared.submit(request) } catch { print("Could not schedule app refresh: \(error)") } } //BGProcessingTaskRequest๋ฅผ ์ฌ์ฉํฉ๋๋ค. ์ต์ ์ด ์๋ ๊ฑธ ์ ์ธํ๋ฉด ์์ ๋์ผํด์. func scheduleDatabaseCleaningIfNeeded() { let lastCleanDate = PersistentContainer.shared.lastCleaned ?? .distantPast let now = Date() let oneWeek = TimeInterval(7 * 24 * 60 * 60) // Clean the database at most once per week. guard now > (lastCleanDate + oneWeek) else { return } //์ด๋ฒ์ ๋ฌด๊ฑฐ์ด DB ์์ ์ด๋๊น, BGProcessingTaskRequest๋ฅผ ์ค์ผ์ฅด ํด์ค๋๋ค. let request = BGProcessingTaskRequest(identifier: "com.example.apple-samplecode.ColorFeed.db_cleaning") //๋คํธ์ํฌ ์ฌ์ฉ์ฌ๋ถ, ์๋์ง ์๋ชจ๋ ์ต์ ๋ ์์ต๋๋ค. request.requiresNetworkConnectivity = false request.requiresExternalPower = true do { try BGTaskScheduler.shared.submit(request) } catch { print("Could not schedule database cleaning: \(error)") } }
5. ๋ฐฑ๊ทธ๋ผ์ด๋ ํ์คํฌ ์คํ ํ ์คํธ ํด๋ณด๊ธฐ
๊ธฐ์กด์ Simulate Background Fetch๋ก๋ ๋์ํ์ง ์์ต๋๋ค ใ ใ ใ ์๋ ์ฒ๋ผ, ์ง์ ์ฝ์์ ๋ช ๋ น์ด๋ฅผ ์ ๋ ฅํด์ค์ผ ํด์.
1. BGTaskScheduler์๊ฒ task๋ฅผ submitํ๋ ์ฝ๋ ์คํ.
2. ๊ทธ ๋ค์ ์์ ์ break point ์ก๊ธฐ
3. break ํฌ์ธํธ๊ฐ ๊ฑธ๋ฆฌ๋ฉด ์๋ ๋ช ๋ น์ด ์ ๋ ฅํ๊ธฐ.
4. ๋ฐฑ ๊ทธ๋ผ์ด๋์์ ์คํ์ (์ ๊ฐ์ด didEnterBackground ์์ ) ์๋ ๋ช ๋ น์ด ์ ๋ ฅ ํ, ๋ค์ ์ฑ์ foreground์์ ์คํ์์ผ ์ฃผ์ด์ผ ์ ๋๋ก ๋ช ๋ น์ด๊ฐ ์คํ๋ฉ๋๋ค.
๋ฐ๋์ ์ค ๊ธฐ๊ธฐ์์ ์คํ์์ผ์ผ ํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ๋๋ฒ๊ทธ ์ฉ๋๋ก๋ง ์ฌ์ฉํด๋ฌ๋ผ๊ณ ๊ฒฝ๊ณ ๊ฐ ์๋ค์.
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"Info.plist์ ๋ฑ๋กํ ํ์คํฌ Identifier"]
์ ๋ช ๋ น์ด๊ฐ ์ ๋๋ก ์คํ๋๋ฉด ์ฝ์์ ์๋ ๋ฌธ๊ตฌ๊ฐ ์ถ๋ ฅ๋๊ณ , ํ์คํฌ ๋ด์์์ BP๋ ์กํ๋๋ค.
Simulating launch for task with identifier ์๊น ์คํ ์ํจ ํ์คํธ Identifier
Starting simulated task: <BGAppRefreshTask: ์๊น ์คํ ์ํจ ํ์คํธ Identifier>๋ฉ์ธ ์ค๋ ๋์์ ์คํ๋์ง ์๋๋ค๋ ์ ์ฐธ๊ณ ํด์ฃผ์ธ์.
6. ์ฐธ๊ณ ์๋ฃ์ ๋ ๋ณด๋ฉด ์ข์ ๊ฒ๋ค
์ฌ์ฉ ์์ ๊ฐ ์ ๋๋ฌ๋ ๊ธ
WWDC 2019 ์ Background Execution ์์
https://developer.apple.com/videos/play/wwdc2019/707/
์ํ ํ๋ก์ ํธ๋ ๋ฐ๋์ ์ค ๊ธฐ๊ธฐ์์ ๋์๊ฐ์ผ ํ๋ค๊ณ ํฉ๋๋ค!
์ ํ ๋ฌธ์
Background Tasks Framework ์ค๋ช
https://developer.apple.com/documentation/backgroundtasks
Background Task ํ ์คํธ ํ๊ธฐ
+ ๋จผ์ ๊ณต๋ถํ๋ฉด ์ข์ ๊ฒ
OperationQueue. task๋ฅผ ์ถ๊ฐํ๋ ๊ฐ๋ ๋ฑ์ Operation Queue์์ ์จ ๊ฒ์ด ๋ง์ ๋ณด์ ๋๋ค. ์ ๋ ์ด ๋ถ๋ถ ์ฝ๋๋ฅผ ์ดํดํ๋ ๋ฐ ์ ๋จน์ด์... ๊ณต๋ถ๋ฅผ ์ข ๋ ํด์ผ๊ฒ ๋ค์!
+ ์ฌ๋ด
์์ ์ฝ๋์์ ๋ชฉ ์๋ฒ๋ฅผ ๋ง๋ ๋ฐฉ์์ด ๊ฝค๋ ๋ง์์ ๋ค์ด์. ๋์ค์ ํ ์คํธ ํ๋ก์ ํธ๋ฅผ ๋ง๋ค ๋ ํ ๋ฒ ๋ฐ๋ผํด๋ณผ๊น ํฉ๋๋ค.
'๊ณต๋ถ > iOS' ์นดํ ๊ณ ๋ฆฌ์ ๋ค๋ฅธ ๊ธ