팀에서 할 일 개수를 메인 앱에 저장했는데도 원격 시뮬레이터의 위젯에는 이전 값이 표시됩니다. 이때 새로고침 API를 반복해서 호출하면 대개 조사할 항목만 늘어납니다. 공유 데이터 쓰기, 확장 프로그램의 데이터 읽기, 타임라인 생성, 시스템의 화면 표시 일정은 서로 다른 단계입니다. 할 일 개수를 표시하는 WidgetKit 위젯을 예로 들어 각 단계를 독립적으로 검증하는 방법을 살펴보겠습니다.
데이터 전달 경계부터 명확히 하기
메인 앱과 위젯 확장 프로그램은 서로 다른 프로세스입니다. 일반적인 UserDefaults.standard가 자동으로 양쪽의 공용 데이터 저장소가 되지는 않습니다. 먼저 프로젝트의 두 target에 동일한 App Group을 활성화하고, 양쪽에서 읽을 수 있는 프로젝트 설정에 그룹 식별자를 넣으세요. 테스트용 식별자를 배포 설정에 하드코딩해서는 안 됩니다.
메인 앱에서 비즈니스 데이터를 쓴 뒤 UserDefaults(suiteName:)에 개수와 갱신 시각을 저장합니다. 확장 프로그램은 같은 suite 이름으로 읽습니다. 쓰기 함수는 이전 값을 조용히 사용하는 대신 성공 또는 오류를 반환해야 합니다. 확장 프로그램에서 읽기에 실패하면 명확한 기본 상태를 표시합니다. 데이터가 정수 하나보다 복잡하다면 버전 번호가 포함된 단일 레코드로 인코딩해, 개수는 갱신됐지만 제목은 이전 값인 식의 필드 간 불일치를 피하세요.
새로고침 요청은 시스템에 “데이터가 바뀌었을 수 있다”고 알릴 뿐입니다. 메인 앱의 데이터를 확장 프로그램으로 옮겨 주지도, 위젯이 즉시 다시 그려진다고 보장하지도 않습니다.
먼저 메인 앱에서 쓴 값을 한 번 다시 읽고, 확장 프로그램의 읽기 지점에서 민감 정보를 제거한 버전 번호를 기록하세요. 양쪽 버전이 다르면 데이터 전달을 확인해야 합니다. 버전은 같은데 화면이 다를 때 비로소 타임라인과 표시 단계를 살펴보세요. 작업 내용, 계정 정보 또는 공유 레코드 전체를 진단 로그에 남기지 마세요.
타임라인 계산을 순수 함수로 만들기
타임라인 항목의 내용이 테스트 실행 시점의 실제 시계에 좌우되어서는 안 됩니다. 프로바이더는 데이터를 읽고 계산 함수를 호출하는 역할만 맡고, 계산 함수는 현재 시각과 읽어 온 스냅샷을 입력받게 하세요. 다음 코드는 최소한의 경계를 보여 줍니다. 타입은 메인 앱과 확장 프로그램이 공유하는 소스 파일에 둘 수 있습니다.
import Foundation
struct CountEntry: Equatable {
let date: Date
let count: Int
let revision: Int
}
func makeEntries(
now: Date,
count: Int,
revision: Int
) -> [CountEntry] {
[
CountEntry(date: now, count: count, revision: revision),
CountEntry(
date: now.addingTimeInterval(15 * 60),
count: count,
revision: revision
)
]
}
두 번째 항목에도 같은 스냅샷이 들어갑니다. 15분이 지났다는 이유만으로 할 일 개수가 바뀔 것이라고 추정해서는 안 됩니다. 프로바이더가 항목을 만든 뒤에는 마지막 항목보다 늦은 시점을 타임라인의 재요청 정책으로 지정할 수 있습니다. 이 정책은 확인을 희망하는 시점이지, 화면이 몇 초 안에 갱신된다는 약속이 아닙니다. 새 비즈니스 데이터가 들어오면 해당 종류의 위젯에 다시 로드하도록 요청하세요. 제품에 정확한 카운트다운 표시가 필요하다면, 촘촘한 타임라인 항목으로 실시간 프로세스를 흉내 내기보다 요구사항을 다시 검토해야 합니다.
입력을 고정하고 제어 가능한 결과 검증하기
단위 테스트에서는 고정된 Date, 개수, 수정 번호를 입력해 항목이 정확히 두 개 생성되는지, 날짜가 엄격히 증가하는지, 두 항목의 개수와 수정 번호가 같은지 검증합니다. 개수가 0인 경계 조건도 테스트하세요. 0은 실제 값으로 렌더링되어야 하며, 읽기 실패 시 사용하는 기본값에 가려져서는 안 됩니다. 이 테스트는 시뮬레이터를 시작하거나 시스템의 실행 일정을 기다리지 않으므로, 실패하면 계산 로직을 바로 확인할 수 있습니다.
클라우드 시뮬레이터에서 실제 데이터 전달 검증하기
먼저 xcrun simctl list devices available로 사용 가능한 iOS 시뮬레이터와 기기 ID를 찾습니다. 빌드와 테스트에 사용할 ID를 고정해, 실행 환경이 바뀐 뒤 기기 이름으로 다른 런타임을 선택하는 일을 피하세요.
xcodebuild test \
-project Widgets.xcodeproj \
-scheme Widgets \
-destination "platform=iOS Simulator,id=$SIM_ID" \
-resultBundlePath "$PWD/widget-tests.xcresult"
여기서 프로젝트 이름과 scheme은 예시입니다. 실행 전에 저장소에 실제로 존재하는 이름으로 바꾸세요. SIM_ID는 작업 설정에서 명시적으로 전달해야 합니다. 기존 .xcresult 때문에 명령이 일찍 실패하지 않도록 실행할 때마다 새 결과 디렉터리를 사용하세요. 타임라인 단위 테스트가 통과하면 메인 앱과 확장 프로그램이 포함된 빌드를 설치하고 시뮬레이터에 위젯을 추가합니다. 먼저 기본 상태를 확인한 뒤, 메인 앱에서 새 수정 번호의 데이터를 쓰고 새로고침을 요청합니다. 마지막으로 확장 프로그램의 읽기 기록과 화면을 대조하세요.
화면 검증에서는 최종 내용과 수정 번호만 확인하고, “요청 후 몇 초 이내에 표시될 것”을 필수 조건으로 단언하지 마세요. 시스템은 새로고침 시점을 조정할 수 있습니다. 즉시 표시되는지 정확하게 검증해야 한다면 시스템 스케줄링을 앱의 타이머로 취급하지 말고, 메인 앱 안에서 제어할 수 있는 미리보기 화면을 테스트하세요.
반복 설치 대신 증상에 따라 원인 찾기
| 관찰 결과 | 우선 확인할 항목 |
|---|---|
| 메인 앱에서 다시 읽은 값부터 잘못됨 | 쓰기 시점, suite 이름, 오류 처리 |
| 메인 앱의 값은 맞지만 확장 프로그램은 이전 값을 읽음 | 두 target의 App Group 설정과 레코드 버전 |
| 확장 프로그램은 새 값을 읽었지만 항목은 이전 값임 | 프로바이더에 전달한 인자와 타임라인 생성 |
| 새 항목은 맞지만 화면은 이전 값임 | 위젯 종류, 뷰가 참조하는 값, 시스템의 실제 표시 시점 |
조사 전에 프로젝트 커밋, 런타임 버전, 기기 ID, 공유 레코드의 수정 번호를 기록하세요. “화면이 바뀌지 않았다”는 스크린샷 한 장보다 문제를 파악하는 데 유용합니다. 실패할 때마다 시뮬레이터 전체를 바로 지우지 마세요. 재현에 필요한 공유 데이터까지 사라집니다. 초기화가 필요하다면 테스트 결과와 필요한 비식별 기록을 먼저 보존한 다음, 앱 데이터 삭제와 시뮬레이터 재생성 중 무엇을 할지 명확히 선택하세요.
검증 조건을 파이프라인에 반영하기
자동화 작업은 두 단계로 나눕니다. 첫 단계에서는 순수 함수 테스트를 실행해 항목 순서, 0 값, 수정 번호의 회귀를 차단합니다. 두 번째 단계에서는 고정된 시뮬레이터에서 앱과 확장 프로그램이 모두 설치됐는지, 양쪽이 같은 레코드를 읽는지 확인하고, 레이아웃은 수동으로 또는 제어 가능한 미리보기를 통해 검증합니다. 위젯의 실제 새로고침은 별도 관찰 항목으로 두고, 첫 단계의 결정적인 검증과 섞지 않습니다.
마지막으로 세 가지를 확인하세요. 두 target이 같은 공유 그룹을 가리키는지, 새 데이터에 내용을 노출하지 않으면서 추적할 수 있는 수정 번호가 있는지, 테스트가 실제로 몇 분이 지나기를 기다리는 대신 시간을 입력값으로 받는지입니다. 이 조건이 충족되면 다음에 이전 화면이 나타나더라도 새로고침을 반복 요청하며 원인을 추측할 필요 없이 데이터, 항목, 표시 순서로 문제를 좁혀 갈 수 있습니다.
자주 묻는 질문
새로고침을 요청했는데 위젯이 바로 바뀌지 않는 이유는 무엇인가요?
새로고침 요청은 즉시 실행을 보장하지 않습니다. 먼저 공유 컨테이너의 값과 제공자의 읽기 결과를 확인하고, 화면 갱신은 시스템이 생성한 타임라인을 기준으로 판단하세요.
타임라인 테스트에서 시뮬레이터의 갱신을 기다려야 하나요?
아니요. 시간과 데이터를 순수 함수에 주입해 항목 순서와 내용을 단위 테스트하고, 시뮬레이터에서는 설치와 데이터 전달 및 최종 표시만 확인하면 됩니다.
고정 구성의 클라우드 Mac을 워크플로에 연결하세요
일·주·월·분기 단위로 전용 물리 노드를 임대하고, 고정된 하드웨어 리소스로 빌드, 자동화 및 MLX 추론 작업을 실행하세요.