DEBUGGING
12iOS 17에서만 죽는 SwiftData 크래시 — unmanaged @Model의 함정
ModelContext 밖에서 init한 @Model의 합성 getter가 iOS 17 런타임에서 SIGABRT — 운반은 순수 struct로 바꿨다.
Swift ClosedRange<Date> 한 줄 때문에 한 달간 헤맨 이야기
Swift ClosedRange<Date>의 precondition trap을 따라가다 보니, 진짜 범인은 캘린더 데이터 매핑에서 가드 변수를 안 쓴 한 줄이었다. 데이터·표현 두 레이어로 막은 회고.
실기기에서만 깨진 콜드스타트 푸시 race condition 디버깅 기록
시뮬레이터에서는 멀쩡했지만 실기기 콜드스타트 푸시에서만 상세 화면이 열리지 않던 문제를 네 단계로 추적한 기록.
Coordinator re-attach 때 생기는 deinit race window 막기
UIKit Coordinator를 재부착하는 순간 old coordinator deinit과 외부 push/deeplink 이벤트가 겹치며 생기는 race window를 막는 방법.
iOS 푸시 알림 중복 진입을 막는 2층 dedupe 패턴
같은 iOS 푸시가 notification tap, cold launch, pending route 경로로 중복 처리되는 문제를 fingerprint dedupe와 envelope id 소비 추적으로 막은 사례.
@MainActor를 클래스에 통째로 붙이면 생기는 fetch 함정
UIKit 때문에 클래스 전체에 @MainActor를 붙였다가 async fetch까지 메인 액터에 묶인 사례. Swift Concurrency에서 UI 빌드와 데이터 fetch 격리를 나누는 방법.
GCD 시리얼 큐로 로그 라이터를 재작성한 이유 — actor + AsyncStream을 버린 결정
actor는 mutual exclusion을 보장하지만 thread identity는 보장하지 않는다. Realm/SwiftData 같은 thread-confined 자원에는 GCD serial queue가 답인 이유.
SwiftData #Predicate에서 enum 비교가 0건을 반환하는 이유
SwiftData #Predicate가 사용자 정의 enum을 만나면 throw 없이 0건을 반환한다. SQL 번역 실패가 침묵으로 드러나는 함정. raw value 컬럼, Swift filter, PK 인코딩 세 가지 우회법.