ReactiveCocoa 4 - 创建一个只运行一次的依赖登录请求
ReactiveCocoa 4 - Creating a dependent login request that runs only once
过去我主要使用 ReactiveCocoa 来简单地绑定视图和视图模型,现在我想尝试一下并在整个新项目中使用它,但我无法理解几件事。
我想做的是这个 -
- 有一个网络登录请求,每个其他网络请求都需要首先调用。
- 如果发出多个请求,它们都需要等到登录完成(例如,假设我有一个选项卡栏控制器,我在登录完成之前在它们之间快速点击;我不想要多个已触发登录请求。)
我花了一些时间探索一些选项,例如队列,研究 flatMap(.Latest)
之类的东西,但如果我完全诚实的话 - 我不知道自己在做什么! :S
下面是一个非常基本的、愚蠢的实现,它很快被拼凑在一起,很可能实现得很糟糕。如果有人可以给我一些我需要更改的指示,将不胜感激。我的 doSomething
方法显然首先登录,但如果一次进行多个调用,它们不会等到第一个调用完成,因为我需要它们。
我可以用 loginValid
属性 做些什么吗?
(此外,关于我应该如何构建这些东西的一般指示会很棒 - 我确信我用这段代码做了很多愚蠢的事情)
谢谢!
class FakeBackend: BackendType {
private var loginResponse = MutableProperty<LoginResponse?>(nil)
private let loginValid = MutableProperty<Bool>(false)
private var loginProducer: SignalProducer<LoginResponse, NSError>! // <-- implicitly unwrapped optional? Yuck
init() {
loginValid <~ loginResponse.producer.map { [=10=] != nil }
loginProducer = SignalProducer { [weak self] observer, disposable in
guard let _self = self else { return }
if let loginResponse = _self.loginResponse.value {
print("Already have login details")
observer.sendNext(loginResponse)
observer.sendCompleted()
} else {
print("Don't have login details, go get them")
_self.logIn().start(observer)
}
}
}
func doSomething() -> SignalProducer<HomeResponse, NSError> {
return loginProducer
.then(SignalProducer<HomeResponse, NSError> { observer, dispoable in
let homeResponse = HomeResponse(title: "My title is this")
observer.sendNext(homeResponse)
observer.sendCompleted()
})
}
private func logIn() -> SignalProducer<LoginResponse, NSError> {
return SignalProducer { observer, disposable in
print("Calling network login")
delayToMainThread(1.0, closure: { [weak self] () -> () in
guard let _self = self else { return }
let loginResponse = LoginResponse(accessToken: "MyAccessToken")
_self.loginResponse.value = loginResponse
observer.sendNext(loginResponse)
observer.sendCompleted()
})
}
}
}
正确的方法是通过 replayLazily
,在 https://spin.atomicobject.com/2014/06/29/replay-replaylast-replaylazily/
中有描述
The -replayLazily convenience method returns a new signal that, when
subscribed to, will immediately send the subscriber the entire history
of values that have come through the source signal, without
re-executing the source signal’s subscription code.
我在 ReactiveCocoa Github 问题页面上收到了对我问题的回复。 https://github.com/ReactiveCocoa/ReactiveCocoa/issues/2706
本质上,您想做这样的事情 -
class FakeBackend: BackendType {
private var login: SignalProducer<LoginResponse, NSError>
init() {
login = SignalProducer { observer, disposable in
print("Logging in...")
// we'd actually make a network call here, but for demo purposes
// let's just return some dummy data.
let loginResponse = LoginResponse(accessToken: "MyAccessToken")
print("Logged in!")
observer.sendNext(loginResponse)
observer.sendCompleted()
}.replayLazily(1)
}
func loadHomeScreen() -> SignalProducer<HomeResponse, NSError> {
return login
.flatMap(.Latest, transform: homeResponse)
}
private func homeResponse(loginResponse: LoginResponse) -> SignalProducer<HomeResponse, NSError> {
return SignalProducer<HomeResponse, NSError> { observer, disposable in
print("Aaaand, we've gotten our HomeResponse.")
let homeResponse = HomeResponse(title: "My title is this")
observer.sendNext(homeResponse)
observer.sendCompleted()
}
}
}
过去我主要使用 ReactiveCocoa 来简单地绑定视图和视图模型,现在我想尝试一下并在整个新项目中使用它,但我无法理解几件事。
我想做的是这个 -
- 有一个网络登录请求,每个其他网络请求都需要首先调用。
- 如果发出多个请求,它们都需要等到登录完成(例如,假设我有一个选项卡栏控制器,我在登录完成之前在它们之间快速点击;我不想要多个已触发登录请求。)
我花了一些时间探索一些选项,例如队列,研究 flatMap(.Latest)
之类的东西,但如果我完全诚实的话 - 我不知道自己在做什么! :S
下面是一个非常基本的、愚蠢的实现,它很快被拼凑在一起,很可能实现得很糟糕。如果有人可以给我一些我需要更改的指示,将不胜感激。我的 doSomething
方法显然首先登录,但如果一次进行多个调用,它们不会等到第一个调用完成,因为我需要它们。
我可以用 loginValid
属性 做些什么吗?
(此外,关于我应该如何构建这些东西的一般指示会很棒 - 我确信我用这段代码做了很多愚蠢的事情)
谢谢!
class FakeBackend: BackendType {
private var loginResponse = MutableProperty<LoginResponse?>(nil)
private let loginValid = MutableProperty<Bool>(false)
private var loginProducer: SignalProducer<LoginResponse, NSError>! // <-- implicitly unwrapped optional? Yuck
init() {
loginValid <~ loginResponse.producer.map { [=10=] != nil }
loginProducer = SignalProducer { [weak self] observer, disposable in
guard let _self = self else { return }
if let loginResponse = _self.loginResponse.value {
print("Already have login details")
observer.sendNext(loginResponse)
observer.sendCompleted()
} else {
print("Don't have login details, go get them")
_self.logIn().start(observer)
}
}
}
func doSomething() -> SignalProducer<HomeResponse, NSError> {
return loginProducer
.then(SignalProducer<HomeResponse, NSError> { observer, dispoable in
let homeResponse = HomeResponse(title: "My title is this")
observer.sendNext(homeResponse)
observer.sendCompleted()
})
}
private func logIn() -> SignalProducer<LoginResponse, NSError> {
return SignalProducer { observer, disposable in
print("Calling network login")
delayToMainThread(1.0, closure: { [weak self] () -> () in
guard let _self = self else { return }
let loginResponse = LoginResponse(accessToken: "MyAccessToken")
_self.loginResponse.value = loginResponse
observer.sendNext(loginResponse)
observer.sendCompleted()
})
}
}
}
正确的方法是通过 replayLazily
,在 https://spin.atomicobject.com/2014/06/29/replay-replaylast-replaylazily/
The -replayLazily convenience method returns a new signal that, when subscribed to, will immediately send the subscriber the entire history of values that have come through the source signal, without re-executing the source signal’s subscription code.
我在 ReactiveCocoa Github 问题页面上收到了对我问题的回复。 https://github.com/ReactiveCocoa/ReactiveCocoa/issues/2706
本质上,您想做这样的事情 -
class FakeBackend: BackendType {
private var login: SignalProducer<LoginResponse, NSError>
init() {
login = SignalProducer { observer, disposable in
print("Logging in...")
// we'd actually make a network call here, but for demo purposes
// let's just return some dummy data.
let loginResponse = LoginResponse(accessToken: "MyAccessToken")
print("Logged in!")
observer.sendNext(loginResponse)
observer.sendCompleted()
}.replayLazily(1)
}
func loadHomeScreen() -> SignalProducer<HomeResponse, NSError> {
return login
.flatMap(.Latest, transform: homeResponse)
}
private func homeResponse(loginResponse: LoginResponse) -> SignalProducer<HomeResponse, NSError> {
return SignalProducer<HomeResponse, NSError> { observer, disposable in
print("Aaaand, we've gotten our HomeResponse.")
let homeResponse = HomeResponse(title: "My title is this")
observer.sendNext(homeResponse)
observer.sendCompleted()
}
}
}