SwiftUI:在 launch/enter 前景上显示 "lock screen"?

SwiftUI: Show "lock screen" on launch/enter foreground?

我正在 SwiftUI 中开发一个 iOS 应用程序,它需要 lock/unlock 来自钥匙串的加密密钥才能执行任何操作。我希望在使用应用程序时将这些键保存在内存中,然后在用户切换到另一个应用程序或进入后台时从内存中删除。当应用程序启动或回到前台时,我想提示他们输入密码以解锁密钥。

我过去在使用 UIKit 时做过类似的事情,但这个应用程序是用 SwiftUI 编写的。我的应用程序的根是一个 TabView,每个选项卡中显示许多其他 View。我希望这个“锁定屏幕”弹出,而不管当时哪个 View 恰好处于活动状态。我知道我可以在 UIApplicationDelegate 中订阅许多 Notification。这可以处理从内存中删除密钥的任务。我不知道如何在当时可能处于活动状态的任何其他 View 之上显示锁定屏幕 View

是否有“标准”SwiftUI 方法来完成此操作?我想尽可能地遵守 SwiftUI 最佳实践,但还没有看到任何关于如何做这样的事情的好例子。

最简单的版本是使用 if/else 块根据 @State 变量有条件地显示锁定屏幕:

struct ContentView: View {
    @State var showLock = false
    
    var body: some View {
        Group {
            if showLock {
                Text("Locked")
            } else {
                Text("Tab view here")
            }
        }.onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)) { _ in
            showLock = true
        }
    }
}

上述方法的缺点是,当显示锁定屏幕时,您可能会丢失选项卡视图中的某些状态。

要保持​​选项卡视图加载并保持您的状态,您可以使用显示在其他内容之上的 fullScreenCover

struct ContentView: View {
    @State var showLock = false
    
    var body: some View {
        Text("Tab view here")
        .fullScreenCover(isPresented: $showLock) {
            VStack {
                Text("Locked")
                Button("Hide") {
                    showLock = false
                }
            }
        }
        .onReceive(NotificationCenter.default.publisher(for: UIApplication.willResignActiveNotification)) { _ in
            showLock = true
        }
    }
}

显然,存储状态 (locked/unlocked) 的位置无关紧要——您可以将其存储在 ObservableObject、环境键等中——SwiftUI 可以看到它更新的任何地方。获取 background/foreground 通知的策略相同——任何设置状态的策略都是可行的。