Skip to content

Instantly share code, notes, and snippets.

@ricardopsantos
Last active January 14, 2023 19:18
Show Gist options
  • Save ricardopsantos/ce6a2f2eb4444fc61e73c2ae6ae5416d to your computer and use it in GitHub Desktop.
Save ricardopsantos/ce6a2f2eb4444fc61e73c2ae6ae5416d to your computer and use it in GitHub Desktop.
Article_13_G7.swift
public typealias GenericRequestWithCacheResponse<T1: Codable, E1: Error> = AnyPublisher<T1, E1>
public extension NetworkinNameSpace.NetworkingUtils {
static func genericRequestWithCache<T1: Codable, E1: Error, T2: Codable, E2: Error>(
_ publisher: AnyPublisher<T2, E2>,
_ type: T1.Type,
_ cachePolicy: CachePolicy,
_ serviceKey: String,
_ serviceParams: [String],
_ cacheManager: CodableCacheManagerProtocol = SimpleCacheManagerForCodable.shared) -> GenericRequestWithCacheResponse<T1, E1> {
let lock = {
AvailabilityState.lockForServiceKey(serviceKey)
}
let unlock = {
AvailabilityState.unLockForServiceKey(serviceKey)
}
// Fetch for CACHED data
func cacheDontLoad() -> GenericRequestWithCacheResponse<T1, E1> {
if let storedModel = cacheManager.syncRetrieve(type,
key: serviceKey,
params: serviceParams) {
return Just(storedModel.model).setFailureType(to: E1.self).eraseToAnyPublisher()
} else {
return .empty()
}
}
// Fetch for NEW data
func noCacheDoLoad() -> GenericRequestWithCacheResponse<T1, E1> {
lock()
return publisher.onErrorComplete()
.flatMap({ model -> GenericRequestWithCacheResponse<T1, E1> in
cacheManager.syncStore(model, key: serviceKey, params: serviceParams, timeToLiveMinutes: nil)
unlock()
if let model = model as? T1 {
return Just(model).setFailureType(to: E1.self).eraseToAnyPublisher()
} else {
return .empty()
}
})
.catch({ error -> GenericRequestWithCacheResponse<T1, E1> in
unlock()
return Fail(error: error).eraseToAnyPublisher()
}).eraseToAnyPublisher()
}
// Fetch for NEW data, OR wait for cache data to change and
func noCacheDoLoadOrWait() -> GenericRequestWithCacheResponse<T1, E1> {
switch AvailabilityState.serviceStates[serviceKey]?.value ?? .free {
case .free: return noCacheDoLoad()
case .refreshing: return awaitForCache()
}
}
// Theres allready a request on the way. Lets wait for it to end and then return the cached value
func awaitForCache() -> GenericRequestWithCacheResponse<T1, E1> {
return AvailabilityState.serviceStates[serviceKey]!.filter({ $0 == .free })
.flatMap { _ in return
cacheDontLoad()
}.eraseToAnyPublisher()
}
switch cachePolicy {
case .ignoringCache:
return noCacheDoLoadOrWait()
case .cacheElseLoad:
return cacheDontLoad().catch { _ -> GenericRequestWithCacheResponse<T1, E1> in
return noCacheDoLoadOrWait()
}.eraseToAnyPublisher()
case .cacheAndLoad:
let cacheDontLoad = cacheDontLoad().onErrorComplete().setFailureType(to: E1.self).eraseToAnyPublisher()
return Publishers.Merge(cacheDontLoad, noCacheDoLoadOrWait()).eraseToAnyPublisher()
case .cacheDontLoad:
return cacheDontLoad()
}
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment