如何仅在接收数据时制作已发布的属性调用映射

Posted

技术标签:

【中文标题】如何仅在接收数据时制作已发布的属性调用映射【英文标题】:How to make a Published property call map only when it receives data 【发布时间】:2021-06-02 11:12:49 【问题描述】:

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

当我点击登录按钮时,应用会触发 GoogleSignIn sign 函数 然后,当在 GoogleDelegate 上接收到会话数据时,它需要向我的服务器端登录方法发起请求。

为此,我有一个控制登录过程的 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 委托之后)才应该映射它。我该怎么做?

【问题讨论】:

【参考方案1】:

刚刚想出了解决这个问题的方法。在我的例子中使用的正确数据类型是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

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

【讨论】:

以上是关于如何仅在接收数据时制作已发布的属性调用映射的主要内容,如果未能解决你的问题,请参考以下文章

仅在有新邮件到达时接收 Gmail 推送通知

ajax对象。同步与异步及ajax发送请求

04AJAX接收服务器返回的数据

当通过网络接收的数据更改属性时,如何将控件绑定到属性更改?

仅在父状态更新后如何接收道具?

STM32F4 - CAN总线发送每次都成功,但CAN接收仅在第一次调用时成功