강프로의 실험실

직접 만들고 운영하면서 남기는 개발 기록

LiveActivity가 떴다가 바로 사라진다 — Widget Extension의 GeometryReader 크래시

·

Activity.request()는 성공한다. 반환된 activity의 상태는 active고, 시스템에 등록된 Activity 개수도 1이다. 그런데 잠금화면에도 Dynamic Island에도 아무것도 안 뜬다. 몇 초 뒤 조용히 dismissed로 바뀐다. Xcode 콘솔은 깨끗하다.

위젯 익스텐션이 크래시하고 있었고, 범인은 GeometryReader 한 줄이었다.

왜 콘솔이 깨끗한가

이 문제가 오래 걸린 이유가 여기 있다. 위젯 익스텐션 크래시는 메인 앱의 Xcode 콘솔에 나타나지 않는다. 별도 프로세스이기 때문이다. 앱 입장에서는 API 호출이 전부 성공했으므로 잘못됐다는 신호가 어디에도 없다.

실제로 무슨 일이 벌어지는지는 두 군데를 봐야 보인다.

xcrun simctl spawn <UDID> log show \
  --predicate 'process == "WidgetRenderer_Activities"'

여기서 이 두 줄이 반복되고 있으면 확정이다.

Archive was nil. LiveActivity will be empty
Couldn't find a placeholder to show

그리고 ~/Library/Logs/DiagnosticReports/에 위젯 익스텐션 이름으로 된 .ips 크래시 리포트가 쌓여 있다. 스택은 이렇게 끝난다.

_assertionFailure
  → LayoutSubview.place(at:anchor:dimensions:)
  → GeometryReaderLayout.placeSubviews()

연쇄가 이렇게 이어진다

  1. 진행 바를 그리는 뷰에서 GeometryReader를 쓴다
  2. iOS 26 위젯 렌더링 컨텍스트에서 GeometryReaderLayout.placeSubviews()가 assertion failure를 낸다
  3. 위젯 익스텐션 프로세스가 죽는다
  4. 시스템이 렌더링 결과 아카이브를 만들지 못한다 (Archive was nil)
  5. 빈 Live Activity → 시스템이 자동으로 dismiss

앱 쪽에서 보이는 증상(“떴다가 사라진다”)과 실제 원인(“레이아웃 assertion”) 사이의 거리가 이렇게 멀어서, 증상만 보고는 절대 못 찾는다.

고치기

부모 폭을 읽어서 자식 폭을 계산하던 걸, scaleEffect로 비율만 주는 방식으로 바꿨다. 부모 크기를 알 필요가 없어진다.

// Before — 크래시
GeometryReader { geo in
    ZStack(alignment: .leading) {
        RoundedRectangle(cornerRadius: 2).fill(Color.white.opacity(0.12))
        RoundedRectangle(cornerRadius: 2).fill(color.opacity(0.8))
            .frame(width: geo.size.width * progress)
    }
}

// After — 정상
RoundedRectangle(cornerRadius: 2).fill(Color.white.opacity(0.12))
    .overlay(alignment: .leading) {
        RoundedRectangle(cornerRadius: 2).fill(color.opacity(0.8))
            .scaleEffect(x: progress, anchor: .leading)
    }
    .clipShape(RoundedRectangle(cornerRadius: 2))

나중에는 이것도 걷어내고 시스템 ProgressView로 갔다. 커스텀 진행 바는 진행률을 직접 계산해야 하는데, 그 계산이 또 다른 문제를 낳았기 때문이다 — 그 이야기는 카운트다운 네 가지를 다 써본 글에 적었다.

남기는 규칙

  • Live Activity 위젯 안에서 GeometryReader를 쓰지 않는다. 부모 크기가 필요해 보이면 overlay + scaleEffect, 아니면 시스템 컴포넌트로 돌아간다.
  • “LiveActivity가 떴다가 사라진다”는 곧 위젯 프로세스 크래시를 의심하라는 신호다. 앱 콘솔이 아니라 WidgetRenderer_Activities 시스템 로그와 DiagnosticReports를 본다.
  • 위젯은 앱과 다른 렌더링 컨텍스트에서 돈다. 앱 화면에서 멀쩡한 뷰 코드가 위젯에서 멀쩡하리라는 보장이 없다.

이 글에서 다룬 서비스

아이벤트

아이의 등하교와 학원 일정을 한 화면에 모아주는 iOS 앱입니다. 종이 알림장은 사진 한 장으로 정리되고, 등하원 시간이 다가오면 잠금화면에 실시간 카운트다운이 뜹니다. 완전 무료입니다.

프로젝트: