Merge pull request #1079 from ianpartridge/nsdecimalnumberhandler
NSDecimalNumberHandler: default is now a property
diff --git a/Foundation/JSONEncoder.swift b/Foundation/JSONEncoder.swift
index 43fc52c..c6f09e0 100644
--- a/Foundation/JSONEncoder.swift
+++ b/Foundation/JSONEncoder.swift
@@ -119,7 +119,7 @@
///
/// - parameter value: The value to encode.
/// - returns: A new `Data` value containing the encoded JSON data.
- /// - throws: `EncodingError.invalidValue` if a non-comforming floating-point value is encountered during encoding, and the encoding strategy is `.throw`.
+ /// - throws: `EncodingError.invalidValue` if a non-conforming floating-point value is encountered during encoding, and the encoding strategy is `.throw`.
/// - throws: An error if any value throws an error during encoding.
open func encode<T : Encodable>(_ value: T) throws -> Data {
let encoder = _JSONEncoder(options: self.options)
diff --git a/Foundation/LengthFormatter.swift b/Foundation/LengthFormatter.swift
index a3c6bb9..ffb72b6 100644
--- a/Foundation/LengthFormatter.swift
+++ b/Foundation/LengthFormatter.swift
@@ -69,7 +69,7 @@
//Extract the number from the measurement
let numberInUnit = unitMeasurement.value
- if isForPersonHeightUse && !numberFormatter.locale.sr3202_fix_isMetricSystemLocale() {
+ if isForPersonHeightUse && !numberFormatter.locale.usesMetricSystem {
let feet = numberInUnit.rounded(.towardZero)
let feetString = string(fromValue: feet, unit: .foot)
@@ -123,7 +123,7 @@
/// - Parameter numberInMeters: the magnitude in terms of meters
/// - Returns: Returns the appropriate unit
private func unit(fromMeters numberInMeters: Double) -> Unit {
- if numberFormatter.locale.sr3202_fix_isMetricSystemLocale() {
+ if numberFormatter.locale.usesMetricSystem {
//Person height is always returned in cm for metric system
if isForPersonHeightUse { return .centimeter }
diff --git a/Foundation/Locale.swift b/Foundation/Locale.swift
index 29b58ba..7b81c63 100644
--- a/Foundation/Locale.swift
+++ b/Foundation/Locale.swift
@@ -221,7 +221,7 @@
/// -seealso: MeasurementFormatter
public var usesMetricSystem: Bool {
// NSLocale should not return nil here, but just in case
- if let result = (_wrapped.object(forKey: .usesMetricSystem) as? NSNumber)?.boolValue {
+ if let result = _wrapped.object(forKey: .usesMetricSystem) as? Bool {
return result
} else {
return false
diff --git a/Foundation/MassFormatter.swift b/Foundation/MassFormatter.swift
index 101856a..3d4b82f 100644
--- a/Foundation/MassFormatter.swift
+++ b/Foundation/MassFormatter.swift
@@ -134,7 +134,7 @@
/// - Parameter numberInKilograms: the magnitude in terms of kilograms
/// - Returns: Returns the appropriate unit
private func convertedUnit(fromKilograms numberInKilograms: Double) -> Unit {
- if numberFormatter.locale.sr3202_fix_isMetricSystemLocale() {
+ if numberFormatter.locale.usesMetricSystem {
if numberInKilograms > 1.0 || numberInKilograms <= 0.0 {
return .kilogram
} else {
@@ -236,22 +236,3 @@
.pound: "pounds",
.stone: "stones"]
}
-
-internal extension Locale {
- /// TODO: Replace calls to the below function to use Locale.usesMetricSystem
- /// Temporary workaround due to unpopulated Locale attributes
- /// See https://bugs.swift.org/browse/SR-3202
- internal func sr3202_fix_isMetricSystemLocale() -> Bool {
- switch self.identifier {
- case "en_US": return false
- case "en_US_POSIX": return false
- case "haw_US": return false
- case "es_US": return false
- case "chr_US": return false
- case "my_MM": return false
- case "en_LR": return false
- case "vai_LR": return false
- default: return true
- }
- }
-}
diff --git a/Foundation/NSURLProtocol.swift b/Foundation/NSURLProtocol.swift
index 74bf665..67f6bb2 100644
--- a/Foundation/NSURLProtocol.swift
+++ b/Foundation/NSURLProtocol.swift
@@ -159,6 +159,9 @@
private static var _registeredProtocolClasses = [AnyClass]()
private static var _classesLock = NSLock()
+
+ //TODO: The right way to do this is using URLProtocol.property(forKey:in) and URLProtocol.setProperty(_:forKey:in)
+ var properties: [URLProtocol._PropertyKey: Any] = [:]
/*!
@method initWithRequest:cachedResponse:client:
@abstract Initializes an NSURLProtocol given request,
diff --git a/Foundation/NSURLSession/NSURLSessionTask.swift b/Foundation/NSURLSession/NSURLSessionTask.swift
index f843780..bcf06da 100644
--- a/Foundation/NSURLSession/NSURLSessionTask.swift
+++ b/Foundation/NSURLSession/NSURLSessionTask.swift
@@ -526,7 +526,20 @@
extension _ProtocolClient : URLProtocolClient {
func urlProtocol(_ protocol: URLProtocol, didReceive response: URLResponse, cacheStoragePolicy policy: URLCache.StoragePolicy) {
- `protocol`.task?.response = response
+ guard let task = `protocol`.task else { fatalError("Received response, but there's no task.") }
+ task.response = response
+ let session = task.session as! URLSession
+ guard let dataTask = task as? URLSessionDataTask else { return }
+ switch session.behaviour(for: task) {
+ case .taskDelegate(let delegate as URLSessionDataDelegate):
+ session.delegateQueue.addOperation {
+ delegate.urlSession(session, dataTask: dataTask, didReceive: response, completionHandler: { _ in
+ URLSession.printDebug("warning: Ignoring disposition from completion handler.")
+ })
+ }
+ case .noDelegate, .taskDelegate, .dataCompletionHandler, .downloadCompletionHandler:
+ break
+ }
}
func urlProtocolDidFinishLoading(_ protocol: URLProtocol) {
@@ -543,13 +556,14 @@
task.state = .completed
session.taskRegistry.remove(task)
case .dataCompletionHandler(let completion):
- let data = Data()
- guard let client = `protocol`.client else { fatalError() }
- client.urlProtocol(`protocol`, didLoad: data)
- return
+ session.delegateQueue.addOperation {
+ completion(`protocol`.properties[URLProtocol._PropertyKey.responseData] as? Data ?? Data(), task.response, nil)
+ task.state = .completed
+ session.taskRegistry.remove(task)
+ }
case .downloadCompletionHandler(let completion):
session.delegateQueue.addOperation {
- completion(task.currentRequest?.url, task.response, nil)
+ completion(`protocol`.properties[URLProtocol._PropertyKey.temporaryFileURL] as? URL, task.response, nil)
task.state = .completed
session.taskRegistry.remove(task)
}
@@ -565,15 +579,15 @@
}
func urlProtocol(_ protocol: URLProtocol, didLoad data: Data) {
+ `protocol`.properties[.responseData] = data
guard let task = `protocol`.task else { fatalError() }
guard let session = task.session as? URLSession else { fatalError() }
switch session.behaviour(for: task) {
- case .dataCompletionHandler(let completion):
- guard let s = task.session as? URLSession else { fatalError() }
- s.delegateQueue.addOperation {
- completion(data, task.response, nil)
- task.state = .completed
- s.taskRegistry.remove(task)
+ case .taskDelegate(let delegate):
+ let dataDelegate = delegate as? URLSessionDataDelegate
+ let dataTask = task as? URLSessionDataTask
+ session.delegateQueue.addOperation {
+ dataDelegate?.urlSession(session, dataTask: dataTask!, didReceive: data)
}
default: return
}
@@ -615,3 +629,10 @@
NSUnimplemented()
}
}
+
+extension URLProtocol {
+ enum _PropertyKey: String {
+ case responseData
+ case temporaryFileURL
+ }
+}
diff --git a/Foundation/NSURLSession/http/EasyHandle.swift b/Foundation/NSURLSession/http/EasyHandle.swift
index 9cb2cad..2fe0ca1 100644
--- a/Foundation/NSURLSession/http/EasyHandle.swift
+++ b/Foundation/NSURLSession/http/EasyHandle.swift
@@ -240,9 +240,16 @@
try! CFURLSession_easy_setopt_int64(rawHandle, CFURLSessionOptionINFILESIZE_LARGE, length).asError()
}
- func set(timeout value: Int) {
+ func set(timeout value: Int) {
try! CFURLSession_easy_setopt_long(rawHandle, CFURLSessionOptionTIMEOUT, value).asError()
- }
+ }
+
+ func getTimeoutIntervalSpent() -> Double {
+ var timeSpent = Double()
+ CFURLSession_easy_getinfo_double(rawHandle, CFURLSessionInfoTOTAL_TIME, &timeSpent)
+ return timeSpent / 1000
+ }
+
}
fileprivate func printLibcurlDebug(handle: CFURLSessionEasyHandle, type: CInt, data: UnsafeMutablePointer<Int8>, size: Int, userInfo: UnsafeMutableRawPointer?) -> CInt {
diff --git a/Foundation/NSURLSession/http/HTTPURLProtocol.swift b/Foundation/NSURLSession/http/HTTPURLProtocol.swift
index 39d030b..efb647b 100644
--- a/Foundation/NSURLSession/http/HTTPURLProtocol.swift
+++ b/Foundation/NSURLSession/http/HTTPURLProtocol.swift
@@ -679,10 +679,10 @@
}
self.client?.urlProtocol(self, didLoad: data)
self.internalState = .taskCompleted
- return
}
- if case .toFile(_, let fileHandle?) = bodyDataDrain {
+ if case .toFile(let url, let fileHandle?) = bodyDataDrain {
+ self.properties[.temporaryFileURL] = url
fileHandle.closeFile()
}
self.client?.urlProtocolDidFinishLoading(self)
@@ -901,6 +901,8 @@
components.path = targetURL.relativeString
guard let urlString = components.string else { fatalError("Invalid URL") }
request.url = URL(string: urlString)
+ let timeSpent = easyHandle.getTimeoutIntervalSpent()
+ request.timeoutInterval = fromRequest.timeoutInterval - timeSpent
return request
}
}
diff --git a/TestFoundation/TestLengthFormatter.swift b/TestFoundation/TestLengthFormatter.swift
index 1255a4f..05e7bcb 100644
--- a/TestFoundation/TestLengthFormatter.swift
+++ b/TestFoundation/TestLengthFormatter.swift
@@ -67,7 +67,7 @@
}
func test_stringFromMetersMetric() {
- formatter.numberFormatter.locale = Locale(identifier: "en_UK")
+ formatter.numberFormatter.locale = Locale(identifier: "en_GB")
XCTAssertEqual(formatter.string(fromMeters: -10000), "-10 km")
XCTAssertEqual(formatter.string(fromMeters: -1), "-0.001 km")
XCTAssertEqual(formatter.string(fromMeters: 0.00001), "0.01 mm")
@@ -85,7 +85,7 @@
}
func test_stringFromMetersMetricPersonHeight() {
- formatter.numberFormatter.locale = Locale(identifier: "en_UK")
+ formatter.numberFormatter.locale = Locale(identifier: "en_GB")
formatter.isForPersonHeightUse = true
XCTAssertEqual(formatter.string(fromMeters: -1), "-100 cm")
XCTAssertEqual(formatter.string(fromMeters: 0.001), "0.1 cm")
diff --git a/TestFoundation/TestNSTimer.swift b/TestFoundation/TestNSTimer.swift
index 7fcb31d..b80aede 100644
--- a/TestFoundation/TestNSTimer.swift
+++ b/TestFoundation/TestNSTimer.swift
@@ -59,7 +59,7 @@
XCTAssertEqual(timer.timeInterval, interval)
let currentInterval = Date().timeIntervalSince1970
- XCTAssertEqual(currentInterval, previousInterval + interval, accuracy: 0.01)
+ XCTAssertEqual(currentInterval, previousInterval + interval, accuracy: 0.2)
previousInterval = currentInterval
flag += 1
diff --git a/TestFoundation/TestNSURLSession.swift b/TestFoundation/TestNSURLSession.swift
index 630b174..285ddd4 100644
--- a/TestFoundation/TestNSURLSession.swift
+++ b/TestFoundation/TestNSURLSession.swift
@@ -38,7 +38,9 @@
("test_verifyHttpAdditionalHeaders", test_verifyHttpAdditionalHeaders),
("test_timeoutInterval", test_timeoutInterval),
("test_customProtocol", test_customProtocol),
+ ("test_customProtocolResponseWithDelegate", test_customProtocolResponseWithDelegate),
("test_httpRedirection", test_httpRedirection),
+ ("test_httpRedirectionTimeout", test_httpRedirectionTimeout),
]
}
@@ -343,6 +345,14 @@
task.resume()
waitForExpectations(timeout: 12)
}
+
+ func test_customProtocolResponseWithDelegate() {
+ let url = URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/Peru")!
+ let d = DataTask(with: expectation(description: "Custom protocol with delegate"), protocolClasses: [CustomProtocol.self])
+ d.responseReceivedExpectation = expectation(description: "A response wasn't received")
+ d.run(with: url)
+ waitForExpectations(timeout: 12)
+ }
func test_httpRedirection() {
let urlString = "http://127.0.0.1:\(TestURLSession.serverPort)/UnitedStates"
@@ -351,6 +361,23 @@
d.run(with: url)
waitForExpectations(timeout: 12)
}
+
+ func test_httpRedirectionTimeout() {
+ var req = URLRequest(url: URL(string: "http://127.0.0.1:\(TestURLSession.serverPort)/UnitedStates")!)
+ req.timeoutInterval = 3
+ let config = URLSessionConfiguration.default
+ var expect = expectation(description: "download task with handler")
+ let session = URLSession(configuration: config, delegate: nil, delegateQueue: nil)
+ let task = session.dataTask(with: req) { data, response, error in
+ defer { expect.fulfill() }
+ if let e = error as? URLError {
+ XCTAssertEqual(e.code, .timedOut, "Unexpected error code")
+ return
+ }
+ }
+ task.resume()
+ waitForExpectations(timeout: 12)
+ }
}
class SessionDelegate: NSObject, URLSessionDelegate {
@@ -369,16 +396,22 @@
var session: URLSession! = nil
var task: URLSessionDataTask! = nil
var cancelExpectation: XCTestExpectation?
+ var responseReceivedExpectation: XCTestExpectation?
+ var protocols: [AnyClass]?
public var error = false
- init(with expectation: XCTestExpectation) {
+ init(with expectation: XCTestExpectation, protocolClasses: [AnyClass]? = nil) {
dataTaskExpectation = expectation
+ protocols = protocolClasses
}
func run(with request: URLRequest) {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 8
+ if let customProtocols = protocols {
+ config.protocolClasses = customProtocols
+ }
session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
task = session.dataTask(with: request)
task.resume()
@@ -387,6 +420,9 @@
func run(with url: URL) {
let config = URLSessionConfiguration.default
config.timeoutIntervalForRequest = 8
+ if let customProtocols = protocols {
+ config.protocolClasses = customProtocols
+ }
session = URLSession(configuration: config, delegate: self, delegateQueue: nil)
task = session.dataTask(with: url)
task.resume()
@@ -401,6 +437,14 @@
public func urlSession(_ session: URLSession, dataTask: URLSessionDataTask, didReceive data: Data) {
capital = String(data: data, encoding: String.Encoding.utf8)!
}
+
+ public func urlSession(_ session: URLSession,
+ dataTask: URLSessionDataTask,
+ didReceive response: URLResponse,
+ completionHandler: @escaping (URLSession.ResponseDisposition) -> Void) {
+ guard responseReceivedExpectation != nil else { return }
+ responseReceivedExpectation!.fulfill()
+ }
}
extension DataTask : URLSessionTaskDelegate {