VesselWheel

Background Tasks 본문

Xcode Study

Background Tasks

JasonYang 2024. 2. 27. 19:56

https://www.youtube.com/watch?v=Lb7OShyNSdM

https://developer.apple.com/documentation/backgroundtasks/

 

Background Tasks | Apple Developer Documentation

Request the system to launch your app in the background to run tasks.

developer.apple.com

https://velog.io/@yoosa3004/iOS-Background-Mode-Background-Task

 

[iOS] Background Mode, Background Task

오늘은 BackgroundMode와 BackgroundTask에 대해 정리해보겠습니다.iOS앱은 기본적으로 포그라운드, 즉 사용자가 앱을 열어 활성화한 경우에만 작동합니다.하지만 사용자가 홈으로 나가거나 App Switcher로

velog.io


이해를 돕기 위해 유튜브에서 관련 영상을 참고하고 공식문서와 블로그를 참고하였다. 

실제 구현 관련 공식문서가 있어 한번 더 참고하였다. 

https://developer.apple.com/documentation/uikit/app_and_environment/scenes/preparing_your_ui_to_run_in_the_background/using_background_tasks_to_update_your_app

 

Using background tasks to update your app | Apple Developer Documentation

Configure your app to perform tasks in the background to make efficient use of processing time and power.

developer.apple.com

 

더보기
import UIKit
import CoreData
import BackgroundTasks

@main
class AppDelegate: UIResponder, UIApplicationDelegate {

    let taskID = "RunningTimer"  // Info.plist에 등록한 키 값을 전역 상수로 선언
    var myTimer: MyTimer?

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // Register handler for task, Task는 launch Handler를 갖는 BGTaskScheduler 객체로 고유한 식별자를 갖는다.
        // 모든 Task는 app launch sequence가 끝나기 전에 BGTaskScheduler에 등록해야하기 때문에, launch가 끝나는 시점인 didFinishLaunchingWithOptions에서 호출하면 된다.
        registerBackgroundTasks()
        
        return true
    }

//    앱의 launch sequence가 끝나기 전에 Background Task를 Scheduler에 "등록"해야 합니다.
//    Info.plist에 등록한 키 값으로 등록해야 합니다.
    private func registerBackgroundTasks() {
        print("Background Task 등록!")
        // 1. Refresh Task 등록
        BGTaskScheduler.shared.register(forTaskWithIdentifier: taskID, using: nil) { task in
            // Handle the task in your app
            self.handleAppRefresh(task: task as! BGAppRefreshTask)
        }
    }
    
    // 2. 실제로 수행할 Background 동작 구현
    func handleAppRefresh(task: BGAppRefreshTask) {
        // Schedule a new refresh task
        scheduleAppRefresh()
        
        // Initialize MyTimer and start it
        myTimer = MyTimer(time: 0, distance: 0.0, pace: 0.0, updateUI: {
            // Post a notification with the timer's state
            NotificationCenter.default.post(name: Notification.Name("TimerUpdated"), object: self.myTimer)
        })
        myTimer?.startTimer()
        
        // Get the current status of the timer
        let status = self.myTimer?.getCurrentStatus() ?? "No Status"
        print(status)
        
        // Provide an expiration handler for the background task
        // that cancels the operation
        task.expirationHandler = {
            self.myTimer?.stopTimer()
            task.setTaskCompleted(success: false)
        }
    }
    
    func scheduleAppRefresh() {
        let request = BGProcessingTaskRequest(identifier: taskID)  //BGProcessingTaskRequest, BGAppRefreshTaskRequest
        request.requiresExternalPower = true // 배터리를 사용할 것인지 여부
        request.requiresNetworkConnectivity = true // 네트워크를 사용할 것인지 여부
        request.earliestBeginDate = Date(timeIntervalSinceNow: 1 * 60) // Fetch no earlier than 1 minutes from now
        do {
            try BGTaskScheduler.shared.submit(request)
        } catch {
            print("Could not schedule app refresh: \(error)")
        }
    }

요약 해석

더보기

백그라운드 작업은 앱이 포그라운드에서 실행되지 않아도 실행되는 작업을 말합니다. 이 코드에서는 백그라운드 작업을 이용해 러닝 타이머를 구현하고 있습니다.

  1. application(_:didFinishLaunchingWithOptions:) 함수에서 백그라운드 작업을 등록합니다. 이 함수는 앱이 처음 시작될 때 호출됩니다.
  2. registerBackgroundTasks() 함수에서는 백그라운드 작업을 등록합니다. BGTaskScheduler.shared.register를 사용해 작업을 등록하고, 해당 작업이 실행될 때 호출될 클로저를 정의합니다. 여기서는 handleAppRefresh(task:) 함수를 호출하도록 되어 있습니다.
  3. handleAppRefresh(task:) 함수에서는 실제로 백그라운드 작업을 처리합니다. 먼저, 다음 백그라운드 작업을 예약하고, MyTimer 인스턴스를 생성하고 시작합니다. 그리고 MyTimer의 상태를 콘솔에 출력합니다. 마지막으로, 작업이 시스템에 의해 중지되어야 할 때를 대비해 만료 핸들러를 제공합니다. 만료 핸들러에서는 타이머를 중지하고 작업을 완료하지 않았음을 시스템에 알립니다.
  4. scheduleAppRefresh() 함수에서는 다음 백그라운드 작업을 예약합니다. BGAppRefreshTaskRequest를 생성하고, BGTaskScheduler.shared.submit을 사용해 작업을 예약합니다.

이 코드를 통해 앱이 백그라운드에서도 활성 상태를 유지하면서 러닝 타이머 기능을 계속 사용할 수 있게 됩니다.

-> Background Tasks의 유형 : BGAppRefreshTaskRequest, BGProcessingTaskRequest

더보기

`BGAppRefreshTaskRequest`와 `BGProcessingTaskRequest`는 iOS의 BackgroundTasks 프레임워크에서 제공하는 두 가지 주요 백그라운드 작업 유형입니다. 각각 다음과 같은 상황에서 사용됩니다:

 

1. `BGAppRefreshTaskRequest`: 앱의 콘텐츠를 최신 상태로 유지하거나 앱이 백그라운드에 있을 때 작은 작업을 수행하는 데 사용됩니다. 예를 들어, 앱이 백그라운드에 있을 때 새로운 데이터를 미리 가져오거나, 알림을 스케줄링하는 등의 작업을 수행할 수 있습니다. 이 작업은 시스템에 큰 부담을 주지 않으며, 사용자의 배터리 수명에 큰 영향을 미치지 않아야 합니다.

2. `BGProcessingTaskRequest`: 더 많은 자원을 필요로 하는 작업에 사용됩니다. 이 작업은 사용자의 배터리를 많이 사용하거나, 네트워크 연결이 필요하거나, 외부 전원이 연결된 상태에서만 수행될 수 있습니다. 이 작업은 대개 사용자가 앱을 사용하지 않는 시간에 큰 데이터를 처리하거나 다운로드하는 등의 작업을 수행하는 데 사용됩니다.

두 작업 유형 모두 백그라운드에서 실행되지만, 각각의 작업 유형은 서로 다른 요구 사항과 제한 사항을 가지고 있습니다. 따라서 앱의 요구 사항에 따라 적절한 작업 유형을 선택해야 합니다.

-> 외부 전원이 연결된 상태에서 달리기는 하는 것이 아니고, 타이머를 UI로 알림하는 것이므로 BGAppRefreshTaskRequest로 진행

=> 하지만 러닝 타이머는 지속적인 타이머를 운용할 필요는 없고 Scene이 포그라운드에서 백그라운드로,  백그라운드에서 포그라운드로 넘어갈 때 러닝기록 프로퍼티를 저장하고, 호출하면 배터리 사용없이 Scene의 변경마다 UI나 Task를 할당 할 수 있다고 한다.

 

따라서, 아래의 글을 참고하여 구현해보고자 한다. 

https://medium.com/@Ariobarxan/ios-application-scene-delegate-vs-app-delegate-a-talk-about-life-cycle-a2ecae9d507e 

 

iOS Application Scene Delegate VS App Delegate(A talk about Life cycle)

Preface

medium.com