-
Notifications
You must be signed in to change notification settings - Fork 50
/
Copy pathNetworkCachePlugin.swift
155 lines (137 loc) · 6.38 KB
/
NetworkCachePlugin.swift
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
//
// NetworkCachePlugin.swift
// RxNetworks
//
// Created by Condy on 2021/10/6.
// https://github.com/yangKJ/RxNetworks
import Foundation
import Moya
import CacheX
import Booming
/// Network cache plugin type
public enum NetworkCacheType {
/** 只从网络获取数据,且数据不会缓存在本地 */
/** Only get data from the network, and the data will not be cached locally */
case ignoreCache
/** 先从网络获取数据,同时会在本地缓存数据 */
/** Get the data from the network first, and cache the data locally at the same time */
case networkOnly
/** 先从缓存读取数据,如果没有再从网络获取 */
/** Read the data from the cache first, if not, then get it from the network */
case cacheElseNetwork
/** 先从网络获取数据,如果没有在从缓存获取,此处的没有可以理解为访问网络失败,再从缓存读取 */
/** Get data from the network first, if not from the cache */
case networkElseCache
/** 先从缓存读取数据,然后在从网络获取并且缓存,可能会获取到两次数据 */
/** Data is first read from the cache, then retrieved from the network and cached, Maybe get `twice` data */
case cacheThenNetwork
}
/// 缓存插件,基于`CacheX`封装使用
/// Cache plugin, based on `CacheX` package use
public struct NetworkCachePlugin {
public let options: NetworkCachePlugin.Options
public init(options: NetworkCachePlugin.Options = .ignoreCache) {
self.options = options
}
public init(cacheType: NetworkCacheType = .ignoreCache) {
let options = Options(cacheType: cacheType)
self.init(options: options)
}
}
extension NetworkCachePlugin {
public struct Options {
/// Network cache plugin type
let cacheType: NetworkCacheType
/// Encryption type, default md5
let cryptoType: CacheX.CryptoType
/// Storage type, default disk and memory.
let cachedOptions: CacheX.CachedOptions
public init(cacheType: NetworkCacheType, cryptoType: CryptoType = .md5, cachedOptions: CachedOptions = .diskAndMemory) {
self.cacheType = cacheType
self.cryptoType = cryptoType
self.cachedOptions = cachedOptions
}
}
}
extension NetworkCachePlugin.Options {
/** 只从网络获取数据,且数据不会缓存在本地 */
/** Only get data from the network, and the data will not be cached locally */
public static let ignoreCache: NetworkCachePlugin.Options = .init(cacheType: .ignoreCache)
/** 先从网络获取数据,同时会在本地缓存数据 */
/** Get the data from the network first, and cache the data locally at the same time */
public static let networkOnly: NetworkCachePlugin.Options = .init(cacheType: .networkOnly)
/** 先从缓存读取数据,如果没有再从网络获取 */
/** Read the data from the cache first, if not, then get it from the network */
public static let cacheElseNetwork: NetworkCachePlugin.Options = .init(cacheType: .cacheElseNetwork)
/** 先从网络获取数据,如果没有在从缓存获取,此处的没有可以理解为访问网络失败,再从缓存读取 */
/** Get data from the network first, if not from the cache */
public static let networkElseCache: NetworkCachePlugin.Options = .init(cacheType: .networkElseCache)
/** 先从缓存读取数据,然后在从网络获取并且缓存,可能会获取到两次数据 */
/** Data is first read from the cache, then retrieved from the network and cached, Maybe get `twice` data */
public static let cacheThenNetwork: NetworkCachePlugin.Options = .init(cacheType: .cacheThenNetwork)
}
extension NetworkCachePlugin: PluginSubType {
public var pluginName: String {
return "CacheX"
}
public func configuration(_ request: HeadstreamRequest, target: TargetType) -> HeadstreamRequest {
switch options.cacheType {
case .cacheElseNetwork, .cacheThenNetwork:
if let response = self.readCacheResponse(target) {
request.result = .success(response)
}
if options.cacheType == .cacheElseNetwork {
request.endRequest = true
}
default:
break
}
return request
}
public func didReceive(_ result: Result<Moya.Response, MoyaError>, target: TargetType) {
saveCacheResponse(result: result, target: target)
}
public func process(_ result: Result<Moya.Response, MoyaError>, target: TargetType) -> Result<Moya.Response, MoyaError> {
switch options.cacheType {
case .networkElseCache:
switch result {
case .success:
return result
case .failure:
if let response = self.readCacheResponse(target) {
return .success(response)
}
}
default:
break
}
return result
}
}
extension NetworkCachePlugin {
private func readCacheResponse(_ target: TargetType) -> Moya.Response? {
let key = options.cryptoType.encryptedString(with: cacheKey(with: target))
return CacheManager.default.storage.fetchCached(forKey: key, options: options.cachedOptions)
}
private func saveCacheResponse(result: Result<Moya.Response, MoyaError>, target: TargetType) {
if case .success(let response) = result {
switch self.options.cacheType {
case .networkElseCache, .cacheThenNetwork, .cacheElseNetwork:
let key = options.cryptoType.encryptedString(with: cacheKey(with: target))
CacheManager.default.storage.storeCached(response, forKey: key, options: options.cachedOptions)
default:
break
}
}
}
private func cacheKey(with target: TargetType) -> String {
if let api = target as? NetworkAPI {
let paramString = X.sortParametersToString(api.parameters)
return target.baseURL.absoluteString + target.path + paramString
} else if let multiTarget = target as? MultiTarget, let api = multiTarget.target as? NetworkAPI {
let paramString = X.sortParametersToString(api.parameters)
return target.baseURL.absoluteString + target.path + paramString
}
return target.baseURL.absoluteString + target.path
}
}