Communitygithub.com

byteratio/iphone-dev

네이티브 iPhone 앱 개발을 위한 컨벤션 — SwiftUI + SwiftData 로컬 퍼스트 앱, XcodeGen 프로젝트 생성, CLI 전용 빌드/테스트/배포 루프 (기본 시뮬레이터, devicectl을 통한 서명된 기기 배포), XCTest 가시적 커버리지, 그리고 무료 개인 팀 서명 문제점. iOS/iPhone 앱을 만들거나, 프로젝트에 iOS 타겟이나 Makefile 루프를 추가하거나, Xcode 서명/프로비저닝/배포 문제를 디버깅하거나, 기존 앱의 로직을 네이티브 Swift 클라이언트로 포팅할 때 사용하세요.

iphone-dev란 무엇인가요?

이 스킬은 작은 규모의 SwiftUI + SwiftData 앱을 처음부터 실제 기기까지 배포하는 전체 과정에서 얻은 실용적인 컨벤션을 제공합니다. 무료 개인 Apple 계정과 App Store 배포가 없는 시나리오에 중점을 두며, SwiftData를 사용한 로컬 퍼스트 데이터 모델링, XcodeGen을 통한 .xcodeproj 없는 프로젝트 관리, 시뮬레이터와 실제 기기 모두를 위한 CLI 기반 개발 루프를 강조합니다. 또한 XCTest를 통한 테스트, 샘플 데이터 시딩, 그리고 무료 팀에서 발생하는 서명 문제(7일 만료, 최대 3개 앱, 푸시 불가 등)와 이를 해결하는 방법을 다룹니다. 기존 서버 앱의 동반 클라이언트로서 동기화 계층(씬 클라이언트, 스냅샷 미러, 스코프 아웃박스, 양방향 동기화)을 선택하는 실용적인 접근 방식도 포함되어 있어, 개발자가 가장 적합한 수준을 선택할 수 있도록 돕습니다. 최종적으로는 애플 생태계에 익숙하지 않은 개발자도 안정적이고 유지보수 가능한 iOS 앱을 구축할 수 있도록 가이드하는 것이 목표입니다.

지원 대상~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/byteratio/skills/tree/main/plugins/hechtch-skills/skills/iphone-dev

즐겨 사용하는 AI에게 물어보기

이 에이전트 스킬이 미리 로드된 새 채팅을 엽니다.

문서

iPhone 앱 개발

빈 저장소부터 실제 휴대폰에서 실행되는 서명된 빌드까지 SwiftUI 앱을 처음부터 끝까지 출시하면서 얻은 컨벤션입니다.

가정된 컨텍스트: 소규모 앱, 무료 개인 Apple 계정, App Store 배포 없음. 유료 개발자 팀은 아래 몇 가지 규칙을 변경합니다 — 서명 섹션에서 이를 표시합니다.

아키텍처 기본값

  • SwiftUI + SwiftData, 로컬 퍼스트. 소규모 앱은 서버 연결이 필요하지 않아야 합니다. 데이터는 기기 내 SwiftData에 저장됩니다. 네트워크 호출은 공개 API에만 사용됩니다 (https, ATS 예외 없음). 앱이 기존 백엔드의 씬 클라이언트로 시작하는 경우, "서버를 없앨 수 있을까?"라는 전환을 예상하세요 — 서버의 로직을 SwiftData 배열 위에서 동작하는 순수 Swift 함수로 포팅하고, 하나의 휴대폰을 위해 서버를 유지하지 마세요.
  • SwiftData 모델링: 정수 외래 키가 아닌 실제 객체 관계. 자연 키에는 @Attribute(.unique)를 사용하세요. SwiftData의 to-many 배열은 순서를 유지하지 않습니다 — 순서가 중요할 때는 sortOrder 필드와 sortedX 계산 속성을 추가하세요. 날짜는 String이 아닌 Date로 저장하세요. 열거형은 String 원시값을 가진 Codable로 선언하세요.
  • 실제 설치에서의 스키마 변경: 모델이나 옵션 속성을 추가하면 다음 실행 시 자동으로 경량 마이그레이션됩니다. 하지만 SchemaMigrationPlan 없이 이름 변경, 삭제 또는 타입 변경을 하면 저장소가 열리지 않습니다 — 배포된 앱은 사용자 데이터는 그대로이지만 시작 시 충돌합니다. (동일한 번들 ID로) 재배포해도 데이터 컨테이너는 지워지지 않습니다. 앱을 삭제해야만 지워집니다. 사용자가 실제 데이터를 쌓기 전에 내보내기/백업 경로를 계획하세요.
  • 뷰는 @Query + modelContext에 직접 접근합니다. 캐싱이 실제로 필요한 경우가 아니라면 Store나 repository 계층은 사용하지 않습니다. 순수 로직 (매칭, 파싱, 임포트)은 열거형 네임스페이스에 위치합니다 (enum Foo { static func … }) — UI 없이 테스트 가능합니다.
  • 배포 대상: 필요한 API가 달리 명시하지 않는 한 현재 메이저 버전에서 하나를 뺀 버전입니다. 하나의 타겟, TARGETED_DEVICE_FAMILY: "1"이며, 앱이 회전을 필요로 하지 않는 한 세로 모드입니다.

프로젝트 생성 — XcodeGen, .xcodeproj는 절대 커밋하지 않음

ios/project.yml이 진실의 원천입니다. .xcodeproj 번들은 생성되며 gitignored 됩니다 (ios/build/와 생성된 Info.plist도 gitignore 하세요). 파일을 추가하거나 제거한 후에는 재생성하세요 — xcodegen은 소스 디렉토리를 glob 처리하므로 기존 파일 수정은 아무것도 필요하지 않습니다.

  • GitHub 릴리스 zip에서 xcodegen을 설치하세요 (PREFIX=~/.local ./install.sh) — Homebrew가 필요 없으며, 잠긴 Mac에서도 작동합니다.
  • 단위 테스트 타겟은 GENERATE_INFOPLIST_FILE: YES가 필요합니다. 그렇지 않으면 테스트 번들 서명에 실패합니다.
  • project.yml에서 gatherCoverageData: true로 스킴을 정의하고 테스트 타겟을 연결하세요. 그렇지 않으면 xcodebuild test가 실행할 대상을 찾지 못합니다.

개발 루프 — CLI 전용, 기본 시뮬레이터

모든 iOS 프로젝트에는 다음 Makefile 타겟이 있습니다. 시뮬레이터 이름, 기기 ID, 팀 ID를 ?= 변수로 매개변수화하여 기계별 정보가 하드코딩되지 않도록 하세요.

타겟설명
ios-projectxcodegen generate + Xcode에서 열기
ios-sim시뮬레이터용 서명되지 않은 빌드, 시뮬레이터 부팅, 설치, 실행 — 기본 루프; 서명 제로, 비밀번호 프롬프트 제로
ios-sim-seedios-sim 실행 후 --seed-demo로 재실행
ios-test시뮬레이터에서 xcodebuild test + xccov 타겟별 커버리지 보고서
ios-deploy연결된 휴대폰용 서명된 빌드 + devicectl 설치 + 실행

ios-test는 매 실행마다 커버리지를 출력합니다 — 기본 규칙은 make test가 항상 숫자를 보여주며, 엄격한 임계값은 없습니다.

시뮬레이터 빌드: CODE_SIGNING_ALLOWED=NO는 generic/platform=iOS 컴파일 검사에 적합합니다. 시뮬레이터 대상은 전혀 서명이 필요하지 않습니다. 시뮬레이터와 휴대폰은 별도의 데이터베이스를 가지고 있음을 기억하세요.

항상 효과가 있는 디버그 편의 기능

  • 샘플 데이터 시더 (DemoData.swift, #if DEBUG): .now를 기준으로 계산된 만료/날짜 필드를 가진 큐레이팅된 데이터셋, 멱등성 (재실행해도 추가되지 않음), 세 가지 방식으로 노출됨 — 설정 "Developer" 섹션 버튼, ProcessInfo.processInfo.arguments를 통해 확인되는 --seed-demo 실행 인자, 그리고 ios-sim-seed 타겟. 시드가 앱의 주요 상태를 실행하는지 확인하는 테스트를 추가하여 조용히 썩지 않도록 하세요.
  • 휠 피커 대신 검색 가능한 피커: 목록이 수십 개 항목을 초과할 수 있는 경우 (임포트된 데이터셋이 이를 보장함).
  • 카메라/스캐너 기능: DataScannerViewController.isSupported && .isAvailable을 확인하고 수동 입력 대체 기능을 제공하세요 — 시뮬레이터에서도 정상적으로 작동합니다.

테스팅

  • 시뮬레이터에서 XCTest; 다른 스택에서 로직을 포팅할 때는 참조 테스트 스위트도 함께 포팅하여 언어 간 의미를 고정하세요.
  • 공유 헬퍼: isStoredInMemoryOnly: true로 ModelContainer를 만드는 makeInMemoryContext(); @MainActor 테스트 클래스와 setUp() async throws.
  • 결과 번들에서 통과/실패를 읽습니다: xcrun xcresulttool get test-results summary --path build/test.xcresult.

서명 및 배포 — 문제점 (각각 실제 시간을 소비함)

  • 팀 ID는 security find-identity의 괄호 안에 있는 값이 아닙니다 — 그것은 인증서 자체의 ID입니다. 프로비저닝 프로파일(security cms -D -i <profile> → application-identifier 접두사) 또는 Xcode의 계정 창에서 실제 팀 ID를 얻으세요. 잘못된 ID는 오해의 소지가 있는 "No Account for Team" 오류를 발생시킵니다.
  • -allowProvisioningUpdates는 일반 터미널에서 Apple ID 세션을 볼 수 없습니다. Xcode GUI에서 한 번 프로비저닝 프로파일을 만드세요 (Signing & Capabilities → Team 설정 → Try Again). 그 후에는 DEVELOPMENT_TEAM=<id> CODE_SIGN_STYLE=Automatic으로 순수 CLI 빌드가 작동합니다.
  • 첫 번째 기기 절차: 휴대폰에서 개발자 모드 활성화 (설정 → 개인정보 보호 및 보안; 이 토글은 Xcode가 기기와 통신한 후에만 나타납니다. 재부팅 필요) → 컴퓨터 신뢰 → 첫 번째 설치 후, 개발자 인증서 신뢰 (설정 → 일반 → VPN 및 기기 관리). Xcode의 "your team has no devices" 오류는 일반적으로 등록 시도가 휴대폰이 재부팅 중이거나 잠겨 있을 때 발생한 것을 의미합니다 — 휴대폰을 실행 대상으로 선택하고 재시도하세요.
  • 모든 빌드 시 키체인 프롬프트: 코드 서명 키체인 대화상자에서 항상 허용을 한 번 클릭하면 빌드별 비밀번호 프롬프트가 중단됩니다.
  • 무료 개인 팀 제한사항: 설치는 7일 후 만료됩니다 (재배포하여 재서명). 최대 3개의 사이드로드 앱, 푸시 알림 없음, App Groups 없음. 유료 팀은 이 네 가지 제한을 모두 해제합니다 — 앱에 푸시 또는 App Groups가 필요하다면 유료 팀을 가져야 할 트리거입니다.
  • 배포/실행 CLI: xcrun devicectl list devices, xcrun devicectl device install app --device <id> <path>.app, … device process launch --device <id> <bundle-id>.

새 Mac 부트스트랩 (헤드리스 친화적)

  1. App Store에서 Xcode 설치 (macappstore://apps.apple.com/app/id497799835).
  2. sudo xcode-select -s /Applications/Xcode.app && sudo xcodebuild -license accept && sudo xcodebuild -runFirstLaunch — 관리자 권한 필요; 표준 계정에서는 먼저 관리자 멤버십을 수정하세요 (su admin -c 'sudo dseditgroup -o edit -a <user> -t user admin').
  3. xcodebuild -downloadPlatform iOS — iOS SDK + 시뮬레이터 런타임은 Xcode 15 이후 별도의 수 GB 다운로드입니다. 이 없으면 빌드가 "iOS X.Y is not installed" 오류와 함께 실패합니다.
  4. DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer는 xcode-select가 고쳐지기 전에 명령별 재정의로 작동합니다.
  5. 릴리스 zip에서 ~/.local로 xcodegen 설치 (위 참조).

기존 서버 앱의 동반 클라이언트 — 동기화 계층

iPhone 앱이 이미 서버 + 웹앱이 있는 앱에 합류할 때 (서버가 단일 진실의 원천으로 유지됨), 휴대폰의 사용 사례가 허용하는 가장 저렴한 계층을 선택하세요. 먼저 "휴대폰이 서버 네트워크 밖에서 작동해야 하는가?"와 "캡처만? 전체 편집?"을 물어보세요.

  1. 씬 클라이언트 — 로컬 저장소 없음; 휴대폰은 기존 API를 호출합니다. 연결이 보장될 때 적합합니다.
  2. 스냅샷 미러 — 전체 JSON 풀에서 SwiftData 캐시를 전체적으로 다시 빌드합니다 (단일 사용자 DB는 작습니다). 병합 없음 → 툼스톤, 행별 타임스탬프, 충돌 엔진 없음. 서버 측 삭제/편집이 자동으로 반영됩니다. 사진은 URL 캐시를 통해 지연 로드됩니다.
  3. 스코프 아웃박스 — 오프라인 생성을 이미 존재하는 레코드의 자식으로 제한합니다 (알려진 부모에 대한 로그, 노트, 사진). 부모는 실제 서버 ID를 가지므로 재생은 일반 API 호출입니다 — 정수 PK 스키마는 UUID 마이그레이션이 필요 없습니다. 동기화 이벤트 = 아웃박스 푸시 후 스냅샷 풀 (자체 캡처는 확인되어 돌아옴). 404-고아는 표면화하세요 ("N을(를) 동기화할 수 없습니다"), 절대 조용히 드롭하지 마세요. 보류 중인 배지 + 마지막 동기화 타임스탬프를 표시하세요. 사용자가 고정한 서버 주소가 응답할 때만 자동 동기화 — 절대 외부 네트워크를 프로브하지 마세요.
  4. 완전 양방향 동기화 (UUID, 툼스톤, 델타 엔드포인트, LWW/CRDT) — 계층 1~3이 충분하지 않다는 증거가 있을 때만; 재작업은 Swift 코드를 작성하기 전에 서버 측에서 시작됩니다 (클라이언트에서 생성 가능한 UUID, 멱등성 upsert, changes?since= + 툼스톤).

계층 2와 3은 잘 조합됩니다: 읽기용 스냅샷 미러와 캡처용 스코프 아웃박스는 계층 4의 메커니즘 없이도 "집에서 작동, 어디서나 캡처"를 처리합니다.

프로젝트 위생

  • 기계 및 기기별 정보 (팀 ID, 기기 UDID, 시뮬레이터 이름)는 Makefile ?= 변수나 프로젝트 자체 노트에 저장하세요 — 스크립트에 절대 하드코딩하지 말고, 이 스킬에도 절대 넣지 마세요.
  • 진행 중인 각 작업 조각에 대해 plans/<topic>.md 문서를 유지하고, 출시된 내용에 대한 최상위 CHANGELOG.md를 유지하세요. 작업이 완료되면 계획을 plans/done/<topic>.md로 이동하고 "Delivered in — " 헤더를 추가하세요.
  • 각 계획 조각은 휴대폰이나 시뮬레이터에서 실행 가능한 것으로 끝나도록 크기를 조정하세요. 실행할 수 없는 iOS 조각은 검토할 수 없습니다.

Individual skills in this repo

This repo contains 5 individual skills — each has its own dedicated page.

byteratio/example-skill

Template skill for this marketplace. Use as a starting point when adding a new skill — copy this plugin directory, rename it, and replace this description with the specific trigger conditions for your skill (what the user says or does that should invoke it).

byteratio/kanban-plans

Track multi-session plans on a locally-running kanban board (github.com/hechtch/kanban) via its agent REST API. Claim a plan, move it through todo/doing/blocked/awaiting_merge/done, leave notes, and set the git branch. Use at the start of any non-trivial multi-session plan, when resuming such a plan, or when the user asks what's on the board or where a plan stands.

byteratio/password-generator

Generates secure, memorable passwords using 3 random dictionary words with leet-speak substitutions. Use when the user asks to generate a password, create a new password, or needs a secure passphrase.

byteratio/security-sweep

Investigate whether a project (or every repo on the machine) is exposed to known and actively-exploited dependency vulnerabilities. Combines a deterministic per-ecosystem audit (npm audit, govulncheck, pip-audit, osv-scanner) with live web research into recent/active supply-chain incidents, then checks each project's lockfiles directly — the leading-indicator check that `npm audit` misses. Use when the user asks to "investigate security", "check for vulnerable dependencies", "are we affected by <a named CVE / package compromise>", "scan the projects for supply-chain issues", or wants a recurring security sweep.

byteratio/ux-agents

Scaffold persona-based UX testing agents for a web app — project-scoped .claude/agents/<persona>-ux.md files that drive a real browser via Playwright, walk user journeys as a specific persona, and report tagged friction findings. Use when setting up UX review automation, when the user asks what a specific user segment would think of the app, or after the user accepts an offer to add persona testing.

관련 스킬