之前在用 SwiftUI 写 AbnormalMouse app 的时候就遇到了这么一个问题,每当 SwiftUI preview 工作的时候,整个 app 都会被运行起来,这样一来就会导致:

  • 一些启动时运行的代码会被执行。比如说我有一段代码是打开 app 时检测 app 是否在 Applications 文件夹内,如果不在就会提示用户移动 app,这个弹窗就会时不时弹出来。
  • 一些页面 onAppear 时执行的代码会被执行。之前我就遇到了一个“怎么样改 preview 的参数都没有效果”的问题,最后发现是 onApear 时参数被 UserDefaults 中的数据覆盖了。

这就很烦。

第一个问题我的解决方法是创建一个新的 scheme 叫做 XXXPreview,这个 scheme 在 build 的时候会执行另外一个 build configuration,而这个 build configuration 会自带一个 compilation condition 方便我们检测。在用 preview 调试的阶段我们切换到这个 scheme 就能避免不需要的代码执行了。

1
2
3
4
5
6
7
8
9
10
11
func applicationDidFinishLaunching(_: Notification) {
#if !PREVIEW
defer { persisted.launchCount += 1 }
purchaseManager.startTrialIfNeeded()
killLauncherIfNeeded()
setupStartAtLoginIfNeeded()
startupPurchaseManager()
observeForSleeps()
presentWindowIfNeeded()
#endif
}

当然也可以通过添加 environment variable 实现。

第二个问题当然也可以通过上述方法解决,但我实际做的是将原本的一个 view 拆分成了两个部分:

1
2
3
4
5
6
7
8
9
10
11
12
struct MoveToScrollSettingsScreen: View {
let store: MoveToScrollDomain.Store

var body: some View {
MoveToScrollSettingsView(store: store)
.onAppear {
// load data!
}
}
}

private struct MoveToScrollSettingsView: View {...}

实际 preview 的是 MoveToScrollSettingsView,自然就不会触发初始化的代码了。

至于我为什么选择了这个方法,可能是因为我总是忘记切换 scheme 吧……