Skip to content

Commit 93d0018

Browse files
authored
Merge pull request #710 from naithar/NSURLRequest-NSCoding
2 parents 11c2169 + 5910b1f commit 93d0018

File tree

4 files changed

+138
-6
lines changed

4 files changed

+138
-6
lines changed

Docs/Status.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -53,8 +53,8 @@ There is no _Complete_ status for test coverage because there are always additio
5353
| `URLProtectionSpace` | Unimplemented | None | |
5454
| `URLProtocol` | Unimplemented | None | |
5555
| `URLProtocolClient` | Unimplemented | None | |
56-
| `NSURLRequest` | Mostly Complete | Incomplete | `NSCoding` remains unimplemented |
57-
| `NSMutableURLRequest` | Mostly Complete | Incomplete | `NSCoding` remains unimplemented |
56+
| `NSURLRequest` | Mostly Complete | Incomplete | |
57+
| `NSMutableURLRequest` | Mostly Complete | Incomplete | |
5858
| `URLResponse` | Mostly Complete | Incomplete | `NSCoding` remains unimplemented |
5959
| `NSHTTPURLResponse` | Mostly Complete | Substantial | `NSCoding` remains unimplemented |
6060
| `NSURL` | Mostly Complete | Substantial | `NSCoding` with non-keyed-coding archivers, `checkResourceIsReachable()`, and resource values remain unimplemented |

Foundation/NSData.swift

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -217,7 +217,12 @@ open class NSData : NSObject, NSCopying, NSMutableCopying, NSSecureCoding {
217217
}
218218

219219
open var bytes: UnsafeRawPointer {
220-
return UnsafeRawPointer(CFDataGetBytePtr(_cfObject))
220+
guard let bytePtr = CFDataGetBytePtr(_cfObject) else {
221+
//This could occure on empty data being encoded.
222+
//TODO: switch with nil when signature is fixed
223+
return UnsafeRawPointer(bitPattern: 0xf00deadb0c0)! //would not result in 'nil unwrapped optional'
224+
}
225+
return UnsafeRawPointer(bytePtr)
221226
}
222227

223228

Foundation/NSURLRequest.swift

Lines changed: 93 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -156,11 +156,101 @@ open class NSURLRequest : NSObject, NSSecureCoding, NSCopying, NSMutableCopying
156156
}
157157

158158
public required init?(coder aDecoder: NSCoder) {
159-
NSUnimplemented()
159+
guard aDecoder.allowsKeyedCoding else {
160+
preconditionFailure("Unkeyed coding is unsupported.")
161+
}
162+
163+
if let encodedURL = aDecoder.decodeObject(forKey: "NS.url") as? NSURL {
164+
self.url = encodedURL._swiftObject
165+
}
166+
167+
if let encodedHeaders = aDecoder.decodeObject(forKey: "NS._allHTTPHeaderFields") as? NSDictionary {
168+
self._allHTTPHeaderFields = encodedHeaders.reduce([String : String]()) { result, item in
169+
var result = result
170+
if let key = item.key as? NSString,
171+
let value = item.value as? NSString {
172+
result[key._swiftObject] = value._swiftObject
173+
}
174+
return result
175+
}
176+
}
177+
178+
if let encodedDocumentURL = aDecoder.decodeObject(forKey: "NS.mainDocumentURL") as? NSURL {
179+
self.mainDocumentURL = encodedDocumentURL._swiftObject
180+
}
181+
182+
if let encodedMethod = aDecoder.decodeObject(forKey: "NS.httpMethod") as? NSString {
183+
self.httpMethod = encodedMethod._swiftObject
184+
}
185+
186+
let encodedCachePolicy = aDecoder.decodeObject(forKey: "NS._cachePolicy") as! NSNumber
187+
self._cachePolicy = CachePolicy(rawValue: encodedCachePolicy.uintValue)!
188+
189+
let encodedTimeout = aDecoder.decodeObject(forKey: "NS._timeoutInterval") as! NSNumber
190+
self._timeoutInterval = encodedTimeout.doubleValue
191+
192+
let encodedHttpBody: Data? = aDecoder.withDecodedUnsafeBufferPointer(forKey: "NS.httpBody") {
193+
guard let buffer = $0 else { return nil }
194+
return Data(buffer: buffer)
195+
}
196+
197+
if let encodedHttpBody = encodedHttpBody {
198+
self._body = .data(encodedHttpBody)
199+
}
200+
201+
let encodedNetworkServiceType = aDecoder.decodeObject(forKey: "NS._networkServiceType") as! NSNumber
202+
self._networkServiceType = NetworkServiceType(rawValue: encodedNetworkServiceType.uintValue)!
203+
204+
let encodedCellularAccess = aDecoder.decodeObject(forKey: "NS._allowsCellularAccess") as! NSNumber
205+
self._allowsCellularAccess = encodedCellularAccess.boolValue
206+
207+
let encodedHandleCookies = aDecoder.decodeObject(forKey: "NS._httpShouldHandleCookies") as! NSNumber
208+
self._httpShouldHandleCookies = encodedHandleCookies.boolValue
209+
210+
let encodedUsePipelining = aDecoder.decodeObject(forKey: "NS._httpShouldUsePipelining") as! NSNumber
211+
self._httpShouldUsePipelining = encodedUsePipelining.boolValue
160212
}
161213

162214
open func encode(with aCoder: NSCoder) {
163-
NSUnimplemented()
215+
guard aCoder.allowsKeyedCoding else {
216+
preconditionFailure("Unkeyed coding is unsupported.")
217+
}
218+
219+
aCoder.encode(self.url?._bridgeToObjectiveC(), forKey: "NS.url")
220+
aCoder.encode(self._allHTTPHeaderFields?._bridgeToObjectiveC(), forKey: "NS._allHTTPHeaderFields")
221+
aCoder.encode(self.mainDocumentURL?._bridgeToObjectiveC(), forKey: "NS.mainDocumentURL")
222+
aCoder.encode(self.httpMethod?._bridgeToObjectiveC(), forKey: "NS.httpMethod")
223+
aCoder.encode(self._cachePolicy.rawValue._bridgeToObjectiveC(), forKey: "NS._cachePolicy")
224+
aCoder.encode(self._timeoutInterval._bridgeToObjectiveC(), forKey: "NS._timeoutInterval")
225+
if let httpBody = self.httpBody?._bridgeToObjectiveC() {
226+
let bytePtr = httpBody.bytes.bindMemory(to: UInt8.self, capacity: httpBody.length)
227+
aCoder.encodeBytes(bytePtr, length: httpBody.length, forKey: "NS.httpBody")
228+
}
229+
//On macOS input stream is not encoded.
230+
aCoder.encode(self._networkServiceType.rawValue._bridgeToObjectiveC(), forKey: "NS._networkServiceType")
231+
aCoder.encode(self._allowsCellularAccess._bridgeToObjectiveC(), forKey: "NS._allowsCellularAccess")
232+
aCoder.encode(self._httpShouldHandleCookies._bridgeToObjectiveC(), forKey: "NS._httpShouldHandleCookies")
233+
aCoder.encode(self._httpShouldUsePipelining._bridgeToObjectiveC(), forKey: "NS._httpShouldUsePipelining")
234+
}
235+
236+
open override func isEqual(_ object: Any?) -> Bool {
237+
//On macOS this fields do not determine the result:
238+
//allHTTPHeaderFields
239+
//timeoutInterval
240+
//httBody
241+
//networkServiceType
242+
//httpShouldUsePipelining
243+
if let other = object as? NSURLRequest {
244+
return other === self
245+
|| (other.url == self.url
246+
&& other.mainDocumentURL == self.mainDocumentURL
247+
&& other.httpMethod == self.httpMethod
248+
&& other._cachePolicy == self._cachePolicy
249+
&& other.httpBodyStream == self.httpBodyStream
250+
&& other._allowsCellularAccess == self._allowsCellularAccess
251+
&& other._httpShouldHandleCookies == self._httpShouldHandleCookies)
252+
}
253+
return false
164254
}
165255

166256
/// Indicates that NSURLRequest implements the NSSecureCoding protocol.
@@ -289,7 +379,7 @@ open class NSURLRequest : NSObject, NSSecureCoding, NSCopying, NSMutableCopying
289379
/// example.
290380
open class NSMutableURLRequest : NSURLRequest {
291381
public required init?(coder aDecoder: NSCoder) {
292-
NSUnimplemented()
382+
super.init(coder: aDecoder)
293383
}
294384

295385
public convenience init(url: URL) {

TestFoundation/TestNSURLRequest.swift

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -27,6 +27,9 @@ class TestNSURLRequest : XCTestCase {
2727
("test_mutableCopy_1", test_mutableCopy_1),
2828
("test_mutableCopy_2", test_mutableCopy_2),
2929
("test_mutableCopy_3", test_mutableCopy_3),
30+
("test_NSCoding_1", test_NSCoding_1),
31+
("test_NSCoding_2", test_NSCoding_2),
32+
("test_NSCoding_3", test_NSCoding_3)
3033
]
3134
}
3235

@@ -203,4 +206,38 @@ class TestNSURLRequest : XCTestCase {
203206
XCTAssertEqual(originalRequest.url, urlA)
204207
XCTAssertNil(originalRequest.allHTTPHeaderFields)
205208
}
209+
210+
func test_NSCoding_1() {
211+
let url = URL(string: "https://apple.com")!
212+
let requestA = NSURLRequest(url: url)
213+
let requestB = NSKeyedUnarchiver.unarchiveObject(with: NSKeyedArchiver.archivedData(withRootObject: requestA)) as! NSURLRequest
214+
XCTAssertEqual(requestA, requestB, "Archived then unarchived url request must be equal.")
215+
}
216+
217+
func test_NSCoding_2() {
218+
let url = URL(string: "https://apple.com")!
219+
let requestA = NSMutableURLRequest(url: url)
220+
//Also checks crash on NSData.bytes
221+
requestA.httpBody = Data()
222+
let requestB = NSKeyedUnarchiver.unarchiveObject(with: NSKeyedArchiver.archivedData(withRootObject: requestA)) as! NSURLRequest
223+
XCTAssertEqual(requestA, requestB, "Archived then unarchived url request must be equal.")
224+
//Check `.httpBody` as it is not checked in `isEqual(_:)`
225+
XCTAssertEqual(requestB.httpBody, requestA.httpBody)
226+
}
227+
228+
func test_NSCoding_3() {
229+
let url = URL(string: "https://apple.com")!
230+
let urlForDocument = URL(string: "http://ibm.com")!
231+
232+
let requestA = NSMutableURLRequest(url: url)
233+
requestA.mainDocumentURL = urlForDocument
234+
//Also checks crash on NSData.bytes
235+
requestA.httpBody = Data(bytes: [1, 2, 3])
236+
let requestB = NSKeyedUnarchiver.unarchiveObject(with: NSKeyedArchiver.archivedData(withRootObject: requestA)) as! NSURLRequest
237+
XCTAssertEqual(requestA, requestB, "Archived then unarchived url request must be equal.")
238+
//Check `.httpBody` as it is not checked in `isEqual(_:)`
239+
XCTAssertNotNil(requestB.httpBody)
240+
XCTAssertEqual(3, requestB.httpBody!.count)
241+
XCTAssertEqual(requestB.httpBody, requestA.httpBody)
242+
}
206243
}

0 commit comments

Comments
 (0)