疑难排查
对你最可能遇到的故障——按领域给出现象、原因与修复。
针对在本模板上开发时真正会碰到的问题,按领域给出 现象 → 原因 → 修复。每一条都链回负责 该主题的页面。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.xcconfig → Info.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.plist 的 CFBundleURLTypes 中。见 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" 等)。
