--- id: ios-background-tasks title: iOS Background Tasks — BGTaskScheduler / Refresh category: Coding status: draft source_trust_level: B verification_status: conceptual created_at: 2026-05-09 updated_at: 2026-05-09 tags: [ios, background, bgtask, vibe-coding] tech_stack: { language: "Swift / BackgroundTasks", applicable_to: ["iOS 13+"] } applied_in: [] aliases: [BGAppRefreshTask, BGProcessingTask, background fetch] --- # iOS Background Tasks > iOS 는 앱이 백그라운드에서 자유롭게 안 돌게 한다. **BGTaskScheduler** 로 OS 가 적절한 시점에 깨워줌. 짧은 refresh (30s) vs 긴 processing (분 단위, charging 시점) 구분. ## 📖 핵심 개념 - BGAppRefreshTask: 짧은 작업 (~30s). 사용자 패턴 학습 후 OS 가 호출. - BGProcessingTask: 긴 작업, 충전/네트워크 조건 가능. 주로 야간. - 둘 다 OS 결정 — 정확한 시각 보장 X. ## 💻 코드 패턴 ### Info.plist 등록 ```xml BGTaskSchedulerPermittedIdentifiers com.example.app.refresh com.example.app.cleanup ``` ### App init 에서 register ```swift @main struct App: SwiftUI.App { init() { BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.example.app.refresh", using: nil) { task in handleRefresh(task as! BGAppRefreshTask) } BGTaskScheduler.shared.register(forTaskWithIdentifier: "com.example.app.cleanup", using: nil) { task in handleCleanup(task as! BGProcessingTask) } } } ``` ### 다음 실행 예약 ```swift func scheduleRefresh() { let req = BGAppRefreshTaskRequest(identifier: "com.example.app.refresh") req.earliestBeginDate = Date(timeIntervalSinceNow: 15 * 60) // 최소 15분 후 do { try BGTaskScheduler.shared.submit(req) } catch { print("schedule failed: \(error)") } } func scheduleCleanup() { let req = BGProcessingTaskRequest(identifier: "com.example.app.cleanup") req.requiresNetworkConnectivity = true req.requiresExternalPower = true req.earliestBeginDate = Date(timeIntervalSinceNow: 4 * 60 * 60) try? BGTaskScheduler.shared.submit(req) } // app didEnterBackground 에서 reschedule func sceneDidEnterBackground(_ scene: UIScene) { scheduleRefresh() } ``` ### 작업 실행 ```swift func handleRefresh(_ task: BGAppRefreshTask) { scheduleRefresh() // 다음 예약 let op = SyncOperation() task.expirationHandler = { op.cancel() } // OS 가 시간 끝났다 알리면 cancel op.completionBlock = { task.setTaskCompleted(success: !op.isCancelled) } OperationQueue().addOperation(op) } ``` ### 디버그 — Xcode breakpoint ``` e -l objc -- (void)[[BGTaskScheduler sharedScheduler] _simulateLaunchForTaskWithIdentifier:@"com.example.app.refresh"] ``` ## 🤔 의사결정 기준 | 작업 | 도구 | |---|---| | 30초 이내 데이터 sync | BGAppRefreshTask | | 큰 다운로드 / DB cleanup | BGProcessingTask | | 위치 변화 trigger | Significant location changes | | 정확한 시각 | UNNotificationRequest (local) — 단 OS 표시만, 코드 실행 X | | 음악 / 통화 / 위치 추적 | Background mode capability + 별도 | | Push 로 깨우기 | Silent push (content-available) | ## ❌ 안티패턴 - **시간 보장 가정**: OS 가 마음대로. 며칠 못 깨울 수도. - **expirationHandler 안 처리**: 시간 초과 시 강제 종료 + suspend. 다음 등록 어려움. - **register 와 submit 혼동**: register 는 한 번 (init), submit 은 매번. - **백그라운드에서 UI 업데이트**: setNeedsDisplay 의미 없음. - **무한 task**: OS 가 throttle. 다음 호출 거의 안 옴. - **테스트 안 함**: simulator 에선 trigger 어려움. lldb 명령으로 강제 실행. - **Info.plist 등록 안 함**: register 가 silently 실패. ## 🤖 LLM 활용 힌트 - "BGTaskScheduler 는 hint, not guarantee" 강조. - 모든 task 가 schedule + register + execute 3단계 페어. ## 🔗 관련 문서 - [[iOS_Push_Notifications]] - [[Android_WorkManager_Patterns]]