Я работаю над кешированием для своего сетевого модуля. Мой модуль будет возвращать AnyCancallable
вызывающей стороне для каждого запроса. Если кэшированные данные недоступны, я использую URLSession.dataTaskPublisher
, он отлично работает с двумя событиями: получение данных и завершение. Если доступны кэшированные данные, я буду использовать CurrentValueSubject
для создания AnyCancallable для возврата. Я отправляю оба события по этой теме, но на стороне вызывающего абонента он получает уведомление только о завершении, без данных.
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
}
}