Swift(스위프트): 백그라운드 작업 (Background Tasks)
소개
BackgroundTasks는 앱이 백그라운드에서 작업을 할 수 있게 하는 프레임워크입니다. (iOS 13부터 이용 가능)
스토리보드 기준으로 설명합니다.
분류
작업의 복잡도, 에너지 양(배터리 소모정도)에 따라 두 가지로 분류합니다.
- App Refresh Task 상대적으로 가벼운 작업(단순 API 호출 또는 저장 등)에 사용합니다. 실행 빈도가 높고, 사용자가 기기를 사용하는 중에도 작업이 실행됩니다.
- Processing Task 상대적으로 무거운 작업(DB 등)에 사용합니다. 옵션으로 배터리 충전이 요구되는지, 네트워크 연결이 요구되는지를 지정할 수 있습니다. 보통 충전중이고 기기가 idle인 상태에서 작업이 실행됩니다.
구현 방법
1: 백그라운드 작업에 대한 권한 추가
프로젝트 설정 메뉴에서 Signing and Capabilities 탭 > + Capability 로 Background Mode를 추가한 뒤
- Background fetch
- Background Processing
를 체크합니다.
2: Info.plist에 식별자(identifier) 추가
Info.plist 파일을 연 뒤, BGTaskSchedulerPermittedIdentifiers(Permitted background task scheduler Identifiers) 키를 추가합니다. 그 배열(Array) 하위 요소로 String 타입의 백그라운드 식별자를 추가합니다.
형식은 일반적으로 기존 앱 번들 식별자(App Bundle Identifier) + 백그라운드 요소 이름으로 지정합니다. 아래 스크린샷은 App Refresh Task(refresh_badge)와 Processing Task(refresh_process) 두 개를 추가했습니다.
- 예) com.example.ExampleApp.refresh_badge(process)
3: AppDelegate의 …didFinishLaunchingWithOptions… 안에 작업(task) 등록
BGTaskScheduler.shared.register(...)를 이용해 백그라운드 작업들을 등록합니다.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import UIKit
import BackgroundTasks
@main
class AppDelegate: UIResponder, UIApplicationDelegate {
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// 1. App Refresh Task
BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.bgsmm.LanguageWeb.refresh_badge", using: nil) { task in
self.handleAppRefresh(task: task as! BGAppRefreshTask)
}
// 2. Processing Task
BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.bgsmm.LanguageWeb.refresh_process", using: nil) { task in
self.handleProcessingTask(task: task as! BGProcessingTask) // 타입 캐스팅 유의 (BG'Processing'Task)
}
// BackgroundTask: https://lemon-dev.tistory.com/entry/iOS-BackgroundTask-Framework-간단-정리 [lemon_dev:티스토리]
return true
}
// ... //
}
- forTaskWithIdentifier
Info.plist파일에서 입력했던 작업 식별자를 입력합니다. 여러 식별자가 있는 경우 각각에 대해 따로register합니다.
handleAppRefresh,handleProcessingTask- 각 작업당 해야 할 일을 지정한 메서드로, 밑에서 구현합니다.
🚧 이 글은 작성중입니다.
빠른 시일 내에 포스트 작성을 완료하겠습니다.
4: handleAppRefresh, handlerProcessingTask 함수 구현
1
2
3
4
5
6
7
8
9
10
11
12
func handleAppRefresh(task: BGAppRefreshTask) {
// 다음 동작 수행, 반복시 필요
scheduleAppRefresh()
task.expirationHandler = {
task.setTaskCompleted(success: false)
}
// 가벼운 백그라운드 작업 작성
task.setTaskCompleted(success: false)
}
1
2
3
4
5
6
7
8
func handleProcessingTask(task: BGProcessingTask) {
task.expirationHandler = {
task.setTaskCompleted(success: false)
}
// 무거운 백그라운드 작업 작성
task.setTaskCompleted(success: true)
}
task.setTaskCompleted(success: true/false)로 작업 성공 여부를 알려줘야 합니다.
5: 스케줄러 함수 scheduleAppRefresh, scheduleProcessingTaskIfNeeded 작성
AppDelegate 클래스 내부에 작성합니다.
1
2
3
4
5
6
7
8
9
10
func scheduleAppRefresh() {
let request = BGAppRefreshTaskRequest(identifier: "com.example.ExampleApp.refresh_badge")
do {
try BGTaskScheduler.shared.submit(request)
// Set a breakpoint in the code that executes after a successful call to submit(_:).
} catch {
print("\(Date()): Could not schedule app refresh: \(error)")
}
}
1
2
3
4
5
6
7
8
9
10
11
12
func scheduleProcessingTaskIfNeeded() {
let request = BGProcessingTaskRequest(identifier: "com.example.ExampleApp.refresh_process")
request.requiresExternalPower = false
request.requiresNetworkConnectivity = false
do {
try BGTaskScheduler.shared.submit(request)
// Set a breakpoint in the code that executes after a successful call to submit(_:).
} catch {
print("\(Date()): Could not schedule processing task: \(error)")
}
}
- try 밑의 주석이 써져 있는 라인에서 breakpoint를 걸어 강제로 백그라운드 작업을 트리거할 수 있습니다. (후술)
6: SceneDelegate 클래스의 sceneDidEnterBackground(…) 메서드 내에 스케줄러 호출 부분 작성
1
2
3
4
5
6
7
8
9
func sceneDidEnterBackground(_ scene: UIScene) {
// Called as the scene transitions from the foreground to the background.
// Use this method to save data, release shared resources, and store enough scene-specific state information
// to restore the scene back to its current state.
let appDelegate = UIApplication.shared.delegate as! AppDelegate
appDelegate.scheduleAppRefresh()
appDelegate.scheduleProcessingTaskIfNeeded()
}
sceneDidEnterBackground은 앱이 백그라운드 모드가 될 때마다 호출되는 메서드이며, 여기에 백그라운드 작업 스케줄러를 불러옵니다.
강제로 백그라운드 작업 트리거
scheduleAppRefresh,scheduleProcessingTaskIfNeeded함수에서submit(_:)에 대한 성공적인 호출 후 실행되는 코드(try BGTaskScheduler.shared.submit(request))에 중단점(breakpoint)을 설정합니다.
- 실제 기기에서 앱을 실행하고, 중단점에 걸릴 때까지 앱을 실행합니다. (=> 백그라운드 모드로 들어감)
-
디버거에서 아래 명령을 실행하여
TASK_IDENTIFIER를 원하는 작업의 식별자(예:com.example.ExampleApp.refresh_...)로 바꿉니다.1
e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"TASK_IDENTIFIER"]
- 홈 화면에서 앱 아이콘을 실행해 다시 실행합니다. 시스템은 해당 작업에 대한 실행 핸들러를 호출합니다.
아래 스크린샷은 백그라운드 작업이 실행할 때마다 텍스트 파일에 로그를 기록하도록 한 경우에서 해당 로그의 기록입니다. (App Background는 앱 리프레시 작업, Processing Task는 프로세싱 작업)





