Skip to content

Commit af8db32

Browse files
committed
Caching improvements
1 parent 117679a commit af8db32

3 files changed

Lines changed: 63 additions & 53 deletions

File tree

Sources/UsefulNetworkLayer/NetworkLayer/APIStructs.swift

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -61,9 +61,13 @@ public struct APIResponse<T> where T: ResponseBodyParsable {
6161
/// Main URL response of the API request.
6262
public private(set) var response: URLResponse
6363

64-
internal init(response: URLResponse, responseBody: T?) {
64+
/// Returns `true` if the response is loaded from cache.
65+
public private(set) var isCachedResponse: Bool
66+
67+
internal init(response: URLResponse, responseBody: T?, isCached: Bool) {
6568
self.response = response
6669
self.responseBody = responseBody
70+
self.isCachedResponse = isCached
6771
}
6872

6973
}

Sources/UsefulNetworkLayer/NetworkLayer/NetworkLayer+Cache.swift

Lines changed: 30 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -25,10 +25,10 @@ import Foundation
2525
import UIKit
2626
#endif
2727

28-
public extension NetworkLayer {
28+
extension NetworkLayer {
2929

3030
/// Custom Caching class.
31-
class Cache: URLCache {
31+
open class Cache: URLCache {
3232

3333
/// Default initializer.
3434
override init() {
@@ -64,7 +64,7 @@ public extension NetworkLayer {
6464

6565
/// Checks validity of the cached response with expiry value. Returns `nil` if its not valid.
6666
private func checkCachedResponse(_ response: CachedURLResponse?) -> CachedURLResponse? {
67-
guard let endDate = response?.userInfo?["cachingEndsAt"] as? Date else { return response }
67+
guard let endDate = response?.userInfo?["cachingEndsAt"] as? Date else { return nil }
6868

6969
if endDate > Date() {
7070
return response
@@ -73,43 +73,6 @@ public extension NetworkLayer {
7373
}
7474
}
7575

76-
/// Updates cache expiry of the specified request
77-
/// - returns: `true` if timer updated successfuly.
78-
@discardableResult
79-
public func changeCacheExpiry(for request: URLRequest, to date: Date) -> Bool {
80-
if let cachedResponse = self.cachedResponseWithForce(for: request) {
81-
let userInfo = ["cachingEndsAt": date]
82-
let newCacheObj = CachedURLResponse(response: cachedResponse.response,
83-
data: cachedResponse.data,
84-
userInfo: userInfo,
85-
storagePolicy: .allowed)
86-
self.removeCachedResponse(for: request)
87-
self.storeCachedResponse(newCacheObj, for: request)
88-
return true
89-
}
90-
91-
return false
92-
}
93-
94-
/// Updates cache expiry of the specified request
95-
/// - returns: `true` if timer updated successfuly.
96-
@discardableResult
97-
public func changeCacheExpiry(for task: URLSessionDataTask, to date: Date) -> Bool {
98-
guard let request = task.currentRequest else { return false }
99-
if let cachedResponse = self.cachedResponseWithForce(for: request) {
100-
let userInfo = ["cachingEndsAt": date]
101-
let newCacheObj = CachedURLResponse(response: cachedResponse.response,
102-
data: cachedResponse.data,
103-
userInfo: userInfo,
104-
storagePolicy: .allowed)
105-
self.removeCachedResponse(for: task)
106-
self.storeCachedResponse(newCacheObj, for: task)
107-
return true
108-
}
109-
110-
return false
111-
}
112-
11376
/// Checks the response and acts accordingly.
11477
override public func cachedResponse(for request: URLRequest) -> CachedURLResponse? {
11578
let cachedResponse = super.cachedResponse(for: request)
@@ -154,11 +117,38 @@ public extension NetworkLayer {
154117

155118
/// Calls the super class.
156119
override public func storeCachedResponse(_ cachedResponse: CachedURLResponse, for request: URLRequest) {
120+
if cachedResponse.userInfo?["cachingEndsAt"] as? Date == nil {
121+
return
122+
}
157123
super.storeCachedResponse(cachedResponse, for: request)
158124
}
159125

160126
/// Calls the super class.
161127
override public func storeCachedResponse(_ cachedResponse: CachedURLResponse, for dataTask: URLSessionDataTask) {
128+
if cachedResponse.userInfo?["cachingEndsAt"] as? Date == nil {
129+
return
130+
}
131+
super.storeCachedResponse(cachedResponse, for: dataTask)
132+
}
133+
134+
open func storeResponse(_ response: URLResponse, data: Data,
135+
for request: URLRequest, expiry: Date) {
136+
let userInfo = ["cachingEndsAt": expiry]
137+
let cachedResponse = CachedURLResponse(response: response,
138+
data: data,
139+
userInfo: userInfo,
140+
storagePolicy: .allowed)
141+
super.storeCachedResponse(cachedResponse, for: request)
142+
}
143+
144+
/// Calls the super class.
145+
public func storeResponse(_ response: URLResponse, data: Data,
146+
for dataTask: URLSessionDataTask, expiry: Date) {
147+
let userInfo = ["cachingEndsAt": expiry]
148+
let cachedResponse = CachedURLResponse(response: response,
149+
data: data,
150+
userInfo: userInfo,
151+
storagePolicy: .allowed)
162152
super.storeCachedResponse(cachedResponse, for: dataTask)
163153
}
164154

Sources/UsefulNetworkLayer/NetworkLayer/NetworkLayer.swift

Lines changed: 28 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -133,10 +133,10 @@ public class NetworkLayer: NSObject, URLSessionDataDelegate {
133133
let id = Int(Date().timeIntervalSince1970 * 1000)
134134
var operation: APIOperation!
135135
var task: URLSessionDataTask!
136-
137136
task = instance._urlSession.dataTask(with: urlRequest) { (data, response, error) in
138137
guard operation != nil else { return }
139138

139+
var isCached = false
140140
instance.sendLog(message: "Data Task for Operation ID: \(operation.identifier) is completed - URL: \(urlRequest.url?.absoluteString ?? "nil")")
141141
operation.isFinished = true
142142
var dataResult = data ?? Data()
@@ -146,6 +146,7 @@ public class NetworkLayer: NSObject, URLSessionDataDelegate {
146146
if let oldCacheObject = instance._cache?.cachedResponseWithForce(for: urlRequest) {
147147
dataResult = oldCacheObject.data
148148
loadedResponse = oldCacheObject.response
149+
isCached = true
149150
} else {
150151
instance.sendLog(message: "Operation:\(operation.identifier) failed with error: \(error.localizedDescription)", logType: .error(code: (error as NSError).code, name: error.localizedDescription))
151152
DispatchQueue.main.async {
@@ -157,12 +158,15 @@ public class NetworkLayer: NSObject, URLSessionDataDelegate {
157158
}
158159
}
159160

160-
instance._cache?.changeCacheExpiry(for: task, to: request.cachingTime.expirationDate ?? Date())
161+
instance._cache?.storeResponse(loadedResponse!, data: dataResult,
162+
for: task, expiry: request.cachingTime.expirationDate ?? Date())
161163

162164
instance.proceedResponse(response: loadedResponse!,
163165
data: dataResult,
164166
operationId: operation.identifier,
165167
request: request,
168+
isCachedResponse: isCached,
169+
dataTask: task,
166170
completion: completion)
167171
}
168172

@@ -171,10 +175,12 @@ public class NetworkLayer: NSObject, URLSessionDataDelegate {
171175
// found in the cache, proceed
172176
instance.sendLog(message: "Operation with ID: \(id) is gathered from the cache - Caching ends: \(response.userInfo?["cachingEndsAt"] ?? "Nil")")
173177
instance.proceedResponse(response: response.response,
174-
data: response.data,
175-
operationId: id,
176-
request: request,
177-
completion: completion)
178+
data: response.data,
179+
operationId: id,
180+
request: request,
181+
isCachedResponse: true,
182+
dataTask: task,
183+
completion: completion)
178184
task.cancel()
179185
} else {
180186
operation = request.operation(with: task, id: id)
@@ -198,6 +204,8 @@ public class NetworkLayer: NSObject, URLSessionDataDelegate {
198204
private func proceedResponse<T,S>(response: URLResponse, data: Data,
199205
operationId: Int,
200206
request: APIConfiguration<T,S>,
207+
isCachedResponse: Bool,
208+
dataTask: URLSessionDataTask,
201209
completion: @escaping (Result<T,S>)->()) where T: ResponseBodyParsable, S: ErrorResponseParsable {
202210

203211
let statusCode = (response as! HTTPURLResponse).statusCode
@@ -219,7 +227,9 @@ public class NetworkLayer: NSObject, URLSessionDataDelegate {
219227
guard data.count > 0 else {
220228
self.sendLog(message: "Data of the response is empty - Ignoring response creation - Operation: \(operationId)", logType: .info)
221229
DispatchQueue.main.async {
222-
completion(.success(.init(response: response, responseBody: nil)))
230+
completion(.success(.init(response: response,
231+
responseBody: nil,
232+
isCached: isCachedResponse)))
223233
}
224234
return
225235
}
@@ -228,7 +238,9 @@ public class NetworkLayer: NSObject, URLSessionDataDelegate {
228238
do {
229239
let jsonObject = try JSONDecoder().decode(request.responseBodyObject, from: data)
230240
DispatchQueue.main.async {
231-
completion(.success(.init(response: response, responseBody: jsonObject)))
241+
completion(.success(.init(response: response,
242+
responseBody: jsonObject,
243+
isCached: isCachedResponse)))
232244
}
233245
} catch {
234246
var errorBody = S.init()
@@ -244,10 +256,12 @@ public class NetworkLayer: NSObject, URLSessionDataDelegate {
244256
if let dataObject = request.responseBodyObject.init(data: data) {
245257
self.sendLog(message: "Data Object created from Operation: \(operationId) - Object: \(dataObject.typeName)")
246258
if request.autoCache, let cacheTiming = dataObject.cachingEndsAt() {
247-
self._cache?.changeCacheExpiry(for: request.request!, to: cacheTiming)
259+
self._cache?.storeResponse(response, data: data, for: dataTask, expiry: cacheTiming)
248260
}
249261
DispatchQueue.main.async {
250-
completion(.success(APIResponse(response: response, responseBody: dataObject)))
262+
completion(.success(APIResponse(response: response,
263+
responseBody: dataObject,
264+
isCached: isCachedResponse)))
251265
}
252266
return
253267
}
@@ -257,10 +271,12 @@ public class NetworkLayer: NSObject, URLSessionDataDelegate {
257271
if let responseObject = request.responseBodyObject.init(response: json) {
258272
self.sendLog(message: "Response Object Created from JSON Data with Operation: \(operationId) - Object: \(responseObject.typeName)")
259273
if request.autoCache, let cacheTiming = responseObject.cachingEndsAt() {
260-
self._cache?.changeCacheExpiry(for: request.request!, to: cacheTiming)
274+
self._cache?.storeResponse(response, data: data, for: dataTask, expiry: cacheTiming)
261275
}
262276
DispatchQueue.main.async {
263-
completion(.success(APIResponse(response: response, responseBody: responseObject)))
277+
completion(.success(APIResponse(response: response,
278+
responseBody: responseObject,
279+
isCached: isCachedResponse)))
264280
}
265281
} else {
266282
DispatchQueue.main.async {

0 commit comments

Comments
 (0)