iOS&Swift🍎/iOS

iOS Background Tasks(백그라운드에서 API 호출하기)

천 원 2024. 2. 20. 10:13

안녕하세요. 천원입니다.

백그라운드에서 API를 호출하기 위해서 학습한 내용을 정리 하고자 이글을 작성합니다.

 

 

 

Background Tasks 프레임워크

iOS 13부터 추가된 프레임워크로 시스템과 앱간의 background Task 요청을 담당하는 역할을 합니다.

해당 프레임워크에는 BGAppRefreshTask & BGProcessingTask 이렇게 두가지 타입이 존재하는데 BGAppRefreshTask는 30초 정도가 소요되는 작업에 사용되고 그 이상의 몇분이 소요되는 것들은 BGProcessingTask를 사용하게 됩니다.

 

WWDC 2019 자료를 참고해 보면 사용자가 Foreground로 진입하는 시점을 시스템이 분석해서 그전에 호출하게 되고 그 외에도 유저의 백그라운드 앱 새로고침 설정, 배터리 충전 상태, 네트워크 연결 등의 여부에 따라 호출하며 앱을 호출하는 정확한 시점은 파악할 수 없습니다.

 

 

 

구현해보기

먼저 Signing&Capabilities 에서 Background Modes를 추가해 주고, Background fetch와 Background processing을 체크해 줍니다. 각각 BGAppRefreshTask와 BGProcessingTask에 대한 설정입니다.

 

 

 

 

 

 

그 다음 스케줄러에 사용할 identifier를 Info.plist에 추가해 줍니다.

Permitted background task scheduler identifiers 을 추가해 주고 Item의 Value 에 String 타입으로 identifier를 작성해 줍니다.

 

 

 

 

 

 

 

다음으로 TasK를 AppDelegate의 didFinishLauchingWithOptions 함수에 등록해 줍니다.

각각의 핸들러 내부에는 실제로 수행할 백그라운드 동작을 구현해 주는데 이때 파라미터로 task를 함께 호출해 줍니다.

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    // Override point for customization after application launch.

    BGTaskScheduler.shared.register(forTaskWithIdentifier: "example.com", using: nil) { task in
        self.handleAppRefresh(task: task as! BGAppRefreshTask) // 실제로 수행할 코드가 구현된 매서드
    }

    // 2. Processing Task
    BGTaskScheduler.shared.register(forTaskWithIdentifier: "example2.com", using: nil) { task in
        self.handleProcessingTask(task: task as! BGProcessingTask)
    }

    return true
}

 

 

 

 

 

수행할 코드의 구현부를 간단하게 보자면 저는 로그를 기록할 파일을 만들어 백그라운드에서 호출될 때 파일에 기록되도록 코드를 작성했고, 함수 내부에서 setTaskCompleted(success: true)를 통해서 task의 성공여부를 반드시 호출해 주어야 합니다. 

func handleAppRefresh(task: BGAppRefreshTask) {

    self.writeLog(_writeValue: "백그라운드 호출 기록")

    task.expirationHandler = {
        task.setTaskCompleted(success: false)
    }

    task.setTaskCompleted(success: true)
}

저는 BGProcessing Task를 사용하는 handleProcessingTask 함수 또한 동일한 코드로 구성하였습니다.

 

 

 

 

 

수행할 Task등록이 되었다면 우리는 Scene이 백그라운드에 진입할 때 해당 Task를 호출할 수 있도록 스케줄러를 등록해 줍니다.

등록시에 timeInterval을 설정할 수 있지만 원하는대로 동작하지 않습니다.

func scheduleAppRefresh() {
    let request = BGAppRefreshTaskRequest(identifier: "example.com") // 설정한 identifier
    request.earliestBeginDate = Date(timeIntervalSinceNow: 60 * 60) // 호출할 timeInterval을 1시간으로 설정

    do {
        try BGTaskScheduler.shared.submit(request)
    } catch {
        print("Could not schedule app refresh: \(error)")
    }
}

func scheduleProcessingTaskIfNeeded() {
    let request = BGProcessingTaskRequest(identifier: "example2.com")
    request.requiresExternalPower = false // 에너지 소모량
    request.requiresNetworkConnectivity = false // 네트워크 사용여부 옵션

    request.earliestBeginDate = Date(timeIntervalSinceNow: 60 * 60)

    do {
        try BGTaskScheduler.shared.submit(request)
    } catch {
        print("\(Date()): Could not schedule processing task: \(error)")
    }
}

 

 

 

 

 

 

 

스케줄러를 등록하는 함수를 sceneDidEnterBackground 에서 호출해 줍니다.

func sceneDidEnterBackground(_ scene: UIScene) {
    scheduleAppRefresh()
    scheduleProcessingTaskIfNeeded()
}

 

 

 

 

테스트해 보기

이제 스케줄러까지 등록을 했으면 테스트를 진행해야 하지만 백그라운드에서 동작하기에 Xcode에서 테스트하기 쉽지 않습니다.

그러니 차근차근 진행해 봅시다.

 

 

앱을 실행 후 해당 지점에 브레이크 포인트를 걸어두고 백그라운드에 진입합니다.

 

그런다음 아래에 명령어를 lldb에 입력해 줍니다.

e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"Info.plist에 등록한 태스크 Identifier"]

 

 

 

명령어가 실행되면 다시 포그라운드 진입 후 Continue 해주면 정상적으로 handleAppRefresh 함수를 호출하는 모습입니다.

 

 

 

 

아래는 기록된 백그라운드 호출 로그입니다.

 

 

출처:

https://itnext.io/swift-ios-13-backgroundtasks-framework-background-app-refresh-in-4-steps-3da32e65bc3d