[SwiftUI 오류 수정] Failed to terminate process: Error Domain=com.apple.extensionKit.errorDomain Code=18 “(null)” UserInfo={NSUnderlyingError=0x14a6c02a0 {Error Domain=RBSRequestErrorDomain Code=3 “No such process found” UserInfo={NSLocalizedFailureReason=No such process found}}}
백그라운서비스을 개발해서 테스트 중인데, 아래와 같은 오류가 콘솔창에 찍히는데, 눈에 잘 띄지않아 그냥 지나쳤는데, 이게 내가 만든어플이 문제인지, 시스템에서 보내는건지 알 수없어서 방치했는데, 오늘 원격 푸시알림 구현하여 테스트 하는데, 잘 안되서 ChatGPT한테 질문을 던졌다.
Failed to terminate process: Error Domain=com.apple.extensionKit.errorDomain Code=18 "(null)" UserInfo={NSUnderlyingError=0x14a6c02a0 {Error Domain=RBSRequestErrorDomain Code=3 "No such process found" UserInfo={NSLocalizedFailureReason=No such process found}}}
위 오류를 먼저 주고 난후 AppDelegate.swift 파일을 주고, 혹시 저 오류가 관련이 있냐고 물었더니 AppDelegate는 연관이 없는데 그 안에서 호출하는 백그라운드 스케줄이 문제일 수 있다고 그러네?
백그라운드 서비스 등록 코드:
func registerBackgroundTask() {
//GProcessingTask (보다 긴 작업, 백그라운드 실행 보장)
BGTaskScheduler.shared.register(forTaskWithIdentifier: backgroundTaskIdentifier, using: nil) { task in
// 작업 실행 시 handleAppRefresh 호출
Log.d(DataSyncManager.TAG, "Background task \(self.backgroundTaskIdentifier) starting.")
self.handleAppRefresh(task: task as! BGAppRefreshTask)
}
Log.d(DataSyncManager.TAG, "Background task \(backgroundTaskIdentifier) registered.")
}
코드 내용을 보면 함수 이름은 handleAppRefresh
이지만, 실제로는 BGProcessingTaskRequest
를 사용하고 있고, 여기에 BGAppRefreshTask
를 강제로 캐스팅하고 있었다.
// MARK: - 백그라운드 작업 핸들러 (ModelContainer 직접 생성)
private func handleAppRefresh(task: BGAppRefreshTask) {
// 다음 작업 예약 (중요!)
scheduleAppRefresh()
// *** 백그라운드 작업용 ModelContainer 및 ModelContext 새로 생성 ***
let modelContainer: ModelContainer
do {
// 앱의 메인 스키마와 동일하게 설정
let schema = Schema([
StockHolding.self, StockPrice.self,
NotificationLog.self, SyncLog.self,
])
// 앱의 메인 설정과 동일한 이름 및 스키마 사용
let config = ModelConfiguration("FloorNotifierDB", schema: schema)
modelContainer = try ModelContainer(for: schema, configurations: [config])
Log.d(DataSyncManager.TAG, "handleAppRefresh: Successfully created ModelContainer for background task.")
} catch {
Log.e(DataSyncManager.TAG, "handleAppRefresh: Failed to create ModelContainer in background: \(error)")
task.setTaskCompleted(success: false) // 컨테이너 생성 실패 시 작업 실패 처리
return
}
let modelContext = ModelContext(modelContainer) // 새 컨텍스트 생성
// *** ModelContainer 생성 끝 ***
let queue = OperationQueue()
queue.maxConcurrentOperationCount = 1 // 동시 작업 수 제한
let operation = BlockOperation {
Task { // Swift Concurrency 사용
// *** 백그라운드 작업에서는 isInitialSync를 false로 호출 ***
await self.performDailySync(modelContext: modelContext, isInitialSync: false)
// await self.performDailySync(modelContext: modelContext) // 새 컨텍스트 전달
// 작업 완료 후에는 modelContext를 명시적으로 저장할 필요 없음 (SwiftData가 관리)
task.setTaskCompleted(success: true) // 작업 성공으로 완료
Log.d(DataSyncManager.TAG, "Background task \(self.backgroundTaskIdentifier) completed successfully.")
}
}
task.expirationHandler = {
operation.cancel() // 작업 만료 시 취소
Log.w(DataSyncManager.TAG, "Background task \(self.backgroundTaskIdentifier) expired.")
// 만료 시에도 setTaskCompleted를 호출해야 할 수 있음 (문서 확인 필요)
}
queue.addOperation(operation)
}
func scheduleAppRefresh() { //scheduleProcessingTask
let request = BGProcessingTaskRequest(identifier: backgroundTaskIdentifier)
request.requiresNetworkConnectivity = true // 네트워크 필요할 경우
request.requiresExternalPower = false // 전원 연결 필요 여부 requiresExternalPower = true (충전 중일 때 우선 처리)
request.earliestBeginDate = calculateNextWeekday12pm()
do {
try BGTaskScheduler.shared.submit(request)
print("BGProcessingTask scheduled")
} catch {
print("Failed to schedule: \(error)")
}
}
func scheduleAppRefreshDirect() {
let request = BGProcessingTaskRequest(identifier: backgroundTaskIdentifier)
request.requiresNetworkConnectivity = true
request.requiresExternalPower = false
// 조용한 푸시를 통해 스케줄링하는 것이므로, earliestBeginDate를 즉시 또는 짧은 시간 후로 설정
request.earliestBeginDate = Date(timeIntervalSinceNow: 5) // 예: 5초 후
do {
try BGTaskScheduler.shared.submit(request)
print("BGProcessingTask scheduled via silent push.")
} catch {
print("Failed to schedule BGProcessingTask via silent push: \(error)")
}
}
챗지피티에 의하면 위 오류는 시스템이 등록되지 않은 타입의 작업을 실행하려다 실패한 것이래.
원래는 BGAppRefreshTask를 사용했는데, 이건 인터넷 연결도 안되고, 많은 데이터 동기화에는 부적합하다고 하여 BGProcessingTask로 코드를 변경하고 테스트 중이었는데, 내가 바꾸지 않은 부분이 있었다.
📌 요약
항목 | 현재 코드 | 해결 방안 |
---|---|---|
등록 | BGAppRefreshTask | ❌ BGProcessingTaskRequest 와 불일치 |
제출 | BGProcessingTaskRequest | ✅ 등록도 이걸로 맞추거나 |
해결책 | 등록 코드에서 BGProcessingTask 로 캐스팅 | 또는 제출을 BGAppRefreshTaskRequest 로 변경 |
❗️이건 이름 문제가 아니라 타입 불일치 문제
함수 이름은 handleAppRefresh
일 수 있지만, 시스템이 호출하는 객체는 BGProcessingTask.
그런데 이걸 강제로 BGAppRefreshTask
로 캐스팅하니, 내부적으로 시스템은:
- “등록된 작업 타입과 제출된 작업 타입이 다르다”
- “처리할 수 있는 적절한 프로세스(혹은 Extension)를 찾을 수 없다”
라는 식으로 해석해서, 결국 오류를 반환
올바르게 수정
BGProcessingTaskRequest 계속 사용
ㅁ
self.handleAppRefresh(task: task as! BGAppRefreshTask)
private func handleAppRefresh(task: BGAppRefreshTask) {
TO-BE:
self.handleAppRefresh(task: task as! BGProcessingTask)
private func handleAppRefresh(task: BGProcessingTask) {
그래도 오류가 해결되지 않는 경우
어떤 경우라도 setTaskCompleted()
호출이 누락되면 시스템이 강제로 작업을 종료하면서 이런 오류를 던질 수 있습니다.
task.expirationHandler = {
operation.cancel()
task.setTaskCompleted(success: false) // 👈 꼭 호출
}
✅ 필수 설정 확인 결과
🔹 Permitted background task scheduler identifiers
- 포함된 값:
com.test.apprefresh
🔹 Required background modes
App processes data in the background
App downloads content from the network
App downloads content in response to push notifications
✅ BGProcessingTask에 필요한 옵션들 모두 설정됨
그럼에도 오류가 나는 이유는?
📌 가능한 원인 다시 정리 (설정 외)
원인 | 설명 |
---|---|
✅ task.setTaskCompleted(success:) 누락 | 작업 끝나고 호출 안 되면 시스템이 강제로 죽이려다 실패함 |
🧪 디버깅 중 | Xcode 디버깅 상태에서 Stop 눌렀거나 백그라운드 task가 중간에 만료됨 |
🧼 이미 종료된 task | task가 너무 빨리 끝나거나 죽어서 시스템에서 해당 프로세스를 못 찾는 경우 |
⏰ 작업 시간 초과 | 시스템이 작업을 강제로 종료하려다 실패할 수 있음 (expirationHandler 트리거) |