Resource.swift 6.07 KB
Newer Older
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//
//  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 ]
}

16
17
18
19
20
21
public enum ApiError: ErrorType {
	case Network( error: NSError, response: NSURLResponse )
	case JSONSerialization(error: NSError)
	case Unknown( error: NSError )
}

22
public class Resource {
23
24
25
26
27
28
29
	public let api: Api
	public let path: String
	
	init( api: Api, path: String ) {
		self.api  = api
		self.path = path
	}
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
}

// 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<AnyObject, NSError> -> 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<AnyObject, NSError> -> 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<AnyObject, NSError> -> 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<AnyObject, NSError> -> 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<AnyObject, NSError> -> Void )
	{
		return request( .DELETE, path: path, parameters: parameters, encoding: encoding,
		                headers: headers, completion: completion)
	}
}

// MARK: CRUD
extension Resource {
	public func item<T: JSONSerializable>( 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 )
		}
	}
81
	
82
83
84
85
86
87
88
89
	public func list<T: JSONSerializable>( 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 )
		}
	}
	
90
91
92
93
94
95
96
97
98
99
100
	public func create<T: JSONSerializable>( 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 )
		}
	}
101
	
102
103
104
105
106
107
108
109
110
	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 )
		}
	}
	
	
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
	// MARK: UTILS
	private func handleItemResult<T: JSONSerializable>( response: Response<AnyObject, NSError>,
	                              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<T: JSONSerializable>( response: Response<AnyObject, NSError>,
	                              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 ) )
134
135
136
137
138
139
140
141
142
143
		}
	}
	
	private func handleEmptyResult( response: Response<AnyObject, NSError>, handler: ( error: ApiError? ) -> Void )
	{
		switch(response.result) {
		case .Success(let JSON):
			handler( error: nil )
		case .Failure(let error):
			handler( error: handleError( response ) )
144
145
146
147
148
149
150
151
152
153
154
155
156
157
		}
	}
	
	private func handleError( response: Response<AnyObject, NSError> ) -> 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 )
	}
158
159
160
161
}

// MARK: NESTED RESOURCES
extension Resource {
162
163
164
165
166
167
168
169
170
171
	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 )
	}
	
172
173
174
	func resourceMemberPath( memberId: AnyObject ) -> String {
		return "\(path)/\(memberId)"
	}
175
176
177
178
179
	
	func nestedResourcePath( nestedResourcePath: String ) -> String {
		return "\(path)/\(nestedResourcePath)"
	}
}