@@ -125,24 +125,10 @@ open class CachedURLResponse : NSObject, NSSecureCoding, NSCopying {
125
125
open class URLCache : NSObject {
126
126
127
127
private static let sharedSyncQ = DispatchQueue ( label: " org.swift.URLCache.sharedSyncQ " )
128
-
129
- private static var sharedCache : URLCache ? {
130
- willSet {
131
- URLCache . sharedCache? . syncQ. sync {
132
- URLCache . sharedCache? . _databaseClient? . close ( )
133
- URLCache . sharedCache? . flushDatabase ( )
134
- }
135
- }
136
- didSet {
137
- URLCache . sharedCache? . syncQ. sync {
138
- URLCache . sharedCache? . setupCacheDatabaseIfNotExist ( )
139
- }
140
- }
141
- }
128
+ private static var sharedCache : URLCache ?
142
129
143
130
private let syncQ = DispatchQueue ( label: " org.swift.URLCache.syncQ " )
144
- private let _baseDiskPath : String ?
145
- private var _databaseClient : _CacheSQLiteClient ?
131
+ private var persistence : CachePersistence ?
146
132
147
133
/*!
148
134
@method sharedURLCache
@@ -169,19 +155,31 @@ open class URLCache : NSObject {
169
155
} else {
170
156
let fourMegaByte = 4 * 1024 * 1024
171
157
let twentyMegaByte = 20 * 1024 * 1024
172
- let cacheDirectoryPath = FileManager . default. urls ( for: . cachesDirectory, in: . userDomainMask) . first? . path ?? " \( NSHomeDirectory ( ) ) /Library/Caches/ "
173
- let path = " \( cacheDirectoryPath) \( Bundle . main. bundleIdentifier ?? UUID ( ) . uuidString) "
174
- let cache = URLCache ( memoryCapacity: fourMegaByte, diskCapacity: twentyMegaByte, diskPath: path)
158
+ let bundleIdentifier = Bundle . main. bundleIdentifier ?? UUID ( ) . uuidString
159
+ let cacheDirectoryPath = FileManager . default. urls ( for: . cachesDirectory, in: . userDomainMask) . first? . path ?? " \( NSHomeDirectory ( ) ) /Library/Caches "
160
+ let diskPath = cacheDirectoryPath + " / " + bundleIdentifier
161
+ let cache = URLCache ( memoryCapacity: fourMegaByte, diskCapacity: twentyMegaByte, diskPath: diskPath)
175
162
sharedCache = cache
163
+ cache. persistence? . setupCacheDirectory ( )
176
164
return cache
177
165
}
178
166
}
179
167
}
180
168
set {
181
- sharedSyncQ. sync { sharedCache = newValue }
169
+ guard newValue !== sharedCache else { return }
170
+
171
+ sharedSyncQ. sync {
172
+ // Remove previous data before resetting new URLCache instance
173
+ URLCache . sharedCache? . persistence? . deleteAllCaches ( )
174
+ sharedCache = newValue
175
+ newValue. persistence? . setupCacheDirectory ( )
176
+ }
182
177
}
183
178
}
184
179
180
+ private var allCaches : [ String : CachedURLResponse ] = [ : ]
181
+ private var cacheSizeInMemory : Int = 0
182
+
185
183
/*!
186
184
@method initWithMemoryCapacity:diskCapacity:diskPath:
187
185
@abstract Initializes an URLCache with the given capacity and
@@ -198,8 +196,11 @@ open class URLCache : NSObject {
198
196
public init ( memoryCapacity: Int , diskCapacity: Int , diskPath path: String ? ) {
199
197
self . memoryCapacity = memoryCapacity
200
198
self . diskCapacity = diskCapacity
201
- self . _baseDiskPath = path
202
-
199
+
200
+ if let _path = path {
201
+ self . persistence = CachePersistence ( path: _path)
202
+ }
203
+
203
204
super. init ( )
204
205
}
205
206
@@ -272,7 +273,9 @@ open class URLCache : NSObject {
272
273
usage of the in-memory cache.
273
274
@result the current usage of the in-memory cache of the receiver.
274
275
*/
275
- open var currentMemoryUsage : Int { NSUnimplemented ( ) }
276
+ open var currentMemoryUsage : Int {
277
+ return self . syncQ. sync { self . cacheSizeInMemory }
278
+ }
276
279
277
280
/*!
278
281
@method currentDiskUsage
@@ -282,19 +285,10 @@ open class URLCache : NSObject {
282
285
usage of the on-disk cache.
283
286
@result the current usage of the on-disk cache of the receiver.
284
287
*/
285
- open var currentDiskUsage : Int { NSUnimplemented ( ) }
286
-
287
- private func flushDatabase( ) {
288
- guard let path = _baseDiskPath else { return }
289
-
290
- do {
291
- let dbPath = path. appending ( " /Cache.db " )
292
- try FileManager . default. removeItem ( atPath: dbPath)
293
- } catch {
294
- fatalError ( " Unable to flush database for URLCache: \( error. localizedDescription) " )
295
- }
288
+ open var currentDiskUsage : Int {
289
+ return self . syncQ. sync { self . persistence? . cacheSizeInDesk ?? 0 }
296
290
}
297
-
291
+
298
292
}
299
293
300
294
extension URLCache {
@@ -303,112 +297,79 @@ extension URLCache {
303
297
public func removeCachedResponse( for dataTask: URLSessionDataTask ) { NSUnimplemented ( ) }
304
298
}
305
299
306
- extension URLCache {
307
-
308
- private func setupCacheDatabaseIfNotExist( ) {
309
- guard let path = _baseDiskPath else { return }
310
-
311
- if !FileManager. default. fileExists ( atPath: path) {
312
- do {
313
- try FileManager . default. createDirectory ( atPath: path, withIntermediateDirectories: true )
314
- } catch {
315
- fatalError ( " Unable to create directories for URLCache: \( error. localizedDescription) " )
300
+ fileprivate struct CachePersistence {
301
+
302
+ let path : String
303
+
304
+ var cacheSizeInDesk : Int {
305
+ do {
306
+ let subFiles = try FileManager . default. subpathsOfDirectory ( atPath: path)
307
+ var total : Int = 0
308
+ for fileName in subFiles {
309
+ let attributes = try FileManager . default. attributesOfItem ( atPath: path. appending ( fileName) )
310
+ total += ( attributes [ . size] as? Int ) ?? 0
316
311
}
317
- }
318
-
319
- // Close the currently opened database connection(if any), before creating/replacing the db file
320
- _databaseClient? . close ( )
321
-
322
- let dbPath = path. appending ( " /Cache.db " )
323
- if !FileManager. default. createFile ( atPath: dbPath, contents: nil , attributes: nil ) {
324
- fatalError ( " Unable to setup database for URLCache " )
325
- }
326
-
327
- _databaseClient = _CacheSQLiteClient ( databasePath: dbPath)
328
- if _databaseClient == nil {
329
- _databaseClient? . close ( )
330
- flushDatabase ( )
331
- fatalError ( " Unable to setup database for URLCache " )
332
- }
333
-
334
- if !createTables( ) {
335
- _databaseClient? . close ( )
336
- flushDatabase ( )
337
- fatalError ( " Unable to setup database for URLCache: Tables not created " )
338
- }
339
-
340
- if !createIndicesForTables( ) {
341
- _databaseClient? . close ( )
342
- flushDatabase ( )
343
- fatalError ( " Unable to setup database for URLCache: Indices not created for tables " )
312
+ return total
313
+ } catch {
314
+ return 0
344
315
}
345
316
}
346
-
347
- private func createTables( ) -> Bool {
348
- guard _databaseClient != nil else {
349
- fatalError ( " Cannot create table before database setup " )
350
- }
351
-
352
- let tableSQLs = [
353
- " CREATE TABLE cfurl_cache_response(entry_ID INTEGER PRIMARY KEY, version INTEGER, hash_value VARCHAR, storage_policy INTEGER, request_key VARCHAR, time_stamp DATETIME, partition VARCHAR) " ,
354
- " CREATE TABLE cfurl_cache_receiver_data(entry_ID INTEGER PRIMARY KEY, isDataOnFS INTEGER, receiver_data BLOB) " ,
355
- " CREATE TABLE cfurl_cache_blob_data(entry_ID INTEGER PRIMARY KEY, response_object BLOB, request_object BLOB, proto_props BLOB, user_info BLOB) " ,
356
- " CREATE TABLE cfurl_cache_schema_version(schema_version INTEGER) "
357
- ]
358
-
359
- for sql in tableSQLs {
360
- if let isSuccess = _databaseClient? . execute ( sql: sql) , !isSuccess {
361
- return false
317
+
318
+ func setupCacheDirectory( ) {
319
+ do {
320
+ if FileManager . default. fileExists ( atPath: path) {
321
+ try FileManager . default. removeItem ( atPath: path)
362
322
}
323
+
324
+ try FileManager . default. createDirectory ( atPath: path, withIntermediateDirectories: true )
325
+ } catch {
326
+ fatalError ( " Unable to create directories for URLCache: \( error. localizedDescription) " )
363
327
}
364
-
365
- return true
366
328
}
367
-
368
- private func createIndicesForTables( ) -> Bool {
369
- guard _databaseClient != nil else {
370
- fatalError ( " Cannot create table before database setup " )
371
- }
372
-
373
- let indicesSQLs = [
374
- " CREATE INDEX proto_props_index ON cfurl_cache_blob_data(entry_ID) " ,
375
- " CREATE INDEX receiver_data_index ON cfurl_cache_receiver_data(entry_ID) " ,
376
- " CREATE INDEX request_key_index ON cfurl_cache_response(request_key) " ,
377
- " CREATE INDEX time_stamp_index ON cfurl_cache_response(time_stamp) "
378
- ]
379
-
380
- for sql in indicesSQLs {
381
- if let isSuccess = _databaseClient? . execute ( sql: sql) , !isSuccess {
382
- return false
383
- }
329
+
330
+ func saveCachedResponse( _ response: CachedURLResponse , for request: URLRequest ) {
331
+ let archivedData = NSKeyedArchiver . archivedData ( withRootObject: response)
332
+ do {
333
+ let cacheFileURL = urlForFileIdentifier ( request. cacheFileIdentifier)
334
+ try archivedData. write ( to: cacheFileURL)
335
+ } catch {
336
+ fatalError ( " Unable to save cache data: \( error. localizedDescription) " )
384
337
}
385
-
386
- return true
387
338
}
388
-
389
- }
390
339
391
- fileprivate struct _CacheSQLiteClient {
392
-
393
- private var database : OpaquePointer ?
394
-
395
- init ? ( databasePath: String ) {
396
- if sqlite3_open_v2 ( databasePath, & database, SQLITE_OPEN_CREATE | SQLITE_OPEN_READWRITE, nil ) != SQLITE_OK {
340
+ func cachedResponse( for request: URLRequest ) -> CachedURLResponse ? {
341
+ let url = urlForFileIdentifier ( request. cacheFileIdentifier)
342
+ guard let data = try ? Data ( contentsOf: url) ,
343
+ let response = NSKeyedUnarchiver . unarchiveObject ( with: data) as? CachedURLResponse else {
397
344
return nil
398
345
}
346
+
347
+ return response
399
348
}
400
-
401
- func execute( sql: String ) -> Bool {
402
- guard let db = database else { return false }
403
-
404
- return sqlite3_exec ( db, sql, nil , nil , nil ) == SQLITE_OK
349
+
350
+ func deleteAllCaches( ) {
351
+ do {
352
+ try FileManager . default. removeItem ( atPath: path)
353
+ } catch {
354
+ fatalError ( " Unable to flush database for URLCache: \( error. localizedDescription) " )
355
+ }
405
356
}
406
-
407
- mutating func close( ) {
408
- guard let db = database else { return }
409
-
410
- sqlite3_close_v2 ( db)
411
- database = nil
357
+
358
+ private func urlForFileIdentifier( _ identifier: String ) -> URL {
359
+ return URL ( fileURLWithPath: path + " / " + identifier)
412
360
}
413
-
361
+
362
+ }
363
+
364
+ fileprivate extension URLRequest {
365
+
366
+ var cacheFileIdentifier : String {
367
+ guard let urlString = url? . absoluteString else {
368
+ fatalError ( " Unable to create cache identifier for request: \( self ) " )
369
+ }
370
+
371
+ let method = httpMethod ?? " GET "
372
+ return urlString + method
373
+ }
374
+
414
375
}
0 commit comments