Loading...
Loading...
Compare original and translation side by side
simulator-tester/axiom:screenshotaxiom-swiftui-navsimulator-tester/axiom:screenshotaxiom-swiftui-navxcrun simctl openurlxcrun simctl openurlimport 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 ?? "") })
}
}
#endifundefinedimport 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 }
// 根据host进行路由
switch url.host {
case "settings":
// 跳转到设置页面
NotificationCenter.default.post(
name: .navigateToSettings,
object: nil
)
case "profile":
// 跳转到个人资料页面
let userID = url.queryItems?["id"] ?? "current"
NotificationCenter.default.post(
name: .navigateToProfile,
object: userID
)
case "reset":
// 将应用重置为初始状态
resetApp()
default:
print("⚠️ 未知调试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 ?? "") })
}
}
#endifundefined
---
---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)
}undefinedimport 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) {
// 先跳转到菜谱页面,再跳转到编辑页面
path.append(Destination.recipe(id: recipeID))
path.append(Destination.recipeEdit(id: recipeID))
}
case "reset":
path = NavigationPath() // 返回根页面
default:
print("⚠️ 未知调试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)
}undefined
---
---#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)")
}
}
}
#endifundefined#if DEBUG
extension DebugRouter {
func handleDebugURL(_ url: URL) {
guard url.scheme == "debug" else { return }
switch url.host {
case "login":
// 展示登录页面
path.append(Destination.login)
case "login-error":
// 展示带错误状态的登录页面
path.append(Destination.login)
// 触发错误状态
NotificationCenter.default.post(
name: .showLoginError,
object: "无效凭据"
)
case "recipe-empty":
// 展示空状态的菜谱列表
UserDefaults.standard.set(true, forKey: "debug_emptyRecipeList")
path.append(Destination.recipes)
case "recipe-error":
// 展示带网络错误的菜谱列表
UserDefaults.standard.set(true, forKey: "debug_networkError")
path.append(Destination.recipes)
default:
print("⚠️ 未知调试URL: \(url)")
}
}
}
#endifundefined
---
---<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>debug</string>
</array>
<key>CFBundleURLName</key>
<string>com.example.debug</string>
</dict>
</array>undefined<key>CFBundleURLTypes</key>
<array>
<dict>
<key>CFBundleURLSchemes</key>
<array>
<string>debug</string>
</array>
<key>CFBundleURLName</key>
<string>com.example.debug</string>
</dict>
</array>undefined/usr/libexec/PlistBuddy -c "Delete :CFBundleURLTypes:0" "${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}" 2>/dev/null || true
**Alternative**: Use separate Info.plist files for Debug vs Release configurations in Build Settings.
---/usr/libexec/PlistBuddy -c "Delete :CFBundleURLTypes:0" "${BUILT_PRODUCTS_DIR}/${INFOPLIST_PATH}" 2>/dev/null || true
**替代方案**:在Build Settings中为Debug和Release配置使用不同的Info.plist文件。
---/axiom:screenshot/axiom:screenshotundefinedundefinedundefinedundefinedsimulator-testersimulator-tester- 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)- 设置页面
- 个人资料页面(带指定用户ID)
- 菜谱详情页面(带指定菜谱ID)
- 错误状态(登录错误、网络错误等)
- 空状态(无菜谱、无收藏等)debug://screen-name # Simple screen navigation
debug://screen-name?param=value # Navigation with parameters
debug://state-name # State configurationdebug://页面名称 # 简单页面跳转
debug://页面名称?参数=值 # 带参数的页面跳转
debug://状态名称 # 状态配置#if DEBUG#if DEBUGundefinedundefined
---
---#if DEBUG
func handleDebugURL(_ url: URL) {
if url.host == "settings" {
// ❌ WRONG — Creates tight coupling
self.showingSettings = true
}
}
#endif#if DEBUG
func handleDebugURL(_ url: URL) {
if url.host == "settings" {
// Use existing NavigationPath
path.append(Destination.settings)
}
}
#endif#if DEBUG
func handleDebugURL(_ url: URL) {
if url.host == "settings" {
// ❌ 错误——造成强耦合
self.showingSettings = true
}
}
#endif#if DEBUG
func handleDebugURL(_ url: URL) {
if url.host == "settings" {
// 使用现有的NavigationPath
path.append(Destination.settings)
}
}
#endif// ❌ WRONG — No #if DEBUG
func handleDebugURL(_ url: URL) {
// This ships to users!
}#if DEBUG
func handleDebugURL(_ url: URL) {
// Stripped from release builds
}
#endif// ❌ 错误——未使用#if DEBUG
func handleDebugURL(_ url: URL) {
// 这段代码会被发布给用户!
}#if DEBUG
func handleDebugURL(_ url: URL) {
// 不会被包含在发布构建中
}
#endif#if DEBUG
case "profile":
let userID = Int(url.queryItems?["id"] ?? "0")! // ❌ Force unwrap
path.append(Destination.profile(id: userID))
#endifid#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#if DEBUG
case "profile":
let userID = Int(url.queryItems?["id"] ?? "0")! // ❌ 强制解包
path.append(Destination.profile(id: userID))
#endifid#if DEBUG
case "profile":
guard let idString = url.queryItems?["id"],
let userID = Int(idString) else {
print("⚠️ 无效的用户ID")
return
}
path.append(Destination.profile(id: userID))
#endif#if DEBUG/axiom:screenshotsimulator-tester#if DEBUG/axiom:screenshotsimulator-testerxcrun simctl openurl booted "debug://recipe-edit?id=42"/axiom:screenshotxcrun simctl openurl booted "debug://recipe-edit?id=42"/axiom:screenshotrouter.path.append(Destination.fromDebugURL(url))router.path.append(Destination.fromDebugURL(url))coordinator.navigate(to: .fromDebugURL(url))coordinator.navigate(to: .fromDebugURL(url))AppRouter.shared.push(Screen.fromDebugURL(url))AppRouter.shared.push(Screen.fromDebugURL(url))#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)
#endifundefined#if DEBUG
case "test-scenario":
// 从URL中解析复杂的测试场景
// 示例: 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"
}
// 跳转到目标页面
path.append(Destination.recipes)
#endifundefined
---
---#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
)
}
#endifundefined#if DEBUG
case "screenshot":
// 解析目标页面和配置
guard let screen = url.queryItems?["screen"] else { return }
// 配置状态
if let state = url.queryItems?["state"] {
applyState(state)
}
// 跳转到目标页面
navigate(to: screen)
// 发送通知以触发外部截图
DispatchQueue.main.asyncAfter(deadline: .now() + 1.0) {
NotificationCenter.default.post(
name: .readyForScreenshot,
object: screen
)
}
#endifundefined
---
---axiom-swiftui-navsimulator-testeraxiom-xcode-debuggingaxiom-swiftui-navsimulator-testeraxiom-xcode-debugging#if DEBUG#if DEBUG