본문 바로가기
C.W.K.
Stream
Lesson 06 of 06 · published

빈 폴더에서 폰에 깔린 빌드까지

~18 min · proof-and-fleet, synthesis, capstone, testflight, workflow

Level 0번들 열어본 사람
0 XP0/81 lessons0/17 achievements
0/100 XP to next level100 XP to go0% complete
"애플이 예뻐서가 아니라 1년에 그 정도 투자는 할만해."

모든 단계를 지나는 길 하나

이 퀘스트는 Spark라는 작은 앱 가족 하나를 패키지부터 폰까지 따라왔어. 이 레슨에선 그 길 전체를 한곳에 모아. 조각들이 실제로 생겨나야 하는 순서대로 늘어놓고, 단계마다 근거가 되는 레슨을 함께 짚어. 핵심은 순서야. 앞 트랙들에서 본 함정 여러 개가, 진짜 순서가 아니라 직관적인 순서대로 밟은 단계에서 나왔거든.

공유 로직부터

Swift 6 언어 모드의 Swift 패키지로 시작해. 데이터 경쟁은 누군가의 폰에서 트랩으로 터지기 전에, 네 책상 앞에서 컴파일 에러로 잡혀야 하니까. 두 앱이 같이 쓰는 값, Codable 타입, 규칙은 라이브러리 타깃에 둬. 저장하는 문서 타입마다 명시적인 init(from:)을 둬서, 처음으로 필드가 늘어날 때 기존 파일을 날리지 않고 옮겨갈 수 있게 해. 순수 로직은 그 코드가 있는 곳에서 바로 Swift Testing으로 테스트해. 그리고 메인 액터 맥락에서 Objective-C API에 넘기는 클로저는 사소하게 넘길 게 아니라 꼭 리뷰할 항목이라는 걸 기억해.

Mac 앱

먼저 앱의 구조를 골라. 입력 조합과 분할 뷰가 중요하다면 창과 텍스트는 AppKit이 맡고, 설정과 폼은 SwiftUI가 맡고, @Observable 모델이 양쪽에 데이터를 대. 빌드는 SwiftPM과 스크립트로 해. 스크립트가 번들을 조립하고, 확장 속성을 지우고, 자체 서명 신원 하나로 서명하고, 이름 바꾸기로 설치한 뒤, 최종 경로에서 검증해. designated requirement가 해시가 아니라 인증서를 가리키는지 확인해서 다음 빌드에도 권한이 살아남게 해. 유틸리티라면 실패했을 때만 다시 켜주는 LaunchAgent를 붙여. 설치된 번들은 해시 검사와 마감을 건 단계로 증명하고, 그 빌드 하나의 사본을 다른 Mac들에 보내.

폰 앱

project.yml로 Xcode 프로젝트를 생성해. 구성은 유니버설 타깃 하나, 소스에 적은 팀, 공유 확장과 App Group, 그리고 손목에서 담는 게 어울리는 앱이라면 워치 앱까지야. 클라이언트는 얇게 유지해. 필요한 건 깐깐한 엔진 주소, HTTPS 쌍둥이 주소나 단독 ATS 키, 모든 호출에 거는 바이트 예산, 명령을 다시 보내지 않고 재연결하는 소켓, 로컬에 쓰는 순간 담기가 끝나는 아웃박스야. 테스트는 전용 시뮬레이터에서 돌리고 모든 건너뛰기를 시끄럽게 만들어. 그러고 나서 시뮬레이터가 못 돌려보는 게 뭔지 기억해. 마이크, Data Protection, 메일에서 온 진짜 공유야.

배포

아카이브부터 해서 식별자가 생기게 하고, 그 뒤에 번들 id를 골라서 App Store Connect 레코드를 만들어. 팀 API 키로 인증해. 파이프라인이 더러운 입력을 거부하고, 소스에서 빌드 번호를 올리고, 로그를 남기며 아카이브하게 해. 이어서 아카이브를 관문에서 검사하고, 종료 상태와 실패 표시를 확인한 뒤에 올리게 해. 이 파이프라인이 찍어주는 건 네 사실 중 첫 번째야. 처리됨, 배정됨, 설치됨은 각각 별개의 사실이고, 설치됨은 기기에서 읽은 숫자로 확인해.

이 중 어느 것도 무료 계정으론 안 돼. 이 퀘스트가 처음부터 전제로 깔았던 조건이 바로 그거야. 멤버십 비용 안에 TestFlight, App Store Connect 레코드, 확장끼리 나눠 쓰는 App Group, 푸시 키가 다 들어 있어. 뒤쪽 트랙은 전부 그 위에 서 있어. 나머지는 이 마지막 트랙의 이름이 된 규율이야. 위에서 따라온 길의 모든 주장은 규칙이 되기 전에 누군가 직접 재본 거야. 네가 보탤 다음 주장도 그래야 해.

Code

퀘스트를 마쳤을 때의 레포, 조각마다 근거가 되는 트랙과 함께·text
spark/
├── Package.swift                    swift-tools 6.x, Swift 6 language mode        (track 4)
├── Sources/SparkCore/               values, Codable with explicit init(from:)     (track 2)
├── Sources/Spark/                   the Mac app: AppKit shell, SwiftUI settings   (track 5)
├── Tests/SparkCoreTests/            Swift Testing for pure logic                  (track 4)
├── scripts/build-app.sh             assemble, sign (self-signed), install, verify (track 6)
├── scripts/test-installed-app.sh    hash identity, bounded stages                 (tracks 7, 13)
├── deploy/com.example.spark.plist   Aqua LaunchAgent, KeepAlive on failure only   (track 7)
├── clients/apple/SparkMobile/
│   ├── project.yml                  ONE universal target, team in source          (tracks 4, 8)
│   ├── Sources/ ShareExtension/ Watch/                                           (tracks 8-10)
│   └── Resources/Info.plist         ATS key alone, export compliance answered     (tracks 11-12)
└── scripts/mobile-testflight.sh     + mobile-testflight.config.zsh + upload gate  (track 12)
전체 실행을 순서대로, 그리고 말은 맨 끝에서·bash
# 1. The package: the logic both apps share, proven where it lives.
swift build && swift test

# 2. The Mac app: built, signed with the stable identity, installed, proven installed.
scripts/build-app.sh
codesign -d -r- /Applications/Spark.app 2>&1 | grep 'certificate leaf'   # not a cdhash
scripts/test-installed-app.sh

# 3. The phone app: generate, test on a DEDICATED simulator, and make skips loud.
(cd clients/apple/SparkMobile && xcodegen generate)
SPARK_SIMULATOR_ID=<udid> scripts/test-mobile.sh

# 4. Delivery: plan, then upload through every guard.
scripts/mobile-testflight.sh --plan
scripts/mobile-testflight.sh --upload     # fact 1 of 4

# 5. Then, and only then, the words: processed, assigned, installed (read on the device).

External links

Exercise

빈 폴더에서 시작해서 Spark를 끝까지 완성해. 만들 건 패키지, 설치하고 증명까지 마친 Mac 앱, 공유 확장이 있는 폰 앱, 사설 네트워크로 통신하는 엔진, 그리고 TestFlight 빌드 하나야. 단계마다 로그를 한 줄씩 남겨. 실행한 명령, 그 명령이 찍은 것, 그리고 어느 레슨의 규칙을 지켰는지를 적어. 마지막은 그 빌드에 대한 네 사실 배포 보고로 끝내고, 레슨에 안 나와서 직접 재야 했던 단계를 전부 목록으로 정리해.
Hint
정말 쓸모 있는 건 마지막 목록이야. 거기 적힌 것 중 너를 놀라게 한 건 전부 네 하우스 룰 후보야. 이 퀘스트의 하우스 룰처럼 이유와 대가를 같이 적어둬.

Progress

Progress is local-only — sign in to sync across devices.
이 페이지에서 버그를 발견하셨거나 피드백이 있으세요?문제 신고

댓글 0

🔔 답글 알림 (로그인 필요)
로그인댓글을 남기려면 로그인해 주세요.

아직 댓글이 없어요. 첫 댓글을 남겨보세요.