Swift Combine:发送值后发送完成
Swift Combine: send completion after send value
我正在为我的网络模块进行缓存。我的模块将 return 一个 AnyCancallable
返回给每个请求的调用者。如果缓存数据不可用,我使用 URLSession.dataTaskPublisher
,它适用于 2 个事件:数据接收和完成。如果缓存数据可用,我将使用 CurrentValueSubject
创建 AnyCancallable 到 return。我发送了关于这个主题的两个事件,但在调用方,它只在完成时收到通知,没有数据。
cacheSubject.send(cachedData.data)
cacheSubject.send(completion: Subscribers.Completion<Error>.finished)
去除完成发送,现在可以接收数据,但没有完成通知。
有人可以告诉我我做错了什么吗?
这是完整的文件,以备你们需要:
public class SSNetworkManager {
public static let shared = SSNetworkManager()
private var cache: [String: CachedData] = [:]
private let cacheSubject = CurrentValueSubject<Data, Error>(Data())
@discardableResult public func makeServiceCall<D: Decodable>(forRequest request: SSNetworkRequest<D>, onMainThread: Bool = true) -> AnyPublisher<D, Error>? {
guard let urlRequest = request.urlRequest else {
return nil
}
var cancelable: AnyPublisher<Data, Error>
if let url = urlRequest.url?.absoluteString,
let cachedData = cache[url],
cachedData.isValid {
cancelable = cacheSubject.eraseToAnyPublisher()
cacheSubject.send(cachedData.data)
cacheSubject.send(completion: Subscribers.Completion<Error>.finished)
} else {
cancelable = URLSession.shared.dataTaskPublisher(for: urlRequest).tryMap {[weak self] (data, response) -> Data in
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
throw SSNetworkError(httpCode: (response as? HTTPURLResponse)?.statusCode ?? 0, data: data)
}
if request.shouldCacheNow,
let url = urlRequest.url?.absoluteString {
self?.cache[url] = CachedData(data: data, expirationTime: request.cacheExpirationTime)
}
return data
}.eraseToAnyPublisher()
}
if onMainThread {
return cancelable
.receive(on: RunLoop.main)
.decode(type: D.self, decoder: JSONDecoder())
.eraseToAnyPublisher()
} else {
return cancelable
.decode(type: D.self, decoder: JSONDecoder())
.eraseToAnyPublisher()
}
}
}
fileprivate struct CachedData {
let data: Data
let expirationTime: Date
var isValid: Bool {
return Date().compare(expirationTime) != .orderedDescending
}
}
这不适合使用主题。相反,return 发布者,与每个案例相关:
public class SSNetworkManager {
// ...
public func makeServiceCall<D: Decodable>(
forRequest request: SSNetworkRequest<D>,
onMainThread: Bool = true
) -> AnyPublisher<D, Error>? {
// consider just returning Empty().eraseToAnyPublisher() instead of nil
guard let urlRequest = request.urlRequest else {
return nil
}
var resultPublisher: AnyPublisher<D: Error>
if let url = urlRequest.url?.absoluteString,
let cachedData = cache[url],
cachedData.isValid {
resultPublisher = Just(cachedData.data)
.setFailureType(to: Error.self)
.eraseToAnyPublisher()
} else {
resultPublisher = URLSession.shared
.dataTaskPublisher(for: urlRequest)
.tryMap { ... }
.decode(type: D.self, decoder: JSONDecoder())
.eraseToAnyPublisher()
}
return onMainThread
? resultPublisher
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
: resultPublisher
.eraseToAnyPublisher()
}
}
我正在为我的网络模块进行缓存。我的模块将 return 一个 AnyCancallable
返回给每个请求的调用者。如果缓存数据不可用,我使用 URLSession.dataTaskPublisher
,它适用于 2 个事件:数据接收和完成。如果缓存数据可用,我将使用 CurrentValueSubject
创建 AnyCancallable 到 return。我发送了关于这个主题的两个事件,但在调用方,它只在完成时收到通知,没有数据。
cacheSubject.send(cachedData.data)
cacheSubject.send(completion: Subscribers.Completion<Error>.finished)
去除完成发送,现在可以接收数据,但没有完成通知。
有人可以告诉我我做错了什么吗?
这是完整的文件,以备你们需要:
public class SSNetworkManager {
public static let shared = SSNetworkManager()
private var cache: [String: CachedData] = [:]
private let cacheSubject = CurrentValueSubject<Data, Error>(Data())
@discardableResult public func makeServiceCall<D: Decodable>(forRequest request: SSNetworkRequest<D>, onMainThread: Bool = true) -> AnyPublisher<D, Error>? {
guard let urlRequest = request.urlRequest else {
return nil
}
var cancelable: AnyPublisher<Data, Error>
if let url = urlRequest.url?.absoluteString,
let cachedData = cache[url],
cachedData.isValid {
cancelable = cacheSubject.eraseToAnyPublisher()
cacheSubject.send(cachedData.data)
cacheSubject.send(completion: Subscribers.Completion<Error>.finished)
} else {
cancelable = URLSession.shared.dataTaskPublisher(for: urlRequest).tryMap {[weak self] (data, response) -> Data in
guard let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 200 else {
throw SSNetworkError(httpCode: (response as? HTTPURLResponse)?.statusCode ?? 0, data: data)
}
if request.shouldCacheNow,
let url = urlRequest.url?.absoluteString {
self?.cache[url] = CachedData(data: data, expirationTime: request.cacheExpirationTime)
}
return data
}.eraseToAnyPublisher()
}
if onMainThread {
return cancelable
.receive(on: RunLoop.main)
.decode(type: D.self, decoder: JSONDecoder())
.eraseToAnyPublisher()
} else {
return cancelable
.decode(type: D.self, decoder: JSONDecoder())
.eraseToAnyPublisher()
}
}
}
fileprivate struct CachedData {
let data: Data
let expirationTime: Date
var isValid: Bool {
return Date().compare(expirationTime) != .orderedDescending
}
}
这不适合使用主题。相反,return 发布者,与每个案例相关:
public class SSNetworkManager {
// ...
public func makeServiceCall<D: Decodable>(
forRequest request: SSNetworkRequest<D>,
onMainThread: Bool = true
) -> AnyPublisher<D, Error>? {
// consider just returning Empty().eraseToAnyPublisher() instead of nil
guard let urlRequest = request.urlRequest else {
return nil
}
var resultPublisher: AnyPublisher<D: Error>
if let url = urlRequest.url?.absoluteString,
let cachedData = cache[url],
cachedData.isValid {
resultPublisher = Just(cachedData.data)
.setFailureType(to: Error.self)
.eraseToAnyPublisher()
} else {
resultPublisher = URLSession.shared
.dataTaskPublisher(for: urlRequest)
.tryMap { ... }
.decode(type: D.self, decoder: JSONDecoder())
.eraseToAnyPublisher()
}
return onMainThread
? resultPublisher
.receive(on: DispatchQueue.main)
.eraseToAnyPublisher()
: resultPublisher
.eraseToAnyPublisher()
}
}