iOS

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

Thor_yeom 2024. 7. 23. 20:08

안녕하세요! 
오늘은 Background에서 API 호출 및 앱 갱신을 통해 사용자에게 지속적인 데이터를 

제공하는 방법에 대해 알아 보겠습니다.

 

 

공식 예제를 통해 브레이크 포인트를 사용해서 

어떻게 구성되어 있는지 확인해보는 것도 좋을 거 같습니다. 아래는 공식문서에 있는 예제입니다.

https://developer.apple.com/documentation/backgroundtasks/refreshing-and-maintaining-your-app-using-background-tasks

 

Refreshing and Maintaining Your App Using Background Tasks | Apple Developer Documentation

Use scheduled background tasks for refreshing your app content and for performing maintenance.

developer.apple.com

 

 

 

 

자. 그럼 본격적으로 시작하겠습니다. Background Task에는 BGAppRefreshTaskBGProcessingTask가 있습니다.

 

 

 

BGAppRefreshTaskBGProcessingTask 모두 동일하게 백그라운드에서 작업을 처리 하긴 하지만

가장 큰 차이점은 시간에 있습니다.

 

30초 정도 걸리는 작업은 BGAppRefreshTask을 사용하고 
그 이상 걸리는 작업( 대용량 파일 업로드/다운로드, 데이터베이스 유지관리, 백그라운드에서 실행 ) 할때 BGProcessingTask를 실행합니다.

 

이번 포스팅에서는 BGAppRefreshTask을 중점적으로 다루겠습니다

 

 

 

 

 

Background Task를 적용하면서 느낀점은 크게 

  • 등록
  • 스케쥴링
  • 실행
  • 완료

이러한 과정을 거쳐서 Background에서 Task가 작동 된다는 것입니다. 

 

 

 

 

더욱 자세한 내용은 순서에 맞게 진행하면서 설명드리겠습니다.

 

 

등록

1. background Mode 설정 및 Info.plist 설정

 

 

Info.plist에서 Source Code를 통해 추가해 줄 수 있습니다.

대체적으로 item 0에는  Bundle.refresh를 붙이는 편임 ( 하지만 예제이기 때문에 example.com으로 함 )

<key>BGTaskSchedulerPermittedIdentifiers</key>
	<array>
	<string>example.com</string>
	</array>

 

 

 

여기까지 셋팅이 됐으면 AppDelegate에서 Baground Task를 적용합니다.

import와 앱이 시작 되었을때 Background에서 Task가 호출될 수 있게 등록(register)을 합니다.

 

스케쥴링

스케쥴링은 언제 Background Task를 실행하는지를 스케쥴( 시간 )을 조정하는 부분입니다.

하지만!! Background Task가 실행하는 것은 시스템에 의해 자동으로 실행 되기 때문에 사용자가 정할 순 없습니다. ( 애플 맘대루... )

공식문서 출처

 

 

 

AppDelegate에 추가로 scheduleAppRefresh()를 만들어 줍니다.

시스템에 의해 Background Task가 실행되면

try 문에 submit 부분이 실행됩니다.

 

 

 

 

백그라운드에서 시스템에 의해서 자동으로 실행된다면...
언제 되는지 계속 기다려야 하나요?

 

 

네니요. ( 그렇게 매정하진 않더라구요... 물론 debug 한정이지만...)

 

 

https://developer.apple.com/documentation/backgroundtasks/starting-and-terminating-tasks-during-development

 

Starting and Terminating Tasks During Development | Apple Developer Documentation

Use the debugger during development to start tasks and to terminate them before completion.

developer.apple.com

 

공식문서를 살펴볼게요 

요약해서 말씀드리면 

 

Background Tasks가 실행 될때까지 수 시간이 걸릴 수 있으니

일찍이 Background Tasks를 실행 할 수 있게 하는 LLDB 명령어가 있다!

 

 단, debug 상태일만 사용해라 

 

공식문서를 보면 해당 명령어가 있습니다.

e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"Info.plist에 적은 item 0 Bundle.refresh"]

 

어떻게 사용하는지는 실행을 하면서 보여드리겠습니다!

 

 

실행

직접적으로 Task가 실행되어 작업이 진행되는 부분입니다. 

공식문서에 보면 OperationQueue를 사용하여 비동적으로 실행 수 있게 해주었습니다.

 

 

왜 OperationQueue를 사용하여
비동기 작업을 했을까?

 

너무 딥하게 들어가면 주제에서 벗어나기 때문에

제가 느꼈던 부분은 

 

OperationQueue를 사용할때의 장점들 때문이라고 생각했습니다. 크게 3가지 정도 입니다. 

  • 작업을 취소하거나 중지할 수 있음
  • 의존성 관리로 인해 순서대로 작업 할 수 있음 ex) 1번 끝나면 2번 실행
  • 상태 모니터링을 통한 관리 

 

OperationQueueDispatchQueue보다 유연하게 작업 관리가 가능하구나 라고만 

알고 가시면 좋을거 같습니다

 

 

 

 

공식문서에 있는 코드를 거의 그대로 가져왔습니다.

 

 

백그라운드 상태에서 반복적으로 실행하기 위해 

scheduleAppRefresh()를 추가하여 Background Tasks가 실행할때마다 

다음 작업을 예약 합니다.

 

 

실행할 작업은 
만들어 놓은 RefreshAppContentsOperation()를 실행합니다.

let operation = RefreshAppContentsOperation()

 

RefreshAppContentsOperation 클래스를 간단히 살펴보면

해당 백그라운드 실행 시간을 UserDefaults에 담아주고 저장하는 클래스 입니다.

import Foundation

class RefreshAppContentsOperation: Operation {
    override func main() {
        if isCancelled {
            return
        }
        
        // 현재 날짜를 UserDefaults에 저장하는 작업을 수행
        let date = Date()
        // UserDefault를 사용하여 add 및 save 
        TaskModel.shared.addTaskDate(date)
        
        // 작업 완료 시 로그 출력
        print("Background task executed and date saved at \(date)")
    }
}

class TaskModel {
    static let shared = TaskModel()
    
    private let userdefaultsKey = "TaskDates"
    
    private init () { }
    
    func addTaskDate(_ date: Date) {
        var dates = fetchTaskDates()
        dates.append(date)
        saveTaskDates(dates)
    }
    
    func fetchTaskDates() -> [Date] {
        let dateArray = UserDefaults.standard.array(forKey: userdefaultsKey) as? [Date] ?? []
        print("백그라운드에서 쌓인 date : \(dateArray)")
        return dateArray
    }
    
    private func saveTaskDates(_ dates: [Date]) {
        UserDefaults.standard.setValue(dates, forKey: userdefaultsKey)
    }
}

 

 

자 이렇게 되면 모든 준비가 끝났습니다. 

어떻게 진행 되는지 순서도로 표현하자면

 

 

백그라운드 상태 진입 -> 스케쥴 실행 -> 등록한 Background Task 실행 -> HandleAppRefresh 실행 ( 작업 ) -> 다음 Background Task 예약 -> 작업 비동기적으로 실행 -> 완료

 

 

 

 

이렇게 하면 실행이 됩니다.!!! 끝!!

 

 

저희 한가지 잊은 사실이 있지 않으신가요?? ㅎㅎ

 

 

바로...

.

.

.

.

.

.

.

 

 

 

백그라운드 조기 실행하는 LLDB 명령어를 사용안했습니다..

 

물론 앱을 백그라운드 상태에 두고 Xcode 계속 열어두시면  print찍어 놓은 부분이 

실행 될테지만... 언제 되냐고.... 퇴근 안하냐고... 

 

 

 

자.!! 

 

그래서 어디에서 LLDB를 명령어를 실행하면 좋냐!!

바로  백그라운드 상태에 진입했을 시점에 브레이크 포인트를 걸고 

LLDB 명령어를 실행하면 됩니다.

 

 

 

단, 백구라운드 상태 진입하고 브레이크 포인트 걸자마자 

바로 LLDB에 명령어 입력을...... 해도 되긴 하지만!

5분에서 10분정도 시간이 흐른뒤 LLDB 명령어를 적으면 더 극적으로 결과가 보이실 겁니다!!

 

 

모두 고생하셨습니다! 긴글 봐주셔서 감사합니다.