在后台线程上将 Cognito 身份验证状态更改发布到 environmentObject

Publishing Cognito authentication state changes to environmentObject on background thread

我正在开发一个使用 AWS Amplify/Cognito 进行身份验证的 SwiftUI 应用程序。我创建了一个会话对象,用于跟踪用户是否已通过身份验证。这个会话对象是一个 ObservableObject,它被加载到 environmentObject 中并被不同的视图访问。它有一个名为 isLoggedIn 的 @Published 属性。在这个会话对象中,已经创建了一个侦听器来捕获身份验证状态的变化,这些变化会更新 isLoggedIn 的值。代码按预期编译和 运行s 但当用户登录时尝试更新 isLoggedIn 属性 时会生成以下 运行 时间警告:

Publishing changes from background threads is not allowed; make sure to publish values from the main thread (via operators like receive(on:)) on model updates.

我的问题是捕获身份验证状态和设置值的适当方法是什么,以便通过 SwiftUI 的 environmentObject 机制发布它?我可以将我的侦听器移动到 AppDelegate 并从那里更新 environmentObject 中包含的 Session 吗?如果是这样,您如何访问视图之外的环境对象?是否有另一种更简洁的方法来捕获值并将其引入 SwiftUI 的 environmentObjects?我知道我可以 API 调用 Cognito/Amplify 来确定用户的身份验证状态,但这不适合 SwiftUI 的反应模型,或者至少我不知道如何适合它在 :)。

下面显示的是此过程中涉及的代码。第一个代码片段是针对 Session 对象的。第二个显示会话对象被放入 SceneDelegate 中的 enviromentObject。最后一个片段显示了一个视图,其中访问对象以做出渲染决定。

Session.swift

class Swift:ObservableObject {
@Published var firstName: String = ""
@Published var lastName: String = ""
@Published var isLoggedIn: Bool = false


init(){
    AWSMobileClient.default().addUserStateListener(self) { (userState, info) in
        switch (userState) {
        case .guest:
            self.isLoggedIn = false
        case .signedOut:
            self.isLoggedIn = false
        case .signedIn:
            self.isLoggedIn = true
        case .signedOutUserPoolsTokenInvalid:
            self.isLoggedIn = false
        case .signedOutFederatedTokensInvalid:
            self.isLoggedIn = false
        default:
            self.isLoggedIn = false
        }
    }
}

SceneDelegate.swift

...
    let currentSession = Session()
    let mainTabView = MainTabView().environmentObject(currentSession)

...

查看

struct MyView: View {
@EnvironmentObject var currentSession: Session

var body: some View {
    VStack{
        if (self.currentSession.isLoggedIn) {
            Spacer()
            Text("Logged In Content")
            Spacer()
        }
        else{
            LoginJoinView()
        }
    }
}

}

我认为您需要将任何更改已发布属性的代码包装在 DispatchQueue.main.async 中,以便在主线程上传播更改。

DispatchQueue.main.async {
        switch (userState) {
        case .guest:
            self.isLoggedIn = false
        case .signedOut:
            self.isLoggedIn = false
        case .signedIn:
            self.isLoggedIn = true
        case .signedOutUserPoolsTokenInvalid:
            self.isLoggedIn = false
        case .signedOutFederatedTokensInvalid:
            self.isLoggedIn = false
        default:
            self.isLoggedIn = false
        }
}