swift - 如何让Published属性只在收到数据时调用map

标签 swift swiftui combine

我是 Combine 的新手,我正在尝试创建一个登录流程,其中:

  • 当我点击登录按钮时,应用会触发 GoogleSignIn sign 函数
  • 然后,当 GoogleDelegate 收到 session 数据时,它需要启动对我的服务器端登录方法的请求。

为此,我有一个控制登录过程的 GoogleSignInController:

class GoogleSignInController: GIDSignInDelegate {
    @Published var onSessionAcquired: GoogleSignInSessionData = ("", "")

    func signIn() {
        if (GIDSignIn.sharedInstance()?.presentingViewController == nil) {
            GIDSignIn.sharedInstance()?.presentingViewController = UIApplication.shared.windows.last?.rootViewController
        }
        GIDSignIn.sharedInstance()?.signIn()
    }

    func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
        if let error = error {
            if (error as NSError).code == GIDSignInErrorCode.hasNoAuthInKeychain.rawValue {
                print("The user has not signed in")
            } else {
                print("\(error.localizedDescription)")
            }
            return
        }
        
        onSessionAcquired = GoogleSignInSessionData(idToken: user.authentication.idToken ?? "", serverAuthCode: user.serverAuthCode ?? "")
        
        print("Successful sign-in!")
        signedIn = true
    }
}

然后在 ViewModel 中,我使用一些状态来控制过程:

    static func whenStartedLogin() -> Feedback<State, Event> {
        Feedback { (state: State) -> AnyPublisher<Event, Never> in
            guard case .loadingGoogleSignIn = state else { return Empty().eraseToAnyPublisher() }

            GoogleSignInController.shared.signIn()
            
            return GoogleSignInController.shared.$onSessionAcquired
                .receive(on: DispatchQueue.main)
                .map { Event.onGoogleSessionAcquired($0) }
                .eraseToAnyPublisher()
        }
    }

    static func whenReceivedGoogleSession() -> Feedback<State, Event> {
        Feedback { (state: State) -> AnyPublisher<Event, Never> in
            guard case let .loadingServerSession(googleSession) = state else { return Empty().eraseToAnyPublisher() }

            return self.signIn(idToken: googleSession.idToken, serverAuthCode: googleSession.serverAuthCode)
              .map { (userInfo, error) in
                  if let error = error {
                      return Event.onFailedToAcquireSession(error)
                  } else if let userInfo = userInfo {
                      return Event.onUserInfoAcquired(userInfo)
                  } else {
                      return Event.onFailedToAcquireSession(AppError.unknown)
                  }
              }
              .eraseToAnyPublisher()
        }
    }

问题是:当我点击登录按钮时,whenStatedLogin 函数被触发,它将我的事件映射到 Event.onGoogleSessionAcquired。这应该仅在 onSessionAcquired 更改时映射(在调用 sign 委托(delegate)之后)。我该怎么做?

最佳答案

刚想出这个问题的解决方案。在我的案例中要使用的正确数据类型是 PassthroughSubject

let sessionSubject = PassthroughSubject<GoogleSignInSessionData, Never>()

...

func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
    if let error = error {
        if (error as NSError).code == GIDSignInErrorCode.hasNoAuthInKeychain.rawValue {
            print("The user has not signed in")
        } else {
            print("\(error.localizedDescription)")
        }
        return
    }
    
    sessionSubject.send(GoogleSignInSessionData(idToken: user.authentication.idToken ?? "", serverAuthCode: user.serverAuthCode ?? ""))
    
    print("Successful sign-in!")
    signedIn = true
}

采用这种方式将使 map 仅在主题接收到事件时调用。

关于swift - 如何让Published属性只在收到数据时调用map,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67804019/

相关文章:

swift - 使用通用数组类型规范扩展结构

ios - 如何解决 - 合并错误 |错误域=NSURLErrorDomain 代码=-999 "cancelled"

ios - 如何在 SwiftUI 中将自定义字体系列设置为整个应用程序的默认值

swift - 来自 ObservableObject 的绑定(bind)值

swift - 组合 flatMap 不返回预期的上下文结果类型

ios - 如何使用Combine的AsyncPublisher解决内存泄漏

Swift - 带三角形的模糊圆圈 - 模糊效果始终在顶部

ios - Swift 中的 UITextField 自定义下划线

arrays - Swift:如何按特定顺序将项目插入数组?

ios - SwiftUI:动态设置图像的大小