疑难排查

对你最可能遇到的故障——按领域给出现象、原因与修复。

针对在本模板上开发时真正会碰到的问题,按领域给出 现象 → 原因 → 修复。每一条都链回负责 该主题的页面。App Store 相关的购买故障在此以精简形式列出;完整的 RevenueCat/StoreKit 矩阵见购买测试

构建与工具链

SPM 包无法解析 / 打开时包报红。 Xcode 没能拉取依赖(Kingfisher、supabase-swift、purchases-ios、posthog-ios、 sentry-cocoa)。File → Packages → Reset Package Caches,然后 Resolve Package Versions。确认能访问 GitHub。见安装

xcodebuild 用错了 Xcode / 找不到 SDK。 命令行指向的 Xcode 与你打开的那个不同。显式设置:

DEVELOPER_DIR=/Applications/Xcode.app/Contents/Developer xcodebuild \
  -scheme SoarStarterSwift \
  -destination 'platform=iOS Simulator,name=iPhone 17 Pro' build

sudo xcode-select -s /Applications/Xcode.app。需要 Xcode 16+。

测试时好时坏 / 一起跑就不稳。 测试套件被设计为串行运行(每个测试重置共享状态)。始终关闭并行测试:

xcodebuild test -scheme SoarStarterSwift \
  -destination 'platform=iOS Simulator,name=iPhone 17 Pro' \
  -parallel-testing-enabled NO

架构 → 测试

密钥与配置

我在 Secrets.xcconfig 里设的键不生效。 值必须走完整条链路:Secrets.xcconfigInfo.plist$() 替换 → AppSecrets。 检查 (a) Secrets.xcconfig 已被 #include?,(b) 该键在 Info.plist 中存在并带 $(KEY) 占位符,(c) 你做了干净构建(改动 xcconfig 并不总会让缓存失效——Product → Clean Build Folder)。见配置

xcconfig 里的 URL 值被截断/弄坏了。 在 xcconfig 里 // 会开启注释,因此 https://… 会丢掉 // 之后的一切。使用模板的转义 技巧——把值写成 https:/$()/…,让 $() 打断 //。见配置

某个服务什么都不做,但应用也不崩溃。 这是设计使然。每个密钥都是可选的;缺失的键会让所属控制器上报 isConfigured = false 并 no-op。若某功能悄无声息地失效,多半是它的键没设——查键参考

Supabase 与鉴权

OAuth / 邮箱 OTP 走进死胡同或从不返回应用。 重定向不在 Supabase 的允许列表里,或 URL scheme 没注册。把 soar://auth-callback 加到 Authentication → URL Configuration → Redirect URLs,并确认 soar scheme 在 Info.plistCFBundleURLTypes 中。见 Supabase 设置

Google 登录打开后立即失败。 反转客户端 ID 的 URL scheme 缺失或错误。GOOGLE_REVERSED_CLIENT_ID 必须注册为一个 URL scheme,并与你 GOOGLE_IOS_CLIENT_ID 对应的那个一致。见 鉴权

已登录,但查询什么也返回不了 / RLS 拒绝。 会话有效,但行级策略拒绝了读取(例如未登录时查询 todos,或某张表缺少预期策略)。确认 会话存在,且该表的 RLS 与应用的预期匹配——数据层列出了每张表的 策略。

会话在重启后不保留 / 意外退出登录。 supabase-swift 把会话存在**钥匙串(Keychain)**里(KeychainLocalStorage),而非 UserDefaults。若你在测试中换了存储或重置了钥匙串,请重新登录。不要把会话移到 UserDefaults。见鉴权

推送通知

模拟器上拿不到设备 token。 预期如此——模拟器从不注册远程推送。APNs 是仅真机(Device-only):你需要一台真机和一个 付费 Apple 团队。见推送通知

发送测试推送时报 BadDeviceToken 沙盒与生产 APNs 不匹配——在一个环境铸造的 token 被发往另一个环境。让端点与构建 (aps-environment)匹配;send-test-push 函数会从 device_tokens 中剪除 BadDeviceToken / Unregistered 的行。见推送通知

变现

付费墙为空 / "Couldn't load license options"。 offerings() 没返回当前 offering 或没有 package。逐环节排查:appl_ SDK 密钥、一个 激活且带 package 的 offering、package 挂到了正确的 App Store 产品、产品挂到了权益、 产品不再是 Missing Metadata,以及协议/税务/银行已完成。完整清单见 购买测试

TestFlight/Release 构建启动即崩溃(checkForSimulatedStoreAPIKeyInRelease)。 Release 构建里的 REVENUECAT_IOS_KEY 是模拟商店密钥(test_)。换成 App Store 公开 SDK 密钥(appl_),递增构建号,重新上传。见 App Store 发布

购买成功后权益不解锁。 REVENUECAT_PRO_ENTITLEMENT_ID 与 RevenueCat 权益标识符(不是显示名;模板默认 pro)不一致。另外确认 webhook 到达了 Supabase 并写入了 subscriptions 行——应用把那张表、而非 CustomerInfo,当作事实来源。见 订阅

TestFlight 构建上的 Missing Compliance。 需要出口合规信息。若应用仅使用标准 SDK 网络,声明无非豁免加密(或在 Info.plist 中把 ITSAppUsesNonExemptEncryption 设为 false)。见 App Store 发布

应用门与深度链接

开发时更新门一直拦着我。 MIN_APP_VERSION 高于正在运行的构建,从而强制显示更新屏。要刻意测试该门,就在 Secrets.xcconfig 里调高 MIN_APP_VERSION / LATEST_APP_VERSION;要越过它就调低。 见应用门

深度链接没打开正确的屏幕。 检查捕获通道和路由。自定义 scheme 链接(soar://…)需要该 scheme 在 CFBundleURLTypes 中;通用链接(https://…)需要 applinks: 授权指向你的 LINKING_DOMAIN 并托管 AASA 文件。在模拟器中测试:

xcrun simctl openurl booted "soar://todos"

深度链接

同意门反复出现。 存储的同意版本不再与 LegalContent.version(一个带日期的字符串)匹配——刻意递增它会 重新提示所有人。若这不在预期内,说明你改动了 LegalContent.version。见 应用门

还没解决?

每个分区都链向负责该主题的页面——从那里开始看完整解释。专门针对沙盒购买问题, 购买测试页面有完整的 RevenueCat/StoreKit 故障矩阵 (错误码 23、"Could not check"、"Already purchased" 等)。

On this page