#iOS.
글
/ 총 19편App Store 심사 제출을 다시 실행해도 안전한 판정 규칙
서버는 심사 요청을 받았는데 응답만 잃어버리면, 자동화를 다시 실행했을 때 중복으로 거절돼요. DailySudoku의 iOS 제출 자동화에서 버전 상태와 제출 객체 상태를 따로 읽어 재실행을 판정하게 고친 과정을 기록해요.
UMP의 사전 안내와 ATT 요청 순서를 바로잡은 기록
DailySudoku는 앱이 직접 ATT를 요청하는데, Google UMP도 사전 안내 뒤에 ATT를 요청할 수 있어요. SDK 바이너리에서 ATT 호출 흔적을 찾고, 두 요청이 겹치지 않도록 호출 순서를 바꾼 과정이에요.
수출 규정 신고값을 두 파일과 ASC에서 맞추는 검사
암호화 수출 규정 신고값은 Info.plist와 제출 선언 파일 두 곳에 있고, App Store Connect에도 남아요. 세 값이 어긋났는데 성공으로 판정하는 경로와, 그것을 막으려다 재시도를 전부 차단한 회귀를 고친 과정을 기록해요.
병렬 테스트가 전역 알림으로 서로를 건드린 사례
로컬에서는 0.02초에 끝나던 광고 배너 테스트가 CI에서는 45분 제한에 걸렸어요. 러너의 부하를 의심했지만 원인은 병렬로 도는 다른 테스트가 보낸 전역 알림이었어요. 재현 실험으로 가설을 확인하고 NotificationCenter를 주입해 분리한 과정을 기록해요.
리뷰 요청 예약에서 취소된 Task가 끝까지 실행된 경로
승리한 뒤 2초 기다렸다가 스토어 리뷰를 요청하는 예약이, 화면을 벗어나 취소한 뒤에도 실행될 수 있었어요. Task.sleep이 정상 반환한 뒤의 취소는 오류로 돌아오지 않기 때문이에요. 취소 동작을 Swift 6.4에서 다시 확인하고 고친 과정을 기록해요.
Apple Pencil 필기를 스도쿠 숫자로 바꾸는 입력 처리
DailySudoku에서 펜으로 쓴 숫자를 셀에 넣으려면 획을 tap과 필기로 나누고, 여러 획을 묶고, 모델 입력으로 바꾸고, 믿을 수 있을 때만 적용해야 해요. 각 단계의 기준값과 아직 검증하지 못한 부분을 기록해요.
스도쿠 81칸을 VoiceOver와 TalkBack에 알리는 방법
DailySudoku 게임판은 화면에서는 뷰 하나지만 보조 기술에는 81개 칸을 따로 알려야 해요. iOS와 Android에서 칸의 위치·내용·선택 동작을 맞춘 방식과, 아직 실기기로 확인하지 못한 부분을 기록해요.
광고 노출 이벤트를 세 플랫폼에서 같은 뜻으로 세는 규약
iOS의 로딩 완료, Android의 impression 통지, Web의 DOM 부착을 모두 ad_impression으로 보내면 플랫폼마다 세는 기준이 달라져요. 광고가 거치는 단계를 여섯으로 나누고, 어떤 신호를 노출 한 건으로 셀지 정한 규약이에요.
일일 퍼즐 점수의 날짜와 리더보드 전송 손실 다루기
23시 50분에 시작해 0시 5분에 끝낸 판에서는 퍼즐 날짜, 전송 시각, 리더보드의 조회 구간이 서로 어긋나요. Game Center와 Play Games 문서로 세 값의 차이를 확인하고, 점수 전송을 어디까지 보장할지 고르는 기준을 정리해요.
일일 퍼즐 ID에 생성기 버전이 필요한 이유
DailySudoku의 일일 퍼즐은 날짜에서 seed를 만들어 보드를 생성해요. 생성 방식이 바뀌면 같은 날짜에서 다른 보드가 나올 수 있는데, 날짜만 담은 ID로는 그 차이를 적을 수 없어요. 현재 구조와, ID에 날짜 정책과 생성기 버전을 넣자는 제안을 기록해요.
위치 권한 없이 GeoIP 권역으로 광고 공급자 순서 정하기
지역마다 쓸 수 있는 광고 공급자가 다를 때 필요한 것은 좌표가 아니라 넓은 권역이에요. 동의를 먼저 확인하고, 서버가 IP로 추정한 권역 버킷만 받아 공급자 순서를 정하는 설계와 그 한계를 정리해요.
광고 제거 일회성 구매의 권한을 유지하고 되돌리는 방법
광고 제거 상품은 한 번 사면 끝나는 것처럼 보이지만, 재설치, 대기 중인 결제, 환불을 거치면 구매 직후의 결과만으로는 보유 여부를 알 수 없어요. StoreKit 2와 Play Billing에서 현재 권한을 다시 읽어 캐시를 맞추는 구현과 남은 공백을 기록해요.
Rust 공유 코어 4편: iOS에 XCFramework로 전달하기
Rust 테스트가 통과해도 iOS 앱에서 링크가 된다는 보장은 없어요. 기기용과 시뮬레이터용 정적 라이브러리를 XCFramework로 묶고, Swift Package로 전달하고, 로컬과 CI가 같은 순서로 빌드하게 만드는 방법을 문서 기준으로 정리해요.
Rust 공유 코어 3편: UniFFI 생성 코드는 adapter 뒤에
UniFFI는 Rust의 함수와 타입에서 Swift와 Kotlin 호출 코드를 만들어 줘요. 무엇을 공개하고 생성된 타입을 어디까지 쓸지는 앱이 정해야 해요. 생성 코드를 adapter 뒤에 두는 구조와 플랫폼별로 챙길 것, 생성 파일의 관리 방식을 정리해요.
Rust 공유 코어 2편: 경계를 넘는 API를 크게 잡기
Rust 엔진을 Swift, Kotlin, TypeScript에서 부를 때에는 내부에 알맞은 타입과 바깥에서 다루기 쉬운 타입이 달라요. 셀 단위의 작은 함수보다 액션 단위의 큰 함수를 내놓고, DTO와 오류와 버전을 계약으로 관리하는 설계를 정리해요.
Rust 공유 코어 1편: 무엇을 공유하고 어디서 끊을지
iOS, Android, Web을 함께 만들면 같은 규칙을 세 번 구현하게 돼요. Rust로 공유할 범위를 결과가 같아야 하는 도메인 규칙으로 좁히고, 코어와 바인딩과 앱의 세 층으로 나누는 설계를 스도쿠를 예로 설명해요.
iOS에서 실행 중에 폰트를 등록할 때 정해야 하는 것
번들에 넣는 폰트는 Info.plist 선언으로 끝나지만, 내려받거나 패키지에 든 폰트는 실행 중에 등록하는 쪽이 더 맞을 수 있어요. CoreText로 파일을 등록할 때 PostScript name을 읽는 방법, 등록 상태를 한곳에서 관리하는 예제, 파일 수명에서 정할 것을 정리해요.
Swift Package Registry를 도입하기 전에 볼 것
Swift 의존성을 Git 주소가 아니라 registry의 ID로 받으면 받아 오는 경로가 달라져요. 요청 순서, 설정 파일, Git과 registry가 섞인 의존 그래프의 위험, mirror와의 차이, 인증과 서명을 정리하고, 팀이 도입을 판단할 때 볼 항목을 적어요.
WWDC26에서 iOS 개발자가 볼 다섯 가지 변화
Apple이 WWDC26에서 발표한 내용 가운데 iOS 개발자에게 닿는 것을 App Intents, 모델 실행, Xcode 27, 화면, 구독 운영으로 나눠 정리해요. 발표된 사실은 Apple의 공식 페이지에서 확인했고, 제 관점은 따로 적어요.