iyOmSd/Title: Swift

[Swift] BGContinuedProcessingTask 백그라운드 작업

냄수 2026. 5. 29. 22:11
반응형

백그라운드에서 작업을 돌릴 수 있는 타입에대해 알아보려합니다

 

백그라운드 전환 이후에도 작업을 계속 수행하고 시스템은 UI를 통해 진행 상황을 표시를 지원합니다

- 버튼 클릭이나 다른 제스처처럼 항상 명확한 사용자 행동으로 시작됩니다 

- 각 작업은 즉각적이고 명확한 목표를 가지고 있습니다 (파일 내보내기 SNS 콘텐츠 게시 연결된 액세서리 업데이트 등)

- 이러한 작업은 측정 가능한 진행률을 가지고 있으며 작업이 완료됐을 때 무엇을 의미하는지 알 수 있습니다

 

시스템은 리소스 부족과 같은 런타임 상황에 따라 백그라운드 작업을 갑작스럽게 종료할 수 있습니다.

구현 시에는 이 작업이 준수하는 ProgressReporting 프로토콜을 사용하여 진행 상황을 보고해야 합니다.

리소스가 부족해지면 시스템은 진행 상황이 거의 없거나 전혀 없는 작업의 종료를 우선적으로 처리합니다

 

BGContinuedProcessingTask
  - 👍 즉시 시작 / 진행률 UI 자동 / 사용자가 기다리는 작업이라 시스템이 우호적(런타임 넉넉) / GPU·CPU·네트워크 사용 가능
  - 👎 iOS 26+ 전용 / 반드시 포그라운드 사용자 액션에서 제출(서버·백그라운드 트리거 ❌) / progress 필수 / 무한은 아님(자원 압박 시 expirationHandler로 회수) / 사용자에게 보이는 작업에만 적합

 

 

기존에 쓰던 백그라운드 방식과 비교해볼까요?

  BGProcessingTask
  - 👍 수 분 이상 긴 작업 / "충전 중·네트워크 필요" 조건 지정 가능 / 무거운 유지보수에 최적
  - 👎 실행 시점을 시스템이 결정(즉시성 없음, 보통 밤/충전) / UI 없음 / 사용자 인지 못 함

  BGAppRefreshTask
  - 👍 사용 패턴 학습해 "열어볼 만한 때" 갱신 / 콘텐츠 신선도 유지에 적합
  - 👎 매우 짧음(~30s) / 시점·실행 보장 안 됨 / UI 없음

  - Background Push Notifications: 서버가 원할때 푸시로 백그라운드 업데이트 진행, BGAppRefreshTask의 푸시버전


beginBackgroundTask (구형)
  - 👍 모든 버전 / 간단 / 즉시
  - 👎 수십 초 / 긴 작업 불가 / UI 없음 → continued가 사실상 현대적 대체재

  Background URLSession
  - 👍 앱이 종료돼도 전송 지속, 시스템이 재시도/관리
  - 👎 순수 다운/업로드 전용 — 임의 연산은 못 함

 

 

플로우

  1. info.plist정의 - UIBackgroundModes키 [processing] BGTaskSchedulerPermittedIdentifiers: [식별자 identifier]
    Permitted background task scheduler identifiers 배열에 새 값을 추가하고 앱 번들 ID로 시작되도록 합니다.
    정적인 식별자 외에도 동적 접미사를 지원하는 와일드카드 형식을 지원합니다
  2. BGTaskScheduler.shared.register 로 id, 큐, 작업 등록
  3. BGContinuedProcessingTaskRequest 생성 후 BGTaskScheduler.shared.submit 제출하면 작업시작
    진행 속도가 예상보다 느릴 경우 시스템은 사용자에게 작업을 계속할지 묻게 됩니다
  4. 필요한 작업 마지막에 setTaskCompleted로 작업완료 알림

 

 

키워드

 

BGContinuedProcessingTaskRequest

init - 제출요청 id, 시스템 UI에 최초노출될 타이틀 지정

 

request.strategy

실행시 전략으로 2가지가 있습니다

  • queue(기본): 본적으로 지속적 처리 작업을 즉시 실행할 수 없을 경우 시스템이 queue에 작업을 추가합니다
    즉시 실행 불가시 큐 사용해서 가능할때까지 대기합니다
  • fail: 해당 작업이 당장 시작되어야만 유용할때.
    작업을 queue에 넣는 대신 즉시 실행할 수 없으면 제출을 실패하게 할 수 있습니다
    이를 통해 앱은 즉각적인 피드백을 받고 상황을 적절히 처리할 수 있습니다

 

 

BGContinuedProcessingTask

.progress.totalUnitCount

현재 진행 상황에 대한 추적 대상 작업 단위의 총 수

 

.progress.completedUnitCount

시스템UI, Live Activity, Dynamic Island용 진행률 갱신

 

.updateTitle(, subtitle: )

시스템UI, Live Activity, Dynamic Island용 제목 갱신

 

.expirationHandler

작업의 백그라운드 시간이 만료되기 직전에 호출되는 핸들러입니다.

정리/중단 된경우 처리

 

.setTaskCompleted(success: )

작업종료를 시스템에 알림(성공/실패)

 

예시코드

// 등록코드 
nonisolated func registerHandler() {
    BGTaskScheduler.shared.register(
        forTaskWithIdentifier: Self.taskIdentifier,
        using: Self.workQueue
    ) { [self] task in
        guard let task = task as? BGContinuedProcessingTask else {
            task.setTaskCompleted(success: false)
            return
        }
        // workQueue 위에서 동기적으로 작업을 수행한다.
        performWork(task)
    }
}

// 사용자가 시작버튼 (Ex: 업로드 버튼)

func start() {
    // 최초 시스템 UI 노출 타이틀지정
    let request = BGContinuedProcessingTaskRequest(
        identifier: Self.taskIdentifier,
        title: "긴 작업 진행 중",
        subtitle: "백그라운드로 전환해도 계속 진행됩니다"
    )
    // 즉시 실행할 수 없으면 큐잉 (다른 옵션: .fail = 즉시 불가 시 제출 실패)
    request.strategy = .queue

    do {
        try BGTaskScheduler.shared.submit(request)
    } catch {
        statusMessage = "작업 제출 실패: \(error.localizedDescription)"
    }
}
// 작업코드
nonisolated private func performWork(_ task: BGContinuedProcessingTask) {
    let total: Int64 = 600

    // 진행률 보고는 BGContinuedProcessingTask 에서 필수.
    task.progress.totalUnitCount = total
    
    // 만료(시스템 회수/강제 종료) 시 루프를 중단시키기 위한 플래그.
    let isCancelled = Locked(false)
    task.expirationHandler = {
        isCancelled.set(true)
    }

    publishStatus("작업 시작")

    var step: Int64 = 0
    while step < total {
        if isCancelled.get() {
            task.setTaskCompleted(success: false)
            return
        }

        // 1초 단위의 더미 작업 시뮬레이션
        Thread.sleep(forTimeInterval: 1.0)
        step += 1

        // 시스템 UI(Live Activity / Dynamic Island)용 진행률 + 제목 갱신
        task.progress.completedUnitCount = step
        task.updateTitle("긴 작업 진행 중", subtitle: "\(step)/\(total)초 완료")
        
    }
    task.setTaskCompleted(success: true)

    // 로컬 푸시 알림
    sendCompletionNotification()
}

 

스크린샷

반응형