Communitygithub.com

byteratio/iphone-dev

适用于原生 iPhone 应用开发的约定——SwiftUI + SwiftData 本地优先应用、XcodeGen 项目生成、纯命令行构建/测试/部署循环(默认模拟器,通过 devicectl 进行已签名设备部署)、带可见覆盖率的 XCTest 以及免费个人团队签名的坑。在创建 iOS/iPhone 应用、为项目添加 iOS 目标或 Makefile 循环、调试 Xcode 签名/配置/部署问题或将现有应用逻辑移植到原生 Swift 客户端时使用。

iphone-dev 是什么?

这是一个专注于原生 iOS 应用开发的技能,涵盖了从空仓库到在实体手机上运行签名构建的完整流程。它默认使用 SwiftUI + SwiftData 作为本地优先的架构,并推荐使用 XcodeGen 进行项目生成,避免将 .xcodeproj 提交到版本控制。该技能详细介绍了纯命令行的开发循环,包括用于模拟器构建、测试、部署以及物理设备签名的 Makefile 目标,并强调了在免费个人 Apple 开发者账号下的签名和部署陷阱,例如 7 天过期限制和无推送通知。此外,它还提供了关于测试(XCTest)、调试便利设施(如示例数据种子器)、项目卫生(如维护计划和变更记录)以及为现有服务器应用构建配套客户端的同步层次建议。适用于任何使用 Claude Code、Cursor 或类似 AI 编码代理构建 iOS 应用的用户,尤其是在处理 SwiftUI、SwiftData、XcodeGen 和免费团队签名时。

兼容平台~Claude Code~Codex CLI~Cursor
npx skills add https://github.com/byteratio/skills/tree/main/plugins/hechtch-skills/skills/iphone-dev

在你喜欢的 AI 中提问

打开一个已预加载此 Agent Skill 的新对话。

文档

iPhone 应用开发

从将一个 SwiftUI 应用从空仓库完整交付到在实体手机上运行的签名构建过程中提炼出的约定。

假设上下文:小规模应用、免费的个人 Apple 账户、无 App Store 分发。付费开发者团队会改变以下几条规则——签名部分会标记哪些规则会变化。

架构默认值

  • SwiftUI + SwiftData,本地优先。 一个小应用不应依赖可达的服务器。数据存储在设备上的 SwiftData 中;网络调用仅用于公共 API(https,无 ATS 例外)。如果应用最初是现有后端的瘦客户端,预期会出现“我们能去掉服务器吗?”的转向——将后端逻辑移植为纯 Swift 函数,操作 SwiftData 数组,而不是为一个手机保留一个服务器。
  • SwiftData 建模:使用真实对象关系,而不是整型外键。对自然键使用 @Attribute(.unique)。SwiftData 的多对多数组不保持顺序——当顺序重要时,添加一个 sortOrder 字段和一个 sortedX 计算属性。日期使用 Date,而不是字符串。枚举使用 String 原始值的 Codable。
  • 实时安装上的架构变更:添加模型或可选属性会在下次启动时静默地进行轻量级迁移;重命名、删除或类型变更如果没有 SchemaMigrationPlan 会导致存储无法打开——部署的应用会在启动时崩溃,用户数据完好但被锁定。重新部署(相同的 bundle ID)永远不会擦除数据容器;只有删除应用才会。在用户积累真实数据之前,规划一个导出/备份路径。
  • 视图直接与 @Query + modelContext 通信。 除非某些东西确实需要缓存,否则不需要 Store / repository 层。纯逻辑(匹配、解析、导入)位于枚举命名空间中(enum Foo { static func … })——无需 UI 即可测试。
  • 部署目标:当前主要版本减一,除非某个所需 API 另有要求。一个目标,TARGETED_DEVICE_FAMILY: "1",竖屏,除非应用需要旋转。

项目生成——XcodeGen,绝不提交 .xcodeproj

ios/project.yml 是真实来源;.xcodeproj 包是生成的并被 gitignore(同时 gitignore ios/build/ 和生成的 Info.plist)。添加或删除文件后重新生成——xcodegen 会扫描源目录,因此编辑现有文件不需要任何操作。

  • 从 GitHub 发布 zip 安装 xcodegen(PREFIX=~/.local ./install.sh)——无需 Homebrew,可在锁定的 Mac 上工作。
  • 单元测试目标需要 GENERATE_INFOPLIST_FILE: YES,否则测试 bundle 签名会失败。
  • 在 project.yml 中定义 scheme,设置 gatherCoverageData: true 并附加测试目标,否则 xcodebuild test 没有东西可运行。

开发循环——纯命令行,默认模拟器

每个 iOS 项目都包含这些 Makefile 目标。将模拟器名称、设备 ID 和团队 ID 参数化为 ?= 变量,这样就不会硬编码任何机器特定的内容:

目标功能
ios-projectxcodegen 生成 + 在 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 计算,幂等(重新运行不添加任何内容),通过三种方式暴露——设置中的“开发者”部分按钮、通过 ProcessInfo.processInfo.arguments 检查的 --seed-demo 启动参数以及 ios-sim-seed 目标。添加一个断言种子器能锻炼应用主要状态的测试,这样它就不会静默腐烂。
  • 当列表可能超过几十项时,使用可搜索的 Picker 而不是滚轮 Picker(导入的数据集保证会达到这个规模)。
  • 相机/扫描器功能:检查 DataScannerViewController.isSupported && .isAvailable 并提供手动输入的后备方案——这样也能在模拟器中优雅降级。

测试

  • 在模拟器中使用 XCTest;当从其他栈移植逻辑时,同时移植参考测试套件,这样语义能在跨语言时锁定。
  • 共享辅助工具:makeInMemoryContext(),构建一个设置了 isStoredInMemoryOnly: true 的 ModelContainer;@MainActor 测试类使用 setUp() async throws。
  • 从结果 bundle 读取通过/失败:xcrun xcresulttool get test-results summary --path build/test.xcresult。

签名与部署——陷阱(每个都花费了真金白银的时间)

  • 团队 ID 不是 security find-identity 中括号内的内容——那是证书自己的 ID。从配置文件中获取真实的团队 ID(security cms -D -i <profile> → application-identifier 前缀)或 Xcode 的 Accounts 面板。错误的 ID 会导致误导性的“No Account for Team”错误。
  • -allowProvisioningUpdates 无法从普通终端看到 Apple ID 会话。 在 Xcode GUI 中创建配置文件一次(Signing & Capabilities → 设置 Team → Try Again);之后,纯 CLI 构建可以与 DEVELOPMENT_TEAM=<id> CODE_SIGN_STYLE=Automatic 一起工作。
  • 首次设备舞蹈:在手机上启用开发者模式(设置 → 隐私与安全性;仅当 Xcode 与设备通信后才会出现切换开关;重启)→ 信任计算机 → 首次安装后,信任开发者证书(设置 → 通用 → VPN 与设备管理)。Xcode 的“your team has no devices”错误通常意味着在手机重启或锁定时进行了注册尝试——选择手机作为运行目标并重试。
  • 每次构建都弹出钥匙串提示:在 codesign 钥匙串对话框上点击一次始终允许;每次构建的密码提示就会停止。
  • 免费个人团队限制:安装会在 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——自 Xcode 15 起,iOS SDK + 模拟器运行时是一个单独的几 GB 下载;如果没有它,构建会失败并提示“iOS X.Y is not installed”。
  4. 在修复 xcode-select 之前,DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer 可以作为每个命令的覆盖项使用。
  5. 从发布 zip 将 xcodegen 安装到 ~/.local(见上文)。

现有服务器应用的配套客户端——同步层次

当 iPhone 应用加入一个已有服务器 + Web 应用的应用时(服务器保持为单一真实来源),选择手机用例允许的最便宜的层次。先问“手机是否需要脱离服务器网络工作?”和“捕获还是完整编辑?”:

  1. 瘦客户端——无本地存储;手机调用现有 API。当连接性得到保证时合适。
  2. 快照镜像——从完整的 JSON 拉取整体重建 SwiftData 缓存(单用户数据库很小)。无需合并 → 无需墓碑、无逐行时间戳、无冲突引擎;服务器端的删除/编辑免费到达。照片通过 URL 缓存延迟加载。
  3. 限定发件箱——离线创建仅限于已存在记录的子项(日志、笔记、针对已知父项的照片)。父项持有真实的服务器 ID,因此重放就是简单的 API 调用——整数主键模式不需要 UUID 迁移。同步事件 = 推送发件箱,然后拉取快照(自己的捕获会被确认返回)。显示 404 孤项(“N 项无法同步”),绝不静默丢弃。显示待处理标记 + 上次同步时间戳。仅当用户固定的服务器地址响应时自动同步——绝不探测外部网络。
  4. 完全双向同步(UUID、墓碑、delta 端点、LWW/CRDT)——仅在证据表明第 1-3 层不够用时使用;重构从服务器端开始(客户端可生成的 UUID、幂等 upsert、changes?since= + 墓碑),然后才编写任何 Swift 代码。

第 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.

相关技能