UNUserNotificationCenter `requestAuthorization`에서 발생하는 희귀한 버그 현상 분석
사내에 2021년에 작성된 코드에서 앱이 멈추는 상황이 발생해서 사례 분석
앱 시작 직후가 아닌, 사용자에게 별도 페이지에서 안내 후 버튼 클릭을 통한 권한 요청의 동선
상세한 분석 링크
https://github.com/lgvv/unusernotificationcenter-requestauthorization-blocking-simulation
GitHub - lgvv/unusernotificationcenter-requestauthorization-blocking-simulation: Investigating Rare Bugs in UNUserNotificationCe
Investigating Rare Bugs in UNUserNotificationCenter’s RequestAuthorization on iOS - lgvv/unusernotificationcenter-requestauthorization-blocking-simulation
github.com
정상 동작 흐름
thread : main
- 사용자가 버튼을 클릭하여 권한 요청 로직을 트리거
- Apple iOS 시스템 Public API 호출
- 메인 스레드에서 DispatchSemaphore.wait()가 호출되어 동기 대기
- 시스템 API의 completion handler에서 콜백
- 콜백 내부에서 semaphore.signal()을 호출하여 대기 해제
- 이후 코드 실행이 재개되어 앱 진입 로직이 정상적으로 수행
문제 발생 흐름
thread : main
- 사용자가 버튼을 클릭하여 권한 요청 로직을 트리거. ✅
- Apple iOS 시스템 Public API 호출 ✅
- 메인 스레드에서 DispatchSemaphore.wait()가 호출되어 동기 대기 ✅
- 시스템 API 콜백(completion handler)이 호출되지 않음. ❌ 응답 없는 지점.
- semaphore.signal()이 실행되지 않아 대기 상태가 해제되지 않음. ❌
- 이후 코드 실행이 재개되지 않고, 앱이 해당 지점에서 멈춤 ❌
오류 상황 발생시 실험
클로저가 백그라운드에서 실행될 수 있다고 공식문서에 적혀 있음.
> 내부적으로 스레드 전환 가능성 있음.
클로저 기반의 코드를 확인
> 콜백이 오지 않음
private func calling() {
// thread: main
let semaphore = DispatchSemaphore(value: 0)
let center = UNUserNotificationCenter.current()
center.requestAuthorization(options: [.criticalAlert]) { _, _ in
semaphore.signal() // ❗️ Missing callback -> MainThread Block
}
semaphore.wait()
// ...
}
애플 공식 문서를 참고하여 Swift Concurrency 코드를 확인
> await 지점을 즉시 지나감.
private func calling2() {
Task { @MainActor in
// thread: main
let center = UNUserNotificationCenter.current()
do {
if try await center.requestAuthorization() == true {
// Authorized
} else {
// Not authorized
}
} catch {
// Handle error
}
}
// ❗️ The await does not suspend; execution continues immediately
}
해결
해당 현상이 발생할 경우 재부팅 전까지 requsetAuthorization에서 callback이 영구적으로 오지 않음.
재부팅 후에는 정상화
조치
애플 기술 문의
> 기술 문의는 영어로 해야하고, 샘플 프로젝트와 자세한 설명 등이 필요
애플 기술 문의 답변
> Xcode 26.1 베타 설치해보고 해당 버전에서도 문제가 발생하면 알려달라는 회신
유의할 점
Xcode 26에서 Default isolation MainActor로 변경할 때 시스템 API의 응답이 없을 수도 있어서 메인스레드를 잠그는 코드가 없는지 체크
'project > 개발 업무' 카테고리의 다른 글
| l-value, r-value (0) | 2025.10.23 |
|---|---|
| Actor에서 Class + OSAllocatedUnfairLock (2) | 2025.08.06 |
| Swift Concurrency Task weak self 실험 정리 (0) | 2025.07.29 |
| Tuist CocoaPod 연동 (0) | 2025.07.05 |
| (Concurrency, Combine) 전역 이벤트 관리 (1) | 2025.05.31 |