Skip to content

Instantly share code, notes, and snippets.

@crayment
Last active April 23, 2016 22:22
Show Gist options
  • Save crayment/97551a30f514a8cd92b2d2e89cb0d286 to your computer and use it in GitHub Desktop.
Save crayment/97551a30f514a8cd92b2d2e89cb0d286 to your computer and use it in GitHub Desktop.
/// 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))
}
}
}
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": "user@example.com",
]
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: "user@example.com")))
}
}
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: "user@example.com")))
}
}
}
}
}
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