关于 axiom-deep-link-debugging
面向 iOS 开发者的调试深层链接实践指南,用于在模拟器中直接进入指定界面或状态。它涵盖 SwiftUI、NavigationStack 集成、参数校验,以及确保调试入口不会进入正式版本。
主要功能
- 01调试专用 URL Scheme
- 02按参数导航至指定界面
- 03链接配置应用状态
- 04集成现有导航架构
- 05限制调试代码进入正式版本
适用场景
- 01自动进入指定界面进行截图
- 02无需手动操作测试错误和空状态
- 03缩短界面修复后的验证流程
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ charleswiltgen/ axiom --skill axiom-deep-link-debugging安装后在 Claude Code 中输入/axiom-deep-link-debugging确认已加载。
详细介绍
深度链接调试
何时使用此技能
适用于:
- 为模拟器测试添加仅用于调试的深度链接
- 启用自动导航,以便截图或测试特定屏幕
- 与
simulator-testerAgent 或/axiom:screenshot集成 - 需要以编程方式导航,但不想实现生产环境的深度链接
- 无需手动点击即可测试导航流程
不要用于:
- 生产环境的深度链接(请改用
axiom-swiftui-nav技能) - Universal Links 或 App Clips
- 复杂的路由架构
示例提示词
1. “Claude Code 无法导航到用于测试的特定屏幕”
→ 添加仅用于调试的 URL scheme,以启用 xcrun simctl openurl 导航
2. “我想自动截取不同屏幕的截图”
→ 为每个屏幕创建可从模拟器调用的调试深度链接
3. “自动化测试需要设置特定的应用状态”
→ 添加既能导航又能配置状态的调试链接
注意事项——何时需要调试深度链接
如果你遇到以下任一情况,请添加调试深度链接:
测试阻碍:
- ❌ “我必须手动点击 5 个屏幕才能测试这个功能”
- ❌ “截屏功能无法显示我需要调试的屏幕”
- ❌ “自动化测试必须经过复杂设置才能进入错误状态”
调试效率低下:
- ❌ “我修复问题、重新构建、手动导航再检查——每次迭代都要花 3 分钟”
- ❌ “Claude Code 无法导航到相应位置,所以没法直观验证修复结果”
解决方案:添加调试深度链接,让你(以及 Claude Code)可以直接跳转到任意屏幕,并配置任意状态。
实现
模式 1:基本调试 URL scheme(SwiftUI)
添加仅用于调试的 URL scheme,以便导航到各个屏幕。
import SwiftUI
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
#if DEBUG
.onOpenURL { url in
handleDebugURL(url)
}
#endif
}
}
#if DEBUG
private func handleDebugURL(_ url: URL) {
guard url.scheme == "debug" else { return }
// Route based on host
switch url.host {
case "settings":
// Navigate to settings
NotificationCenter.default.post(
name: .navigateToSettings,
object: nil
)
case "profile":
// Navigate to profile
let userID = url.queryItems?["id"] ?? "current"
NotificationCenter.default.post(
name: .navigateToProfile,
object: userID
)
case "reset":
// Reset app to initial state
resetApp()
default:
print("⚠️ Unknown debug URL: \(url)")
}
}
#endif
}
#if DEBUG
extension Notification.Name {
static let navigateToSettings = Notification.Name("navigateToSettings")
static let navigateToProfile = Notification.Name("navigateToProfile")
}
extension URL {
var queryItems: [String: String]? {
guard let components = URLComponents(url: self, resolvingAgainstBaseURL: false),
let items = components.queryItems else {
return nil
}
return Dictionary(uniqueKeysWithValues: items.map { ($0.name, $0.value ?? "") })
}
}
#endif
用法:
# From simulator
xcrun simctl openurl booted "debug://settings"
xcrun simctl openurl booted "debug://profile?id=123"
xcrun simctl openurl booted "debug://reset"
模式 2:与 NavigationPath 集成(iOS 16+)
将调试深度链接与 NavigationStack 集成,实现可靠的导航。
import SwiftUI
@MainActor
class DebugRouter: ObservableObject {
@Published var path = NavigationPath()
#if DEBUG
func handleDebugURL(_ url: URL) {
guard url.scheme == "debug" else { return }
switch url.host {
case "settings":
path.append(Destination.settings)
case "recipe":
if let id = url.queryItems?["id"], let recipeID = Int(id) {
path.append(Destination.recipe(id: recipeID))
}
case "recipe-edit":
if let id = url.queryItems?["id"], let recipeID = Int(id) {
// Navigate to recipe, then to edit
path.append(Destination.recipe(id: recipeID))
path.append(Destination.recipeEdit(id: recipeID))
}
case "reset":
path = NavigationPath() // Pop to root
default:
print("⚠️ Unknown debug URL: \(url)")
}
}
#endif
}
struct ContentView: View {
@StateObject private var router = DebugRouter()
var body: some View {
NavigationStack(path: $router.path) {
HomeView()
.navigationDestination(for: Destination.self) { destination in
destinationView(for: destination)
}
}
#if DEBUG
.onOpenURL { url in
router.handleDebugURL(url)
}
#endif
}
@ViewBuilder
private func destinationView(for destination: Destination) -> some View {
switch destination {
case .settings:
SettingsView()
case .recipe(let id):
RecipeDetailView(recipeID: id)
case .recipeEdit(let id):
RecipeEditView(recipeID: id)
}
}
}
enum Destination: Hashable {
case settings
case recipe(id: Int)
case recipeEdit(id: Int)
}
用法:
# Navigate to settings
xcrun simctl openurl booted "debug://settings"
# Navigate to recipe #42
xcrun simctl openurl booted "debug://recipe?id=42"
# Navigate to recipe #42 edit screen
xcrun simctl openurl booted "debug://recipe-edit?id=42"
# Pop to root
xcrun simctl openurl booted "debug://reset"
模式 3:状态配置链接
既能导航又能配置状态的调试链接。
#if DEBUG
extension DebugRouter {
func handleDebugURL(_ url: URL) {
guard url.scheme == "debug" else { return }
switch url.host {
case "login":
// Show login screen
path.append(Destination.login)
case "login-error":
// Show login screen WITH error state
path.append(Destination.login)
// Trigger error state
NotificationCenter.default.post(
name: .showLoginError,
object: "Invalid credentials"
)
case "recipe-empty":
// Show recipe list in empty state
UserDefaults.standard.set(true, forKey: "debug_emptyRecipeList")
path.append(Destination.recipes)
case "recipe-error":
// Show recipe list with network error
UserDefaults.standard.set(true, forKey: "debug_networkError")
path.append(Destination.recipes)
default:
print("⚠️ Unknown debug URL: \(url)")
}
}
}
#endif
用法:
# Test login error state
xcrun simctl openurl booted "debug://login-error"
# Test empty recipe list
xcrun simctl openurl booted "debug://recipe-empty"
# Test network error handling
xcrun simctl openurl booted "debug://recipe-error"
模式 4:Info.plist 配置(仅 DEBUG)
仅在调试构建中注册调试 URL scheme。
步骤 1:将 scheme 添加到 Info.plist
<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>debug</string>
</array>
<key>CFBundleURLName</key>
<string>com.example.debug</string>
</dict>
</array>
步骤 2:从发布构建中移除
在目标的 Build Phases 中添加 Run Script 阶段(在 “Copy Bundle Resources” 之前运行):
# Strip debug URL scheme from Release builds
if [ "${CONFIGURATION}" = "Release" ]; then
echo "Removing debug URL scheme from Info.plist"
/usr/libexec/PlistBuddy -c "Delete :CFBundleURLTypes:0" "${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}" 2>/dev/null || true
fi
替代方案:在 Build Settings 中为 Debug 和 Release 配置使用不同的 Info.plist 文件。
与模拟器测试集成
使用 /axiom:screenshot 命令
# 1. Navigate to screen
xcrun simctl openurl booted "debug://settings"
# 2. Wait for navigation
sleep 1
# 3. Capture screenshot
/axiom:screenshot
使用 simulator-tester Agent
只需告诉该 Agent:
- “导航到 Settings 并截屏”
- “打开食谱编辑器并检查布局”
- “进入错误状态并展示效果”
该 Agent 会使用你的调试深度链接进行导航。
必须完成的前置步骤
添加调试深度链接之前,务必先完成以下步骤:
步骤 1:明确导航需求
列出测试时需要访问的所有屏幕:
- Settings screen
- Profile screen (with specific user ID)
- Recipe detail (with specific recipe ID)
- Error states (login error, network error, etc.)
- Empty states (no recipes, no favorites)
步骤 2:选择 URL scheme 模式
debug://screen-name # Simple screen navigation
debug://screen-name?param=value # Navigation with parameters
debug://state-name # State configuration
步骤 3:添加 URL 处理程序
使用 #if DEBUG 确保代码在发布构建中会被移除。
步骤 4:测试深度链接
# Boot simulator
xcrun simctl boot "iPhone 16 Pro"
# Launch app
xcrun simctl launch booted com.example.YourApp
# Test each deep link
xcrun simctl openurl booted "debug://settings"
xcrun simctl openurl booted "debug://profile?id=123"
常见错误
❌ 错误——在 URL 处理程序中硬编码导航
#if DEBUG
func handleDebugURL(_ url: URL) {
if url.host == "settings" {
// ❌ WRONG — Creates tight coupling
self.showingSettings = true
}
}
#endif
问题:URL 处理程序现在负责导航逻辑,重复实现了协调器/路由器的模式。
✅ 正确——使用现有导航系统:
#if DEBUG
func handleDebugURL(_ url: URL) {
if url.host == "settings" {
// Use existing NavigationPath
path.append(Destination.settings)
}
}
#endif
❌ 错误——在生产环境中保留调试代码
// ❌ WRONG — No #if DEBUG
func handleDebugURL(_ url: URL) {
// This ships to users!
}
问题:调试端点暴露在生产环境中,存在安全风险。
✅ 正确——使用 #if DEBUG 包裹:
#if DEBUG
func handleDebugURL(_ url: URL) {
// Stripped from release builds
}
#endif
❌ 错误——使用未经验证的查询参数
#if DEBUG
case "profile":
let userID = Int(url.queryItems?["id"] ?? "0")! // ❌ Force unwrap
path.append(Destination.profile(id: userID))
#endif
问题:如果缺少 id 或其值无效,就会崩溃。
✅ 正确——验证参数:
#if DEBUG
case "profile":
guard let idString = url.queryItems?["id"],
let userID = Int(idString) else {
print("⚠️ Invalid profile ID")
return
}
path.append(Destination.profile(id: userID))
#endif
测试清单
在自动化工作流中使用调试深层链接之前:
- URL 处理程序用
#if DEBUG包裹 - 已在模拟器中手动测试所有深层链接
- 已验证参数(不要强制解包)
- 深层链接与现有导航集成(不要重复实现逻辑)
- 已从 Release 构建中移除 URL scheme(通过脚本或单独的 Info.plist)
- 已在 README 或注释中记录,供其他开发者参考
- 可与
/axiom:screenshot命令配合使用 - 可与
simulator-testerAgent 配合使用
真实案例
场景:你正在调试菜谱应用编辑器屏幕中的布局问题。
之前(手动测试):
- 构建应用 → 30 秒
- 启动模拟器
- 点击“菜谱” → 等待加载
- 滚动到菜谱 #42
- 点击以打开详情
- 点击“编辑”
- 检查布局是否已修复
- 进行更改,重新构建 → 从第 1 步重复 总计:每次迭代需 2-3 分钟
之后(使用调试深层链接):
- 构建应用 → 30 秒
- 运行:
xcrun simctl openurl booted "debug://recipe-edit?id=42" - 运行:
/axiom:screenshot - Claude 分析截图并确认布局已修复
- 如有需要,进行更改并重新构建 → 从第 2 步重复 总计:每次迭代需 45 秒
节省时间:通过视觉验证,迭代速度提升 60-75%
与现有导航集成
对于使用 NavigationStack 的应用
添加调试 URL 处理程序,将内容追加到现有 NavigationPath:
router.path.append(Destination.fromDebugURL(url))
对于使用 Coordinator 模式的应用
在调试 URL 处理程序中触发 Coordinator 方法:
coordinator.navigate(to: .fromDebugURL(url))
对于使用自定义路由的应用
与路由器的导航 API 集成:
AppRouter.shared.push(Screen.fromDebugURL(url))
核心原则:调试深层链接应使用现有导航,而不是取而代之。
高级模式
模式 5:参数化状态设置
#if DEBUG
case "test-scenario":
// Parse complex test scenario from URL
// Example: debug://test-scenario?user=premium&recipes=empty&network=slow
if let userType = url.queryItems?["user"] {
configureUser(type: userType) // "premium", "free", "trial"
}
if let recipesState = url.queryItems?["recipes"] {
configureRecipes(state: recipesState) // "empty", "full", "error"
}
if let networkState = url.queryItems?["network"] {
configureNetwork(state: networkState) // "fast", "slow", "offline"
}
// Now navigate
path.append(Destination.recipes)
#endif
用法:
# Test premium user with empty recipe list
xcrun simctl openurl booted "debug://test-scenario?user=premium&recipes=empty"
# Test slow network with error handling
xcrun simctl openurl booted "debug://test-scenario?network=slow&recipes=error"
模式 6:截图自动化辅助工具
创建单个 URL,以设置状态并进行捕获:
#if DEBUG
case "screenshot":
// Parse screen and configuration
guard let screen = url.queryItems?["screen"] else { return }
// Configure state
if let state = url.queryItems?["state"] {
applyState(state)
}
// Navigate
navigate(to: screen)
// Post notification for external capture
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
NotificationCenter.default.post(
name: .readyForScreenshot,
object: screen
)
}
#endif
用法:
# Navigate to login screen with error state, wait, then screenshot
xcrun simctl openurl booted "debug://screenshot?screen=login&state=error"
sleep 2
xcrun simctl io booted screenshot login-error.png
相关技能
axiom-swiftui-nav— 生产环境中的深层链接和 NavigationStack 模式simulator-tester— 使用调试深层链接进行模拟器自动化测试axiom-xcode-debugging— 环境优先的调试工作流
总结
调试深层链接可实现:
- 闭环调试,并进行视觉验证
- 视觉修复的迭代速度提升 60-75%
- 自动化测试,无需手动导航
- 为任何应用状态自动截图
请记住:
- 用
#if DEBUG包裹所有调试代码 - 从 Release 构建中移除 URL scheme
- 与现有导航集成,不要重复实现
- 验证所有参数(不要强制解包)
- 为团队成员提供文档说明