我是 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/