APNs가 HTTP/2 200 OK를 준다. 기기의 apsd도 push를 받는다. 그런데 Live Activity가 기동되지 않는다. 서버 로그만 보면 성공이라 어디를 봐야 할지도 모른다.
push-to-start에서 같은 증상을 두 번 겪었고, 원인은 서로 달랐다. 둘 다 페이로드 구조 문제였다.
먼저: APNs 200은 아무것도 보장하지 않는다
200 OK는 “APNs가 페이로드를 접수했다”는 뜻이지 “기기가 이걸 처리했다”는 뜻이 아니다. 페이로드를 실제로 해석하는 건 기기의 liveactivitiesd 데몬이고, 얘가 거부하면 그 사실은 기기 로그에만 남는다.
그래서 push-to-start를 디버깅할 때는 Xcode Console에서 liveactivitiesd 프로세스 로그를 반드시 같이 본다. 거부됐다면 이런 줄이 뜬다.
Incoming message does not contain usable attributes Incoming message could not be parsed
이 두 줄이 이 글의 나머지 전부에 대한 공통 증상이다. 메시지가 똑같아서 원인 구분이 안 된다는 게 함정이다.
함정 ①: attributes를 aps 밖에 뒀다
attributes와 attributes-type 키를 페이로드 최상위에 두고 있었다. 어디선가 그렇게 본 것 같아서 그렇게 했던 건데, ActivityKit 규격상 이 키들은 반드시 aps 딕셔너리 안에 중첩돼야 한다.
// ❌ 거부됨
{
"aps": { "timestamp": ..., "event": "start", "content-state": {...} },
"attributes-type": "IVENTActivityAttributes",
"attributes": {...}
}
// ✅ 정상
{
"aps": {
"timestamp": ..., "event": "start",
"content-state": {...},
"attributes-type": "IVENTActivityAttributes",
"attributes": {...}
}
}
함정 ②: attributes에 Swift 타입에 없는 키가 있었다
위치를 고쳤는데도 같은 로그가 났다. 이번 원인이 더 고약하다.
알림 제목과 본문을 함께 실어 보내려고 attributes dict에 alert_title, alert_body를 끼워 넣고 있었다. Swift Codable은 모르는 키를 그냥 무시하니까 안전하다고 생각했다.
liveactivitiesd의 내부 디코더는 표준 SwiftCodable과 다르게 동작한다. 알 수 없는 키가 하나라도 있으면 메시지 전체를 거부한다.
그래서 규칙은 이렇게 된다.
attributesdict에는ActivityAttributes구조체의 stored property만 넣는다. 하나도 더도 말고.- 알림 문구는
aps.alert가 원래 자리다. 거기로 옮긴다.
고칠 때 주의할 점 하나. 우리 코드베이스에서 push-to-start 페이로드를 만드는 자리가 네 군데였다 — 실제 발송 경로, 발송 실패 재시도 큐, 어드민 테스트 콘솔, 그리고 APNs 클라이언트 자체. 한 군데만 고치면 나머지에서 같은 증상이 계속 난다. 페이로드 조립을 한 함수로 모으고 파라미터로 분리하는 게 맞다.
함정 ③: 리브랜딩하면서 타입 이름 대소문자가 어긋났다
같은 계열의 세 번째 사고. 앱 이름을 바꾸면서 attributes-type 문자열의 대소문자가 Swift 타입명과 어긋났다. 이것도 증상은 똑같이 “200 OK인데 안 뜸”이다. attributes-type은 Swift 타입 이름과 정확히 일치하는 문자열이어야 한다.
체크리스트
push-to-start가 200인데 안 뜬다면, 이 순서로 본다.
- 기기 로그에서
liveactivitiesd를 필터링한다. 서버 로그는 여기서 쓸모가 없다. attributes/attributes-type이aps안에 있는가.attributes-type문자열이 Swift 타입명과 대소문자까지 같은가.attributes에 구조체에 없는 키가 섞여 있지 않은가. 특히 나중에 “편의상” 얹은 필드.- 페이로드를 만드는 자리가 몇 군데인지 세어본다.
세 번 다 같은 로그 두 줄을 봤다. 원인을 구분해주지 않으니 체크리스트로 훑는 수밖에 없다.
이 글에서 다룬 서비스
아이벤트
아이의 등하교와 학원 일정을 한 화면에 모아주는 iOS 앱입니다. 종이 알림장은 사진 한 장으로 정리되고, 등하원 시간이 다가오면 잠금화면에 실시간 카운트다운이 뜹니다. 완전 무료입니다.