Last active
April 23, 2016 22:22
-
-
Save crayment/97551a30f514a8cd92b2d2e89cb0d286 to your computer and use it in GitHub Desktop.
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
/// Custom type for the error case which includes a value. Here I end up using JSON but you could easily decode to a model on error. | |
struct MyNetworkError<T> { | |
let response: Response<NSData, NSError>? | |
let error: ErrorType | |
let value: T? | |
var statusCode: HTTPStatusCode? { | |
guard let response = response?.response else { return nil } | |
return HTTPStatusCode(intValue: response.statusCode) | |
} | |
init(response: Response<NSData, NSError>? = nil, error: ErrorType, value: T?) { | |
self.response = response | |
self.error = error | |
self.value = value | |
} | |
} | |
extension NetworkService { | |
/// Request that fulfills with a model of type T or rejects with a custom error with JSON included. | |
func request<T: JSONDecodable>(URLRequest: URLRequestConvertible) -> Task<Float, T, MyNetworkError<JSON>> { | |
return request(URLRequest).success { $0.value }.failure { value -> Task<Float, T, MyNetworkError<JSON>> in | |
let error = value.error?.error ?? NetworkService.AssertionError() // Should always have an error on failure | |
guard let data = value.error?.response?.data, let json = try? JSON(data: data) else { | |
return Task(error: MyNetworkError(response: value.error?.response, error: error, value: nil)) | |
} | |
return Task(error: MyNetworkError(response: value.error?.response, error: error, value: json)) | |
} | |
} | |
/// Request that fulfills with a model of type T or rjects with a custom error with that has a U? as it's value. | |
func request<T: JSONDecodable, U: JSONDecodable>(URLRequest: URLRequestConvertible) -> Task<Float, T, MyNetworkError<U>> { | |
return request(URLRequest).success { $0.value }.failure { value -> Task<Float, T, MyNetworkError<U>> in | |
let error = value.error?.error ?? NetworkService.AssertionError() // Should always have an error on failure | |
guard let data = value.error?.response?.data, | |
let json = try? JSON(data: data), | |
let model = try? U(json: json) | |
else { | |
return Task(error: MyNetworkError(response: value.error?.response, error: error, value: nil)) | |
} | |
return Task(error: MyNetworkError(response: value.error?.response, error: error, value: model)) | |
} | |
} | |
} |
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
class NetworkServiceExtensionsSpec: QuickSpec { | |
override func spec() { | |
beforeSuite { | |
LSNocilla.sharedInstance().start() | |
} | |
afterSuite { | |
LSNocilla.sharedInstance().stop() | |
} | |
afterEach { | |
LSNocilla.sharedInstance().clearStubs() | |
} | |
describe("NetworkService Customizations") { | |
var subject: NetworkService! | |
var testJSON: JSON! | |
var request: NSMutableURLRequest! | |
beforeEach { | |
subject = NetworkService() | |
testJSON = [ | |
"id": "1", | |
"email": "[email protected]", | |
] | |
request = NSMutableURLRequest(URL: NSURL(string: "https://example.com/path")!) | |
} | |
describe("200 OK") { | |
var task: Task<Float, User, MyNetworkError<JSON>>! | |
beforeEach { | |
stubAnyRequest().andReturn(.Code200OK).withJSON(testJSON) | |
task = subject.request(request) | |
} | |
it("task should be fulfilled") { | |
expect(task.state).toEventually(equal(TaskState.Fulfilled)) | |
} | |
it("value should be correct User") { | |
expect(task.value).toEventually(equal(User(userID: "1", email: "[email protected]"))) | |
} | |
} | |
describe("custom error type") { | |
var task: Task<Float, User, MyNetworkError<JSON>>! | |
beforeEach { | |
stubAnyRequest().andReturn(.Code404NotFound).withJSON(testJSON) | |
task = subject.request(request) | |
} | |
it("task should be rejected") { | |
expect(task.state).toEventually(equal(TaskState.Rejected)) | |
} | |
it("error should contain response data") { | |
expect({ () -> JSON? in | |
guard let data = task.errorInfo?.error?.response?.data else { return nil } | |
return try? JSON(data: data) | |
}()).toEventually(equal(testJSON)) | |
} | |
} | |
describe("custom error type as User") { | |
var task: Task<Float, User, MyNetworkError<User>>! | |
beforeEach { | |
stubAnyRequest().andReturn(.Code404NotFound).withJSON(testJSON) | |
task = subject.request(request) | |
} | |
it("task should be rejected") { | |
expect(task.state).toEventually(equal(TaskState.Rejected)) | |
} | |
it("error should contain response data") { | |
expect(task.errorInfo?.error?.value).toEventually(equal(User(userID: "1", email: "[email protected]"))) | |
} | |
} | |
} | |
} | |
} |
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
struct User { | |
let userID: String | |
let email: String | |
} | |
extension User: JSONDecodable { | |
init(json: JSON) throws { | |
// Extract id as a string or a int | |
if let userID = try? json.string("id") { | |
self.userID = userID | |
} else { | |
let userID = try json.int("id") | |
self.userID = String(userID) | |
} | |
email = try json.string("email") | |
} | |
} | |
extension User: JSONEncodable { | |
func toJSON() -> JSON { | |
return .Dictionary([ | |
"id": .String(userID), | |
"email": .String(email) | |
]) | |
} | |
} | |
extension User: Equatable {} | |
func ==(lhs: User, rhs: User) -> Bool { | |
return lhs.email == rhs.email && | |
lhs.userID == rhs.userID | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment