4.0 KiB
4.0 KiB
id, title, category, status, source_trust_level, verification_status, created_at, updated_at, tags, tech_stack, applied_in, aliases
| id | title | category | status | source_trust_level | verification_status | created_at | updated_at | tags | tech_stack | applied_in | aliases | ||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| ios-background-tasks | iOS Background Tasks — BGTaskScheduler / Refresh | Coding | draft | B | conceptual | 2026-05-09 | 2026-05-09 |
|
|
|
iOS Background Tasks
iOS 는 앱이 백그라운드에서 자유롭게 안 돌게 한다. BGTaskScheduler 로 OS 가 적절한 시점에 깨워줌. 짧은 refresh (30s) vs 긴 processing (분 단위, charging 시점) 구분.
📖 핵심 개념
- BGAppRefreshTask: 짧은 작업 (~30s). 사용자 패턴 학습 후 OS 가 호출.
- BGProcessingTask: 긴 작업, 충전/네트워크 조건 가능. 주로 야간.
- 둘 다 OS 결정 — 정확한 시각 보장 X.
💻 코드 패턴
Info.plist 등록
<key>BGTaskSchedulerPermittedIdentifiers</key>
<array>
<string>com.example.app.refresh</string>
<string>com.example.app.cleanup</string>
</array>
App init 에서 register
@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)
}
}
}
다음 실행 예약
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()
}
작업 실행
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단계 페어.