-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
1683d92
commit 30a49e4
Showing
8 changed files
with
404 additions
and
12 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
// | ||
// JSONNetworkResponse.swift | ||
// NetworkOperation | ||
// | ||
// Created by Alasdair Law on 18/12/2016. | ||
// Copyright © 2016 Alasdair Law. All rights reserved. | ||
// | ||
|
||
public struct JSONResponse: NetworkResponse { | ||
public typealias T = Any | ||
|
||
public let response: URLResponse | ||
public let data: T | ||
|
||
public init(response: URLResponse, data: Data) throws { | ||
self.response = response | ||
|
||
self.data = try JSONSerialization.jsonObject(with: data) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,125 @@ | ||
// | ||
// NetworkOperation.swift | ||
// NetworkOperation | ||
// | ||
// Created by Alasdair Law on 17/12/2016. | ||
// Copyright © 2016 Alasdair Law. All rights reserved. | ||
// | ||
|
||
/** | ||
NSOperation subclass which performs a network request. | ||
*/ | ||
public class NetworkOperation<T: NetworkResponse>: Operation { | ||
private let session: URLSession | ||
private let request: URLRequest | ||
private let networkCompletion: (_ response: T?, _ error: Error?) -> Void | ||
|
||
private var task: URLSessionTask! | ||
|
||
private var _isFinished: Bool | ||
override public var isFinished: Bool { | ||
get { | ||
return self._isFinished | ||
} | ||
set { | ||
self.willChangeValue(forKey: NSStringFromSelector(#selector(setter: isFinished))) | ||
self._isFinished = newValue | ||
self.didChangeValue(forKey: NSStringFromSelector(#selector(setter: isFinished))) | ||
} | ||
} | ||
|
||
private var _isExecuting: Bool | ||
override public var isExecuting: Bool { | ||
get { | ||
return self._isExecuting | ||
} | ||
set { | ||
self.willChangeValue(forKey: NSStringFromSelector(#selector(setter: isExecuting))) | ||
self._isExecuting = newValue | ||
self.didChangeValue(forKey: NSStringFromSelector(#selector(setter: isExecuting))) | ||
} | ||
} | ||
|
||
override public var isAsynchronous: Bool { | ||
return true | ||
} | ||
|
||
/** | ||
Initialises a NetworkOperation. | ||
|
||
- Parameters: | ||
- request: The request the network operation should perform. | ||
- completion: Block called after the network request has completed, or when the operation was cancelled. | ||
*/ | ||
init(session: URLSession, request: URLRequest, completion: @escaping (_ response: T?, _ error: Error?) -> Void) { | ||
self.session = session | ||
self.networkCompletion = completion | ||
self._isFinished = false | ||
self._isExecuting = false | ||
|
||
self.request = request | ||
|
||
super.init() | ||
|
||
self.task = self.session.dataTask(with: self.request) { [unowned self] (data, response, error) in | ||
try? self.handle(data: data, response: response, error: error, nr: T.self) | ||
} | ||
} | ||
|
||
/** | ||
Initialises a NetworkOperation. | ||
|
||
- Parameters: | ||
- url: The URL the network operation should point to. | ||
- completion: Block called after the network request has completed, or when the operation was cancelled. | ||
*/ | ||
convenience init(session: URLSession, url: URL, completion: @escaping (_ response: T?, _ error: Error?) -> Void) { | ||
self.init(session: session, request: URLRequest(url: url), completion: completion) | ||
} | ||
|
||
override public func start() { | ||
if self.isCancelled { | ||
self.isFinished = true | ||
} else if !self.isExecuting { | ||
self.isExecuting = true | ||
|
||
self.task.resume() | ||
} | ||
} | ||
|
||
override public func cancel() { | ||
self.isFinished = true | ||
|
||
self.task.cancel() | ||
|
||
super.cancel() | ||
} | ||
|
||
// MARK: Private | ||
|
||
private func handle(data: Data?, response: URLResponse?, error: Error?, nr: T.Type) throws { | ||
var networkResponse: T? | ||
defer { | ||
self.completeNetworkOperation(response: networkResponse, error: error) | ||
} | ||
|
||
guard let response = response as? HTTPURLResponse else { | ||
return | ||
} | ||
guard let data = data else { | ||
return | ||
} | ||
networkResponse = try nr.init(response: response, data: data) | ||
} | ||
|
||
private func completeNetworkOperation(response: T?, error: Error?) { | ||
if !self.isCancelled { | ||
self.networkCompletion(response, error) | ||
} | ||
|
||
if self.isExecuting { | ||
self.isExecuting = false | ||
self.isFinished = true | ||
} | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
// | ||
// NetworkResponse.swift | ||
// NetworkOperation | ||
// | ||
// Created by Alasdair Law on 18/12/2016. | ||
// Copyright © 2016 Alasdair Law. All rights reserved. | ||
// | ||
|
||
public protocol NetworkResponse { | ||
associatedtype T | ||
|
||
var response: URLResponse { get } | ||
var data: T { get } | ||
|
||
init(response: URLResponse, data: Data) throws | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,53 @@ | ||
// | ||
// MockSessionDataTask.swift | ||
// NetworkOperation | ||
// | ||
// Created by Alasdair Law on 19/12/2016. | ||
// Copyright © 2016 Alasdair Law. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
internal protocol MockResponse { | ||
static func response(from url: URL) -> CompletionValues | ||
} | ||
|
||
internal enum MockSessionDataTaskError: Error { | ||
case cancelled | ||
} | ||
|
||
internal class MockSessionDataTask: URLSessionDataTask { | ||
private let mockResponse: CompletionValues | ||
private let completion: Completion | ||
|
||
private var _state: URLSessionTask.State | ||
override internal var state: URLSessionTask.State { | ||
get { | ||
return self._state | ||
} | ||
set { | ||
self._state = newValue | ||
} | ||
} | ||
|
||
init(mockResponse: CompletionValues, completion: @escaping Completion) { | ||
self.mockResponse = mockResponse | ||
self.completion = completion | ||
self._state = .running | ||
|
||
super.init() | ||
} | ||
|
||
override internal func resume() { | ||
let response = self.mockResponse | ||
|
||
if self._state == .running { | ||
self.completion(response.0, response.1, response.2) | ||
} | ||
} | ||
|
||
override internal func cancel() { | ||
self.state = .canceling | ||
self.completion((nil, nil, MockSessionDataTaskError.cancelled)) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
// | ||
// MockURLSession+JSON.swift | ||
// NetworkOperation | ||
// | ||
// Created by Alasdair Law on 18/12/2016. | ||
// Copyright © 2016 Alasdair Law. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
struct MockJSONResponse: MockResponse { | ||
static func response(from url: URL) -> (Data?, URLResponse?, Error?) { | ||
let jsonObject = [ | ||
"test": "success" | ||
] | ||
let data = try! JSONSerialization.data(withJSONObject: jsonObject, options: []) | ||
let response = HTTPURLResponse(url: url, statusCode: 200, httpVersion: "https", headerFields: nil) | ||
|
||
return (data, response, nil) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,31 @@ | ||
// | ||
// MockURLSession.swift | ||
// NetworkOperation | ||
// | ||
// Created by Alasdair Law on 18/12/2016. | ||
// Copyright © 2016 Alasdair Law. All rights reserved. | ||
// | ||
|
||
import Foundation | ||
|
||
internal typealias CompletionValues = (Data?, URLResponse?, Error?) | ||
internal typealias Completion = (CompletionValues) -> Void | ||
|
||
internal class MockSession: URLSession { | ||
private var mocks = [URL: CompletionValues]() | ||
|
||
internal func mock(url: URL, with response: (URL) -> CompletionValues) { | ||
self.mocks[url] = response(url) | ||
} | ||
|
||
internal func removeMocks() { | ||
self.mocks.removeAll() | ||
} | ||
|
||
override internal func dataTask(with request: URLRequest, completionHandler: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionDataTask { | ||
guard let mockResponse = self.mocks[request.url!] else { | ||
return super.dataTask(with: request, completionHandler: completionHandler) | ||
} | ||
return MockSessionDataTask(mockResponse: mockResponse, completion: completionHandler) | ||
} | ||
} |
Oops, something went wrong.