有没有办法检测发布者何时有新订阅者? | Swift, 合并
Is there a way to detect when a publisher has a new subscriber? | Swift, Combine
我正在开发一个带有 API 调用的 MVVM 结构。
我现在有这个结构:
//Get publisher
loginPublisher = LoginService.generateLoginPublisher()
//Create a subscriber
loginSubscriber = loginPublisher!
.sink { error in
print("Something bad happened")
self.isLoading = false
} receiveValue: { value in
self.saveClient(value)
self.client = value
self.isLoading = false
}
//Asking service to start assync task and notify its result on publisher
LoginService.login(email, password, loginPublisher!)
基本上我所做的是从 LoginService
获取某个发布者,然后我在 loginPublisher
上创建一个订阅者,然后我告诉 LoginService
做一些异步逻辑并发送它结果 loginPublisher
这样我用 loginSubscriber
.
管理发送的数据
我想在执行 LoginService.generateLoginPublisher()
时在内部执行 LoginService.login(),但如果我这样做,LoginService.login()
逻辑有可能在我创建 loginSubscriber
,这就是为什么我被迫控制何时调用 LoginService.login()
。
当发布者有新订阅者时,如何从 LoginService 检测到?
这是我的 LoginService
class:
class LoginService{
static func generateLoginPublisher() -> PassthroughSubject<Client, NetworkError>{
return PassthroughSubject<Client, NetworkError>()
}
static func login(_ email: String,_ password: String,_ loginPublisher: PassthroughSubject<Client, NetworkError>){
let url = NetworkBuilder.getApiUrlWith(extraPath: "login")
print(url)
let parameters: [String: String] = [
"password": password,
"login": email
]
print(parameters)
let request = AF.request(
url, method: .post,
parameters: parameters,
encoder: JSONParameterEncoder.default
)
request.validate(statusCode: 200...299)
request.responseDecodable(of: Client.self) { response in
if let loginResponse = response.value{//Success
loginPublisher.send(loginResponse)
}
else{//Failure
loginPublisher.send(completion: Subscribers.Completion<NetworkError>.failure(.thingsJustHappen))
}
}
}
}
您可以处理发布者事件并注入副作用来跟踪订阅,例如
class LoginService {
static func generateLoginPublisher(onSubscription: @escaping (Subscription) -> Void) -> AnyPublisher<Client, NetworkError> {
return PassthroughSubject<Client, NetworkError>()
.handleEvents(receiveSubscription: onSubscription, receiveOutput: nil, receiveCompletion: nil, receiveCancel: nil, receiveRequest: nil)
.eraseToAnyPublisher()
}
}
所以
loginPublisher = LoginService.generateLoginPublisher() { subscription in
// do anything needed here when on new subscription appeared
}
如果您想完全控制订阅,您可以创建自定义 Publisher
和 Subscription
。
Publisher
的 func receive<S: Subscriber>(subscriber: S)
方法是发布者收到新订阅者时调用的方法。
如果你只是想在发生这种情况时发出网络请求,你只需要创建一个自定义 Publisher
和 return 一个 Future
来包装来自这个方法的网络请求.
一般来说,您应该将 Future
用于一次性异步事件,PassthroughSubject
不适合用于网络请求 Publisher
。
我终于使用 Future
而不是 Dávid Pásztor 建议的 PassthroughtSubject
解决了我的问题。使用 Future
我不必担心 LoginService.login()
在创建 loginSubscriber
.
之前逻辑完成
LoginSevice.login()
方法:
static func login(_ email: String,_ password: String) -> Future<Client, NetworkError>{
return Future<Client, NetworkError>{ completion in
let url = NetworkBuilder.getApiUrlWith(extraPath: "login")
print(url)
let parameters: [String: String] = [
"password": password,
"login": email
]
print(parameters)
let request = AF.request(
url, method: .post,
parameters: parameters,
encoder: JSONParameterEncoder.default
)
request.validate(statusCode: 200...299)
request.responseDecodable(of: Client.self) { response in
if let loginResponse = response.value{//Success
completion(.success(loginResponse))
}
else{//Failure
completion(.failure(NetworkError.thingsJustHappen))
}
}
}
}
实施:
loginSubscriber = LoginService.login(email, password)
.sink { error in
print("Something bad happened")
self.isLoading = false
} receiveValue: { value in
self.saveClient(value)
self.client = (value)
self.isLoading = false
}
我正在开发一个带有 API 调用的 MVVM 结构。 我现在有这个结构:
//Get publisher
loginPublisher = LoginService.generateLoginPublisher()
//Create a subscriber
loginSubscriber = loginPublisher!
.sink { error in
print("Something bad happened")
self.isLoading = false
} receiveValue: { value in
self.saveClient(value)
self.client = value
self.isLoading = false
}
//Asking service to start assync task and notify its result on publisher
LoginService.login(email, password, loginPublisher!)
基本上我所做的是从 LoginService
获取某个发布者,然后我在 loginPublisher
上创建一个订阅者,然后我告诉 LoginService
做一些异步逻辑并发送它结果 loginPublisher
这样我用 loginSubscriber
.
我想在执行 LoginService.generateLoginPublisher()
时在内部执行 LoginService.login(),但如果我这样做,LoginService.login()
逻辑有可能在我创建 loginSubscriber
,这就是为什么我被迫控制何时调用 LoginService.login()
。
当发布者有新订阅者时,如何从 LoginService 检测到?
这是我的 LoginService
class:
class LoginService{
static func generateLoginPublisher() -> PassthroughSubject<Client, NetworkError>{
return PassthroughSubject<Client, NetworkError>()
}
static func login(_ email: String,_ password: String,_ loginPublisher: PassthroughSubject<Client, NetworkError>){
let url = NetworkBuilder.getApiUrlWith(extraPath: "login")
print(url)
let parameters: [String: String] = [
"password": password,
"login": email
]
print(parameters)
let request = AF.request(
url, method: .post,
parameters: parameters,
encoder: JSONParameterEncoder.default
)
request.validate(statusCode: 200...299)
request.responseDecodable(of: Client.self) { response in
if let loginResponse = response.value{//Success
loginPublisher.send(loginResponse)
}
else{//Failure
loginPublisher.send(completion: Subscribers.Completion<NetworkError>.failure(.thingsJustHappen))
}
}
}
}
您可以处理发布者事件并注入副作用来跟踪订阅,例如
class LoginService {
static func generateLoginPublisher(onSubscription: @escaping (Subscription) -> Void) -> AnyPublisher<Client, NetworkError> {
return PassthroughSubject<Client, NetworkError>()
.handleEvents(receiveSubscription: onSubscription, receiveOutput: nil, receiveCompletion: nil, receiveCancel: nil, receiveRequest: nil)
.eraseToAnyPublisher()
}
}
所以
loginPublisher = LoginService.generateLoginPublisher() { subscription in
// do anything needed here when on new subscription appeared
}
如果您想完全控制订阅,您可以创建自定义 Publisher
和 Subscription
。
Publisher
的 func receive<S: Subscriber>(subscriber: S)
方法是发布者收到新订阅者时调用的方法。
如果你只是想在发生这种情况时发出网络请求,你只需要创建一个自定义 Publisher
和 return 一个 Future
来包装来自这个方法的网络请求.
一般来说,您应该将 Future
用于一次性异步事件,PassthroughSubject
不适合用于网络请求 Publisher
。
我终于使用 Future
而不是 Dávid Pásztor 建议的 PassthroughtSubject
解决了我的问题。使用 Future
我不必担心 LoginService.login()
在创建 loginSubscriber
.
LoginSevice.login()
方法:
static func login(_ email: String,_ password: String) -> Future<Client, NetworkError>{
return Future<Client, NetworkError>{ completion in
let url = NetworkBuilder.getApiUrlWith(extraPath: "login")
print(url)
let parameters: [String: String] = [
"password": password,
"login": email
]
print(parameters)
let request = AF.request(
url, method: .post,
parameters: parameters,
encoder: JSONParameterEncoder.default
)
request.validate(statusCode: 200...299)
request.responseDecodable(of: Client.self) { response in
if let loginResponse = response.value{//Success
completion(.success(loginResponse))
}
else{//Failure
completion(.failure(NetworkError.thingsJustHappen))
}
}
}
}
实施:
loginSubscriber = LoginService.login(email, password)
.sink { error in
print("Something bad happened")
self.isLoading = false
} receiveValue: { value in
self.saveClient(value)
self.client = (value)
self.isLoading = false
}