Skip to content
This repository has been archived by the owner on Apr 7, 2021. It is now read-only.

Commit

Permalink
Added init to allow usage of pre-made URLSession instance
Browse files Browse the repository at this point in the history
  • Loading branch information
Aleksandar Vacic committed Sep 27, 2017
1 parent 0e3de92 commit 28e7455
Showing 1 changed file with 61 additions and 16 deletions.
77 changes: 61 additions & 16 deletions NetworkOperation.swift
Expand Up @@ -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)
}

Expand All @@ -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] = [:]
}


Expand Down Expand Up @@ -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
}
Expand Down

0 comments on commit 28e7455

Please sign in to comment.