首页 > 解决方案 > 如何仅在接收数据时制作已发布的属性调用映射

问题描述

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

为此,我有一个控制登录过程的 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调用委托后)进行映射。我怎样才能做到这一点?

标签: swiftswiftuicombine

解决方案


刚刚想出了这个问题的解决方案。在我的情况下要使用的正确数据类型是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
}

以这种方式使地图仅在主题收到事件时才被调用。


推荐阅读