Permalink
Browse files

Added init to allow usage of pre-made URLSession instance

  • Loading branch information...
radianttap committed Sep 27, 2017
1 parent 0e3de92 commit 28e7455a05f302f8ef265209af6eee97133936f1
Showing with 61 additions and 16 deletions.
  1. +61 −16 NetworkOperation.swift
View
@@ -10,28 +10,62 @@ import Foundation
/// Subclass of [AsyncOperation](https://github.com/radianttap/Swift-Essentials/blob/master/Operation/AsyncOperation.swift)
/// that handles all aspects of direct data download over network.
/// It uses QoS.utility by default.
///
/// It will automatically handle Auth challenges, using URLSession.serverTrustPolicy. Returns `userCancelledAuthentication` error if that fails.
///
/// In the simplest case, you can supply just URLRequest and a `Callback` which accepts `NetworkPayload` instance.
///
/// Or you can also supply custom URLSessionConfiguration just for this request.
/// Or you can also supply custom URLSessionConfiguration just for this request. Or URLSession instance if you have it somewhere else.
final class NetworkOperation: AsyncOperation {
typealias Callback = (NetworkPayload) -> Void
/// Set network start timestamp, creates URLSessionDataTask and starts it (resume)
override func workItem() {
payload.start()
task = localURLSession.dataTask(with: payload.urlRequest)
if localURLSession == nil {
if self.isUsingURLSessionDelegate {
localURLSession = URLSession(configuration: self.urlSessionConfiguration,
delegate: self,
delegateQueue: nil)
} else {
localURLSession = URLSession(configuration: self.urlSessionConfiguration)
}
}
if isUsingURLSessionDelegate {
task = localURLSession.dataTask(with: payload.urlRequest)
} else {
task = localURLSession.dataTask(with: payload.urlRequest, completionHandler: {
[weak self] data, response, error in
guard let `self` = self else { return }
self.payload.response = response as? HTTPURLResponse
if let e = error {
self.payload.error = .urlError(e as? URLError)
} else {
self.payload.data = data
}
self.finish()
})
}
taskMetrics[.tsStart] = Date()
task?.resume()
}
fileprivate func finish() {
if isUsingURLSessionDelegate {
localURLSession.invalidateAndCancel()
}
taskMetrics[.tsEnd] = Date()
payload.end()
markFinished()
// log(level: .debug, "Metrics:\n\( NetworkTaskMetric.printout(taskMetrics) )")
callback(payload)
}
@@ -49,43 +83,52 @@ final class NetworkOperation: AsyncOperation {
}
/// Designated initializer
/// Designated initializer, allows to create one URLSession per Operation. Can be used with URLSessionDelegate or with completion handler.
///
/// - Parameters:
/// - request: `URLRequest` value to execute
/// - urlRequest: `URLRequest` value to execute
/// - urlSessionConfiguration: `URLSessionConfiguration` for this particular network call. Fallbacks to `default` if not specified
/// - callback: A closure to pass the result back
init(urlRequest: URLRequest,
urlSessionConfiguration: URLSessionConfiguration = URLSessionConfiguration.default,
callback: @escaping (NetworkPayload) -> Void)
{
init(urlRequest: URLRequest, urlSessionConfiguration: URLSessionConfiguration = URLSessionConfiguration.default, usesDelegate: Bool = false, callback: @escaping (NetworkPayload) -> Void) {
self.payload = NetworkPayload(urlRequest: urlRequest)
self.callback = callback
self.urlSessionConfiguration = urlSessionConfiguration
self.isUsingURLSessionDelegate = usesDelegate
super.init()
}
self.qualityOfService = .utility
/// Designated initializer, uses supplied URLSession instance. Always uses completionHandler form of URLSessionDataTask
///
/// - Parameters:
/// - urlRequest: `URLRequest` value to execute
/// - urlSession: URLSession instance to use for this Operation
/// - callback: A closure to pass the result back
init(urlRequest: URLRequest, urlSession: URLSession, callback: @escaping (NetworkPayload) -> Void) {
self.payload = NetworkPayload(urlRequest: urlRequest)
self.callback = callback
self.urlSessionConfiguration = urlSession.configuration
self.localURLSession = urlSession
self.isUsingURLSessionDelegate = false
super.init()
}
fileprivate(set) var payload: NetworkPayload
private(set) var callback: Callback
private(set) var isUsingURLSessionDelegate = false
fileprivate var incomingData = Data()
/// Configuration to use for the URLSession that will handle `urlRequest`
private(set) var urlSessionConfiguration : URLSessionConfiguration
/// URLSession is built for each request. Delegate calls are handled internally
fileprivate var localURLSession: URLSession {
return URLSession(configuration: urlSessionConfiguration,
delegate: self,
delegateQueue: nil)
}
fileprivate var localURLSession: URLSession!
/// Actual network task, generated by `localURLSession`
fileprivate var task: URLSessionDataTask?
var allowEmptyData: Bool = true
fileprivate var taskMetrics: [NetworkTaskMetric: Any] = [:]
}
@@ -152,7 +195,9 @@ extension NetworkOperation: URLSessionDataDelegate {
payload.error = .urlError(e as? URLError)
} else {
if incomingData.isEmpty {
payload.error = .noData
if !allowEmptyData {
payload.error = .noData
}
} else {
payload.data = incomingData
}

0 comments on commit 28e7455

Please sign in to comment.