// // Resource.swift // Pods // // Created by Mauro Bender on 13/8/16. // // import Alamofire public protocol JSONSerializable { init( fromJSON json: [ String: AnyObject ] ) func toJSON() -> [ String: AnyObject ] } public enum ApiError: ErrorType { case Network( error: NSError, response: NSURLResponse ) case JSONSerialization(error: NSError) case Unknown( error: NSError ) } public class Resource { public let api: Api public let path: String init( api: Api, path: String ) { self.api = api self.path = path } } // MARK: REQUESTS extension Resource { public func request( method: ApiMethod, path: String? = nil, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = .URL, headers: [String: String]? = nil, completion: Response -> Void ) { let requestPath = path != nil ? "\(self.path)/\(path!)" : self.path api.request( method, path: requestPath, parameters: parameters, encoding: encoding, headers: headers, completion: completion ) } public func get( path: String? = nil, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = .URL, headers: [String: String]? = nil, completion: Response -> Void ) { return request( .GET, path: path, parameters: parameters, encoding: encoding, headers: headers, completion: completion) } public func post( path: String? = nil, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = .URL, headers: [String: String]? = nil, completion: Response -> Void ) { return request( .POST, path: path, parameters: parameters, encoding: encoding, headers: headers, completion: completion) } public func put( path: String? = nil, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = .URL, headers: [String: String]? = nil, completion: Response -> Void ) { return request( .PUT, path: path, parameters: parameters, encoding: encoding, headers: headers, completion: completion) } public func delete( path: String? = nil, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = .URL, headers: [String: String]? = nil, completion: Response -> Void ) { return request( .DELETE, path: path, parameters: parameters, encoding: encoding, headers: headers, completion: completion) } } // MARK: CRUD extension Resource { public func item( id: AnyObject, parameters: [ String: String ]? = nil, headers: [ String: String]? = nil, completion: ( ( T?, error: ApiError? ) -> Void ) ) { get( "\(id)", parameters: parameters, headers: headers ) { response in self.handleItemResult( response, handler: completion ) } } public func list( parameters: [ String: String ]? = nil, headers: [ String: String]? = nil, completion: ( ( [ T ]?, error: ApiError? ) -> Void ) ) { get( parameters: parameters, headers: headers ) { response in self.handleListResult( response, handler: completion ) } } public func create( item: T, parameters: [ String: String ]? = nil, headers: [ String: String]? = nil, encoding: ParameterEncoding = .JSON, completion: ( ( T?, error: ApiError? ) -> Void ) ) { var requestParameters = item.toJSON() if parameters != nil { requestParameters += parameters! } post( parameters: requestParameters, encoding: encoding, headers: headers ) { response in self.handleItemResult( response, handler: completion ) } } public func destroy( id: AnyObject, parameters: [ String: String ]? = nil, headers: [ String: String]? = nil, completion: ( ( error: ApiError? ) -> Void ) ) { delete( "\(id)", parameters: parameters, headers: headers ) { response in self.handleEmptyResult( response, handler: completion ) } } // MARK: UTILS private func handleItemResult( response: Response, handler: ( T?, error: ApiError? ) -> Void ) { switch(response.result) { case .Success(let JSON): handler( T( fromJSON: JSON as! [String : AnyObject] ), error: nil ) case .Failure(let error): handler( nil, error: handleError( response ) ) } } private func handleListResult( response: Response, handler: ( [ T ]?, error: ApiError? ) -> Void ) { switch(response.result) { case .Success(let JSON): if let JSONList = JSON as? [ [String: AnyObject] ] { let list = JSONList.map { T( fromJSON: $0 ) } handler( list, error: nil ) } case .Failure(let error): handler( nil, error: handleError( response ) ) } } private func handleEmptyResult( response: Response, handler: ( error: ApiError? ) -> Void ) { switch(response.result) { case .Success(let JSON): handler( error: nil ) case .Failure(let error): handler( error: handleError( response ) ) } } private func handleError( response: Response ) -> ApiError { let error = response.result.error! if let statusCode = response.response?.statusCode where !(200...299).contains( statusCode ) { return .Network( error: error, response: response.response! ) } else if error.code == -6006 { return .JSONSerialization( error: error ) } return .Unknown( error: error ) } } // MARK: NESTED RESOURCES extension Resource { public func member( memberID: AnyObject ) -> Resource { let memberPath = resourceMemberPath( memberID ) return Resource( api: api, path: memberPath ) } public func resource( resourcePath: String ) -> Resource { let nestedPath = nestedResourcePath( resourcePath ) return Resource( api: api, path: nestedPath ) } func resourceMemberPath( memberId: AnyObject ) -> String { return "\(path)/\(memberId)" } func nestedResourcePath( nestedResourcePath: String ) -> String { return "\(path)/\(nestedResourcePath)" } }