@@ -36,14 +36,34 @@ extension HTTPCookie {
36
36
*/
37
37
open class HTTPCookieStorage : NSObject {
38
38
39
+ /* both sharedStorage and sharedCookieStorages are synchronized on sharedSyncQ */
39
40
private static var sharedStorage : HTTPCookieStorage ?
40
41
private static var sharedCookieStorages : [ String : HTTPCookieStorage ] = [ : ] //for group storage containers
42
+ private static let sharedSyncQ = DispatchQueue ( label: " org.swift.HTTPCookieStorage.sharedSyncQ " )
43
+
44
+ /* only modified in init */
41
45
private var cookieFilePath : String !
42
- private let workQueue : DispatchQueue = DispatchQueue ( label: " HTTPCookieStorage.workqueue " )
43
- var allCookies : [ String : HTTPCookie ]
46
+
47
+ /* synchronized on syncQ, please don't use _allCookies directly outside of init/deinit */
48
+ private var _allCookies : [ String : HTTPCookie ]
49
+ private var allCookies : [ String : HTTPCookie ] {
50
+ get {
51
+ if #available( OSX 10 . 12 , iOS 10 . 0 , tvOS 10 . 0 , watchOS 3 . 0 , * ) {
52
+ dispatchPrecondition ( condition: DispatchPredicate . onQueue ( self . syncQ) )
53
+ }
54
+ return self . _allCookies
55
+ }
56
+ set {
57
+ if #available( OSX 10 . 12 , iOS 10 . 0 , tvOS 10 . 0 , watchOS 3 . 0 , * ) {
58
+ dispatchPrecondition ( condition: DispatchPredicate . onQueue ( self . syncQ) )
59
+ }
60
+ self . _allCookies = newValue
61
+ }
62
+ }
63
+ private let syncQ = DispatchQueue ( label: " org.swift.HTTPCookieStorage.syncQ " )
44
64
45
65
private init ( cookieStorageName: String ) {
46
- allCookies = [ : ]
66
+ _allCookies = [ : ]
47
67
cookieAcceptPolicy = . always
48
68
super. init ( )
49
69
let bundlePath = Bundle . main. bundlePath
@@ -59,9 +79,11 @@ open class HTTPCookieStorage: NSObject {
59
79
private func loadPersistedCookies( ) {
60
80
guard let cookies = NSMutableDictionary ( contentsOfFile: cookieFilePath) else { return }
61
81
var cookies0 = _SwiftValue. fetch ( cookies) as? [ String : [ String : Any ] ] ?? [ : ]
62
- for key in cookies0. keys {
63
- if let cookie = createCookie ( cookies0 [ key] !) {
64
- allCookies [ key] = cookie
82
+ self . syncQ. sync {
83
+ for key in cookies0. keys {
84
+ if let cookie = createCookie ( cookies0 [ key] !) {
85
+ allCookies [ key] = cookie
86
+ }
65
87
}
66
88
}
67
89
}
@@ -87,11 +109,7 @@ open class HTTPCookieStorage: NSObject {
87
109
}
88
110
89
111
open var cookies : [ HTTPCookie ] ? {
90
- var theCookies : [ HTTPCookie ] ?
91
- workQueue. sync {
92
- theCookies = Array ( self . allCookies. values)
93
- }
94
- return theCookies
112
+ return Array ( self . syncQ. sync { self . allCookies. values } )
95
113
}
96
114
97
115
/*!
@@ -103,10 +121,12 @@ open class HTTPCookieStorage: NSObject {
103
121
*/
104
122
open class var shared : HTTPCookieStorage {
105
123
get {
106
- if sharedStorage == nil {
107
- sharedStorage = HTTPCookieStorage ( cookieStorageName: " shared " )
124
+ return sharedSyncQ. sync {
125
+ if sharedStorage == nil {
126
+ sharedStorage = HTTPCookieStorage ( cookieStorageName: " shared " )
127
+ }
128
+ return sharedStorage!
108
129
}
109
- return sharedStorage!
110
130
}
111
131
}
112
132
@@ -122,12 +142,14 @@ open class HTTPCookieStorage: NSObject {
122
142
method with the same identifier will return the same cookie storage instance.
123
143
*/
124
144
open class func sharedCookieStorage( forGroupContainerIdentifier identifier: String ) -> HTTPCookieStorage {
125
- guard let cookieStorage = sharedCookieStorages [ identifier] else {
126
- let newCookieStorage = HTTPCookieStorage ( cookieStorageName: identifier)
127
- sharedCookieStorages [ identifier] = newCookieStorage
128
- return newCookieStorage
145
+ return sharedSyncQ. sync {
146
+ guard let cookieStorage = sharedCookieStorages [ identifier] else {
147
+ let newCookieStorage = HTTPCookieStorage ( cookieStorageName: identifier)
148
+ sharedCookieStorages [ identifier] = newCookieStorage
149
+ return newCookieStorage
150
+ }
151
+ return cookieStorage
129
152
}
130
- return cookieStorage
131
153
}
132
154
133
155
@@ -138,7 +160,7 @@ open class HTTPCookieStorage: NSObject {
138
160
same name, domain and path, if any.
139
161
*/
140
162
open func setCookie( _ cookie: HTTPCookie ) {
141
- workQueue . sync {
163
+ self . syncQ . sync {
142
164
guard cookieAcceptPolicy != . never else { return }
143
165
144
166
//add or override
@@ -173,9 +195,13 @@ open class HTTPCookieStorage: NSObject {
173
195
}
174
196
175
197
private func updatePersistentStore( ) {
198
+ if #available( OSX 10 . 12 , iOS 10 . 0 , tvOS 10 . 0 , watchOS 3 . 0 , * ) {
199
+ dispatchPrecondition ( condition: DispatchPredicate . onQueue ( self . syncQ) )
200
+ }
201
+
176
202
//persist cookies
177
203
var persistDictionary : [ String : [ String : Any ] ] = [ : ]
178
- let persistable = allCookies. filter { ( _, value) in
204
+ let persistable = self . allCookies. filter { ( _, value) in
179
205
value. expiresDate != nil &&
180
206
value. isSessionOnly == false &&
181
207
value. expiresDate!. timeIntervalSinceNow > 0
@@ -189,15 +215,23 @@ open class HTTPCookieStorage: NSObject {
189
215
_ = nsdict. write ( toFile: cookieFilePath, atomically: true )
190
216
}
191
217
218
+ /*!
219
+ @method lockedDeleteCookie:
220
+ @abstract Delete the specified cookie, for internal callers already on syncQ.
221
+ */
222
+ private func lockedDeleteCookie( _ cookie: HTTPCookie ) {
223
+ let key = cookie. domain + cookie. path + cookie. name
224
+ self . allCookies. removeValue ( forKey: key)
225
+ updatePersistentStore ( )
226
+ }
227
+
192
228
/*!
193
229
@method deleteCookie:
194
230
@abstract Delete the specified cookie
195
231
*/
196
232
open func deleteCookie( _ cookie: HTTPCookie ) {
197
- let key = cookie. domain + cookie. path + cookie. name
198
- workQueue. sync {
199
- self . allCookies. removeValue ( forKey: key)
200
- updatePersistentStore ( )
233
+ self . syncQ. sync {
234
+ self . lockedDeleteCookie ( cookie)
201
235
}
202
236
}
203
237
@@ -206,13 +240,15 @@ open class HTTPCookieStorage: NSObject {
206
240
@abstract Delete all cookies from the cookie storage since the provided date.
207
241
*/
208
242
open func removeCookies( since date: Date ) {
209
- let cookiesSinceDate = allCookies. values. filter {
210
- $0. properties![ . created] as! Double > date. timeIntervalSinceReferenceDate
211
- }
212
- for cookie in cookiesSinceDate {
213
- deleteCookie ( cookie)
243
+ self . syncQ. sync {
244
+ let cookiesSinceDate = self . allCookies. values. filter {
245
+ $0. properties![ . created] as! Double > date. timeIntervalSinceReferenceDate
246
+ }
247
+ for cookie in cookiesSinceDate {
248
+ lockedDeleteCookie ( cookie)
249
+ }
250
+ updatePersistentStore ( )
214
251
}
215
- updatePersistentStore ( )
216
252
}
217
253
218
254
/*!
@@ -226,12 +262,8 @@ open class HTTPCookieStorage: NSObject {
226
262
into a set of header fields to add to a request.
227
263
*/
228
264
open func cookies( for url: URL ) -> [ HTTPCookie ] ? {
229
- var cookies : [ HTTPCookie ] ?
230
265
guard let host = url. host else { return nil }
231
- workQueue. sync {
232
- cookies = Array ( allCookies. values. filter { $0. domain == host } )
233
- }
234
- return cookies
266
+ return Array ( self . syncQ. sync ( execute: { allCookies} ) . values. filter { $0. domain == host } )
235
267
}
236
268
237
269
/*!
0 commit comments