project/개발 업무

UNUserNotificationCenter `requestAuthorization`에서 발생하는 희귀한 버그 현상 분석

lgvv 2025. 10. 27. 23:03

UNUserNotificationCenter `requestAuthorization`에서 발생하는 희귀한 버그 현상 분석

 

예전에 작성된 코드에서 앱이 멈추는 상황이 발생해서 사례 분석

 

상세한 분석 링크

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

  1. 사용자가 버튼을 클릭하여 권한 요청 로직을 트리거
  2. Apple iOS 시스템 Public API 호출
  3. 메인 스레드에서 DispatchSemaphore.wait()가 호출되어 동기 대기
  4. 시스템 API의 completion handler에서 콜백
  5. 콜백 내부에서 semaphore.signal()을 호출하여 대기 해제
  6. 이후 코드 실행이 재개되어 앱 진입 로직이 정상적으로 수행

 


문제 발생 흐름

 

thread : main

  1. 사용자가 버튼을 클릭하여 권한 요청 로직을 트리거. ✅
  2. Apple iOS 시스템 Public API 호출 ✅
  3. 메인 스레드에서 DispatchSemaphore.wait()가 호출되어 동기 대기 ✅
  4. 시스템 API 콜백(completion handler)이 호출되지 않음. ❌ 응답 없는 지점.
  5. semaphore.signal()이 실행되지 않아 대기 상태가 해제되지 않음. ❌
  6. 이후 코드 실행이 재개되지 않고, 앱이 해당 지점에서 멈춤 ❌

 

 

오류 상황 발생시 실험

클로저가 백그라운드에서 실행될 수 있다고 공식문서에 적혀 있음.

> 내부적으로 스레드 전환 가능성 있음.


클로저 기반의 코드를 확인

> 콜백이 오지 않음

 

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