-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathServerRequest.swift
More file actions
327 lines (254 loc) · 16.5 KB
/
ServerRequest.swift
File metadata and controls
327 lines (254 loc) · 16.5 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
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
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
//
// ServerRequest.swift
//
//
// Created by Umar Farooque.
// All rights reserved.
//
import UIKit
protocol ServerRequestDelegate : class {
/**
requestFinishedWithResult method
- important: This method will give the response from your server which conform to the dictionary types that can be used for parsing data into your models.
- returns: Return type is void.
- parameter responseDictionary: This is the data that is fetched from the server and is of dictionary type.
- parameter apiCallType: This is return type of the API that is used for checking and correctly parsing the data.
- parameter response: This is the response returned by the server and could be used to get more detailed information like response status code, etc.
This is the success method called when the server's response code is not null and status code is of series 2XX.
*/
func requestFinishedWithResult(_ responseDictionary :[String:Any],apiCallType: ServerRequest.API_TYPES_NAME,response:URLResponse)->Void
/**
requestFinishedWithResultArray method
- important: This method will give the response from your server which conform to the array types that can be used for parsing data into your models.
- returns: Return type is void.
- parameter responseDictionary: This is the data that is fetched from the server and is of array type.
- parameter apiCallType: This is return type of the API that is used for checking and correctly parsing the data.
- parameter response: This is the response returned by the server and could be used to get more detailed information like response status code, etc.
This is the success method called when the server's response code is not null and status code is of series 2XX.
*/
func requestFinishedWithResultArray(_ responseArray :Array<Any>,apiCallType: ServerRequest.API_TYPES_NAME,response:URLResponse)->Void
/**
requestFinishedWithResponse method
- important: This method will give the response from your server when the response code is of the series 4XX.
- returns: Return type is void.
- parameter message: This is the message string that is fetched or derived when the server' response is of type 4XX.
- parameter apiCallType: This is return type of the API that is used for checking and correctly parsing the data.
- parameter response: This is the response returned by the server and could be used to get more detailed information like response status code, etc.
This is the failure method called when the server's response is not null and status code is of series 4XX.
*/
func requestFinishedWithResponse(_ response: URLResponse, message:String ,apiCallType:ServerRequest.API_TYPES_NAME)-> Void
/**
requestFailedWithError method
- important: This method will be called in case an error is encountered and it will give the details of the error as well as response.
- returns: Return type is void.
- parameter error: This is error that was encountered.
- parameter apiCallType: This is return type of the API that is used for checking and correctly parsing the data.
- parameter response: This is the response returned by the server and could be used to get more detailed information like response status code, etc.
This is the failure method called when the server's response is not received or some other error conditions is encountered.
*/
func requestFailedWithError(_ error: Error ,apiCallType:ServerRequest.API_TYPES_NAME,response:URLResponse?) ->Void
}
class ServerRequest: NSObject {
/**
BASE_URL
- important: This is the Base URL or in some cases the domain of your server requests. This is the part of the URL that remains constant.
*/
var BASE_URL = "" //MARK: ENTER YOUR SERVER URL
var ACCESS_TOKEN = ""
var REFRESH_TOKEN = ""
weak var delegate: ServerRequestDelegate?
var apiType: API_TYPES_NAME?
/**
TIME_OUT
- important: This is the timeout interval for the request you want to set.
*/
var TIME_OUT = 120.0
/**
API_TYPES_NAME
- important: This is the ENUM type that you will use for naming your APIs, it's of type Int.
*/
enum API_TYPES_NAME: Int {
//EXAMPLE
case loginAPI
case logoutAPI
case initialPayloadAPI
}
/**
generateUrlRequestWithURLPartParameters method
- important: This is the method that is called when initiating a server request.
- returns: Return type is void.
- parameter postParam: This is parameter where you pass the paramaters that are to be sent in the request body.
- parameter urlPartParam: This is parameter where you pass the paramaters that are to be sent in the request URL.
The urlPartParam and postParam are used for passing request parameter in url and or in the body of the request respectively.
*/
func generateUrlRequestWithURLPartParameters(_ urlPartParam:[String:Any]?, postParam:[String:Any]?)-> Void {
var serverRequestUrl = ""
let request = NSMutableURLRequest()
//MARK: ADD YOUR REQUEST HEADERS HERE
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.timeoutInterval = TIME_OUT
if postParam != nil
{
do{
let httpBodyData = try JSONSerialization.data(withJSONObject: postParam!, options: JSONSerialization.WritingOptions())
request.httpBody = httpBodyData
}
catch let error as NSError {
// SHOW ERROR
DispatchQueue.main.async(execute: {
self.delegate?.requestFailedWithError(error, apiCallType: self.apiType!,response: nil)
})
return
}
}
var urlStr: String = ""
//MARK: DEPENDING ON THE CALL, ADD APPROPRIATE URL IN CASES
switch apiType! {
case .initialPayloadAPI:
//replace with your URL string
urlStr = "/version/\(Bundle.main.infoDictionary?["CFBundleShortVersionString"] as? String ?? "")/build/\(Bundle.main.infoDictionary?["CFBundleVersion"] as? String ?? "")/app?&access_token=\(ACCESS_TOKEN)&refresh_token=\(REFRESH_TOKEN)"
request.httpMethod = "GET"
case .loginAPI:
//replace with your URL string
urlStr = "/login?access_token=\(ACCESS_TOKEN)&refresh_token=\(REFRESH_TOKEN)&date=\(urlPartParam?["asOfDate"] as? String ?? "")" //date param derived from urlPartParam
request.httpMethod = "POST"
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
case .logoutAPI:
//replace with your URL string
urlStr = "/logout?domain=self&access_token=\(ACCESS_TOKEN)&refresh_token=\(REFRESH_TOKEN)"
request.httpMethod = "GET"
default:
request.setValue("application/x-www-form-urlencoded", forHTTPHeaderField: "Content-Type")
urlStr = ""
request.httpMethod = "GET"
}
//Example of a case where you want to encode cascading "&" in URL
serverRequestUrl = "\(BASE_URL)\(urlStr)"
if apiType == ServerRequest.API_TYPES_NAME.loginAPI {
serverRequestUrl = serverRequestUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
serverRequestUrl = serverRequestUrl.replacingOccurrences(of: "&", with: "%26")
serverRequestUrl = "\(serverRequestUrl)&date=\(urlPartParam?["asOfDate"] as? String ?? "")&access_token=\(ACCESS_TOKEN)&refresh_token=\(REFRESH_TOKEN)"
}else{
//Example of a case with generic encoding in URL
serverRequestUrl = serverRequestUrl.addingPercentEncoding(withAllowedCharacters: .urlQueryAllowed)!
}
request.url = URL(string:serverRequestUrl)
self.performSessionDataTaskwithRequest(request as URLRequest)
}
/**
performSessionDataTaskwithRequest method
- important: This is the internal method that is called from generateUrlRequestWithURLPartParameters, after the request is setup including things like URL, headers, body, etc.
- returns: Return type is void.
- parameter request: This is final request that is generated to be sent to the server.
URLSession is created within this method and used further for sending and receiving the requests.
*/
func performSessionDataTaskwithRequest(_ request:URLRequest)->Void{
var resultFromServer: Any?
let responseResultData = [String:Any]()
let session : URLSession
let configuration = URLSessionConfiguration.default
//Set the configuration's cache policy depending on your requirement
configuration.requestCachePolicy = .reloadIgnoringLocalCacheData
// configuration.requestCachePolicy = .returnCacheDataElseLoad
session = URLSession(configuration: configuration, delegate: nil, delegateQueue: nil)
session.dataTask(with: request) { (data, response, error ) in
if error != nil {
DispatchQueue.main.async(execute: {
//call requestFailedWithError method
self.delegate?.requestFailedWithError(error!, apiCallType: self.apiType!,response: nil)
//invalidate the session
session.invalidateAndCancel()
})
}else{
if response != nil {
let httpResponse: HTTPURLResponse = response as! HTTPURLResponse
do{
resultFromServer = try JSONSerialization.jsonObject(with: data!, options: JSONSerialization.ReadingOptions.mutableContainers)
//MARK: SUCCESS CONDITION 2XX
if httpResponse.statusCode == 200 || httpResponse.statusCode == 201 || httpResponse.statusCode == 202 || httpResponse.statusCode == 204 || httpResponse.statusCode == 203 {
if let respArr = resultFromServer as? [Any]{
//server response is of type array
DispatchQueue.main.async(execute: {
self.delegate?.requestFinishedWithResultArray(respArr, apiCallType: self.apiType!,response: httpResponse)
})
}else if let respdict = resultFromServer as? [String : Any] {
//server response is of type dictionary
DispatchQueue.main.async(execute: {
self.delegate?.requestFinishedWithResult(respdict,apiCallType: self.apiType!,response: httpResponse)
})
}else{
//server response is of type string or other
DispatchQueue.main.async(execute: {
self.delegate?.requestFinishedWithResult(responseResultData,apiCallType: self.apiType!,response: httpResponse)
})
}
}
else {
if let respdict = resultFromServer as? [String : Any] {
DispatchQueue.main.async(execute: {
self.delegate?.requestFinishedWithResponse(httpResponse, message: respdict.debugDescription, apiCallType: self.apiType!)
})
}
//MARK: FAILURE CONDITION 4XX
if httpResponse.statusCode == 401 || httpResponse.statusCode == 402 || httpResponse.statusCode == 403 {
let messageString: String = (responseResultData.values.first as? String)!
DispatchQueue.main.async(execute: {
self.delegate?.requestFinishedWithResponse(httpResponse, message: messageString, apiCallType: self.apiType!)
})
}
else {
if let respArray = responseResultData.values.first as? NSArray {
if responseResultData.values.count > 0 && respArray.count > 0 {
let msgStr = respArray.firstObject
DispatchQueue.main.async(execute: {
self.delegate?.requestFinishedWithResponse(httpResponse, message: msgStr as! String, apiCallType: self.apiType!)
})
}
}else {
DispatchQueue.main.async(execute: {
if data != nil {
self.delegate?.requestFinishedWithResponse(httpResponse, message: String(data: data!, encoding: .utf8)! , apiCallType: self.apiType!)
}else{
self.delegate?.requestFinishedWithResponse(httpResponse, message: "Error from server", apiCallType: self.apiType!)
}
})
}
}
}
}
//MARK: ERROR HANDLING
catch let error as NSError {
DispatchQueue.main.async(execute: {
//MARK: FAILURE CONDITION 5XX, USUALLY FOR LOGOUT
if httpResponse.statusCode == 500 || httpResponse.statusCode == 502 || httpResponse.statusCode == 503 || httpResponse.statusCode == 501 {
if data != nil {
var respStr = String(data: data!, encoding: .utf8)
if respStr != nil {
respStr = respStr?.lowercased()
// if respStr!.contains("the access token provided is invalid") == true || respStr!.contains("not authenticated") == true || respStr!.contains("auth2error") == true || respStr!.contains("oauth2error") == true || respStr!.contains("invalid access token") == true || respStr!.contains("token expired") == true {
DispatchQueue.main.async(execute: {
let errorTemp = NSError(domain:"Auth error", code:500, userInfo:nil)
self.delegate?.requestFailedWithError(errorTemp, apiCallType: self.apiType!,response:nil)
session.invalidateAndCancel()
})
// }
}
}
}
self.delegate?.requestFailedWithError(error, apiCallType: self.apiType!,response:httpResponse)
session.invalidateAndCancel()
})
}
}else{
DispatchQueue.main.async(execute: {
let errorTemp = NSError(domain:"", code:500, userInfo:nil)
self.delegate?.requestFailedWithError(errorTemp, apiCallType: self.apiType!,response:nil)
session.invalidateAndCancel()
})
}
}
session.finishTasksAndInvalidate()
}.resume()
}
}