Skip to content

Commit e09173b

Browse files
committed
minor code refractor
1 parent 9bd6a90 commit e09173b

File tree

6 files changed

+115
-93
lines changed

6 files changed

+115
-93
lines changed

HttpUtility/Extensions/EncodableExtension.swift

Lines changed: 19 additions & 45 deletions
Original file line numberDiff line numberDiff line change
@@ -15,71 +15,45 @@ extension Encodable
1515
var components = URLComponents(string: urlString)
1616
if(components != nil)
1717
{
18-
do {
19-
let encoder = try JSONEncoder().encode(self)
20-
let requestDictionary = (try? JSONSerialization.jsonObject(with: encoder, options: .allowFragments)).flatMap{$0 as? [String: Any?]}
18+
let requestDictionary = convertToDictionary()
2119

22-
if(requestDictionary != nil)
23-
{
24-
var queryItems: [URLQueryItem] = []
20+
if(requestDictionary != nil)
21+
{
22+
var queryItems: [URLQueryItem] = []
2523

26-
requestDictionary?.forEach({ (key, value) in
24+
requestDictionary?.forEach({ (key, value) in
2725

28-
if(value != nil)
26+
if(value != nil)
27+
{
28+
let strValue = value.map { String(describing: $0) }
29+
if(strValue != nil && strValue?.count != 0)
2930
{
30-
let strValue = value.map { String(describing: $0) }
31-
if(strValue != nil && strValue?.count != 0)
32-
{
33-
queryItems.append(URLQueryItem(name: key, value: strValue))
34-
}
31+
queryItems.append(URLQueryItem(name: key, value: strValue))
3532
}
36-
})
37-
38-
components?.queryItems = queryItems
39-
return components?.url!
40-
}
33+
}
34+
})
4135

42-
} catch let error {
43-
debugPrint("convertToQueryStringUrl => Error => \(error)")
36+
components?.queryItems = queryItems
37+
return components?.url!
4438
}
4539
}
4640

4741
debugPrint("convertToQueryStringUrl => Error => Conversion failed, please make sure to pass a valid urlString and try again")
4842

4943
return nil
5044
}
51-
52-
func convertToMultiPartFormData(boundary: String) -> Data
45+
46+
private func convertToDictionary() -> [String: Any?]?
5347
{
54-
let lineBreak = "\r\n"
55-
var requestData = Data()
56-
5748
do {
5849
let encoder = try JSONEncoder().encode(self)
59-
let requestDictionary = (try? JSONSerialization.jsonObject(with: encoder, options: .allowFragments)).flatMap{$0 as? [String: Any?]}
60-
61-
if(requestDictionary != nil)
62-
{
63-
requestDictionary?.forEach({ (key, value) in
64-
if(value != nil)
65-
{
66-
let strValue = value.map { String(describing: $0) }
67-
if(strValue != nil && strValue?.isEmpty == false)
68-
{
69-
requestData.append("\(lineBreak)--\(boundary)\r\n" .data(using: .utf8)!)
70-
requestData.append("content-disposition: form-data; name=\"\(key)\" \(lineBreak + lineBreak)" .data(using: .utf8)!)
71-
requestData.append("\(strValue!)" .data(using: .utf8)!)
72-
}
73-
}
74-
})
75-
76-
requestData.append("--\(boundary)--\(lineBreak)" .data(using: .utf8)!)
77-
}
50+
let result = (try? JSONSerialization.jsonObject(with: encoder, options: .allowFragments)).flatMap{$0 as? [String: Any?]}
51+
return result
7852

7953
} catch let error {
8054
debugPrint(error)
8155
}
8256

83-
return requestData
57+
return nil
8458
}
8559
}

HttpUtility/HttpUtility.swift

Lines changed: 66 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -21,37 +21,40 @@ struct HttpHeaderFields
2121
struct NetworkError : Error
2222
{
2323
let reason: String?
24+
let httpStatusCode: Int?
2425
}
2526

2627
struct HttpUtility
2728
{
2829
private var _token: String? = nil
2930
private var _dateFormatter: DateFormatter? = nil
30-
31+
3132
init(token: String?){
3233
_token = token
3334
}
34-
35+
3536
init(dateFormatter: DateFormatter){
3637
_dateFormatter = dateFormatter
3738
}
38-
39+
3940
init(token: String, dateFormatter: DateFormatter)
4041
{
4142
_token = token
4243
_dateFormatter = dateFormatter
4344
}
44-
45+
4546
init(){}
46-
47-
func getApiData<T:Decodable>(requestUrl: URL, resultType: T.Type, completionHandler:@escaping(Result<T?, NetworkError>)-> Void)
47+
48+
func getData<T:Decodable>(requestUrl: URL, resultType: T.Type, completionHandler:@escaping(Result<T?, NetworkError>)-> Void)
4849
{
49-
50+
5051
var urlRequest = createUrlRequest(requestUrl: requestUrl)
5152
urlRequest.httpMethod = HttpMethodType.GET
52-
53+
5354
URLSession.shared.dataTask(with: requestUrl) { (responseData, httpUrlResponse, error) in
54-
55+
56+
//todo: this code can be added into a common function
57+
let statusCode = (httpUrlResponse as? HTTPURLResponse)?.statusCode
5558
if(error == nil && responseData != nil && responseData?.count != 0)
5659
{
5760
let decoder = self.createJsonDecoder()
@@ -63,68 +66,108 @@ struct HttpUtility
6366
catch let error
6467
{
6568
debugPrint(error)
66-
completionHandler(.failure(NetworkError(reason: error.localizedDescription)))
69+
completionHandler(.failure(NetworkError(reason: error.localizedDescription, httpStatusCode: statusCode)))
6770
}
6871
}
6972
else
7073
{
71-
let error = NetworkError(reason: error.debugDescription)
74+
let error = NetworkError(reason: error.debugDescription,httpStatusCode: statusCode)
7275
completionHandler(.failure(error))
7376
}
74-
77+
7578
}.resume()
7679
}
77-
80+
7881
// MARK: - Post Api
79-
func postApiData<T:Decodable>(requestUrl: URL, requestBody: Data, resultType: T.Type, completionHandler:@escaping(Result<T?, NetworkError>)-> Void)
82+
func postData<T:Decodable>(requestUrl: URL, requestBody: Data, resultType: T.Type, completionHandler:@escaping(Result<T?, NetworkError>)-> Void)
8083
{
8184
var urlRequest = createUrlRequest(requestUrl: requestUrl)
8285
urlRequest.httpMethod = HttpMethodType.POST
8386
urlRequest.httpBody = requestBody
8487
urlRequest.addValue("application/json", forHTTPHeaderField: HttpHeaderFields.contentType)
85-
88+
8689
URLSession.shared.dataTask(with: urlRequest) { (data, httpUrlResponse, error) in
87-
90+
let statusCode = (httpUrlResponse as? HTTPURLResponse)?.statusCode
8891
if(error == nil && data != nil && data?.count != 0)
8992
{
9093
do {
91-
94+
9295
let decoder = self.createJsonDecoder()
9396
let response = try decoder.decode(T.self, from: data!)
9497
completionHandler(.success(response))
9598
}
9699
catch let decodingError
97100
{
98101
debugPrint(decodingError)
99-
let networkError = NetworkError(reason: decodingError.localizedDescription)
102+
let networkError = NetworkError(reason: decodingError.localizedDescription, httpStatusCode: statusCode)
100103
completionHandler(.failure(networkError))
101104
}
102105
}
103106
else
104107
{
105-
let error = NetworkError(reason: error.debugDescription)
108+
let error = NetworkError(reason: error.debugDescription, httpStatusCode: statusCode)
106109
completionHandler(.failure(error))
107110
}
108-
111+
109112
}.resume()
110113
}
111-
114+
115+
func postMultipartFormData<T:Decodable>(requestUrl: URL, multiPartRequestBody: Data, resultType: T.Type, completionHandler:@escaping(Result<T?, NetworkError>)-> Void)
116+
{
117+
var urlRequest = URLRequest(url: requestUrl)
118+
119+
urlRequest.httpMethod = HttpMethodType.POST
120+
121+
let boundary = "---------------------------------\(UUID().uuidString)"
122+
urlRequest.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "content-type")
123+
urlRequest.addValue("\(multiPartRequestBody.count)", forHTTPHeaderField: "content-length")
124+
urlRequest.httpBody = multiPartRequestBody
125+
126+
debugPrint("multipart form data => \(String(describing: String(data: multiPartRequestBody, encoding: .utf8)))")
127+
128+
URLSession.shared.dataTask(with: urlRequest) { (data, httpUrlResponse, error) in
129+
let statusCode = (httpUrlResponse as? HTTPURLResponse)?.statusCode
130+
if(error == nil && data != nil && data?.count != 0)
131+
{
132+
do {
133+
let decoder = self.createJsonDecoder()
134+
let response = try decoder.decode(T.self, from: data!)
135+
completionHandler(.success(response))
136+
}
137+
catch let decodingError
138+
{
139+
debugPrint(decodingError)
140+
let networkError = NetworkError(reason: decodingError.localizedDescription, httpStatusCode: statusCode)
141+
completionHandler(.failure(networkError))
142+
}
143+
}
144+
else
145+
{
146+
debugPrint(error.debugDescription)
147+
let networkError = NetworkError(reason: error.debugDescription, httpStatusCode: statusCode)
148+
completionHandler(.failure(networkError))
149+
}
150+
151+
}.resume()
152+
153+
}
154+
112155
// MARK: - Private functions
113156
private func createJsonDecoder() -> JSONDecoder
114157
{
115158
let decoder = JSONDecoder()
116159
decoder.dateDecodingStrategy = _dateFormatter != nil ? .formatted(_dateFormatter!) : .iso8601
117160
return decoder
118161
}
119-
162+
120163
private func createUrlRequest(requestUrl: URL) -> URLRequest
121164
{
122165
var urlRequest = URLRequest(url: requestUrl)
123166
if(_token != nil)
124167
{
125168
urlRequest.addValue(_token!, forHTTPHeaderField: "authorization")
126169
}
127-
170+
128171
return urlRequest
129172
}
130173
}

HttpUtilityTests/ExtensionsTests/EncodableExtensionUnitTest.swift

Lines changed: 13 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ class EncodableExtensionUnitTest: XCTestCase {
1313

1414
private let exampleUrl = "https://www.example.com"
1515

16-
func test_convertToQueryStringUrl_With_SimpleStructure_Returuns_URLQueryItemCollection()
16+
func test_convertToQueryStringUrl_With_SimpleStructure_Returuns_QueryStringUrl()
1717
{
1818
// ARRANGE
1919
struct Simple : Encodable {let name, description: String}
@@ -26,7 +26,7 @@ class EncodableExtensionUnitTest: XCTestCase {
2626
XCTAssertNotNil(result)
2727
}
2828

29-
func test_convertToQueryStringUrl_With_IntegerValue_Returuns_URLQueryItemCollection()
29+
func test_convertToQueryStringUrl_With_IntegerValue_Returuns_QueryStringUrl()
3030
{
3131
// ARRANGE
3232
struct simple : Encodable {
@@ -41,9 +41,13 @@ class EncodableExtensionUnitTest: XCTestCase {
4141

4242
// ASSERT
4343
XCTAssertNotNil(result)
44+
XCTAssertTrue(result!.absoluteString.contains("name=\(objSimple.name)"))
45+
XCTAssertTrue(result!.absoluteString.contains("id=\(objSimple.id)"))
46+
4447
}
4548

46-
func test_convertToQueryStringUrl_With_array_Returuns_URLQueryItemCollection()
49+
//todo: need to test arrays
50+
func test_convertToQueryStringUrl_With_array_Returuns_QueryStringUrl()
4751
{
4852
// ARRANGE
4953
struct simple : Encodable {
@@ -58,9 +62,10 @@ class EncodableExtensionUnitTest: XCTestCase {
5862

5963
// ASSERT
6064
XCTAssertNotNil(result)
65+
XCTAssertTrue(result!.absoluteString.contains("name=\(objSimple.name)"))
6166
}
6267

63-
func test_convertToQueryStringUrl_With_Multiple_DataType_Returuns_URLQueryItemCollection()
68+
func test_convertToQueryStringUrl_With_Multiple_DataType_Returuns_QueryStringUrl()
6469
{
6570
// ARRANGE
6671
struct simple : Encodable {
@@ -77,21 +82,9 @@ class EncodableExtensionUnitTest: XCTestCase {
7782

7883
// ASSERT
7984
XCTAssertNotNil(result)
80-
}
81-
82-
// MARK: - convert to multipart form data
83-
//todo: need to write more solid test
84-
func test_convertToMultiPartFormData_Returns_Valid_MultiFormData()
85-
{
86-
// ARRANGE
87-
let request = MultiPartFormRequest(name: UUID().uuidString, lastName: UUID().uuidString, dateOfJoining: UUID().uuidString, dateOfBirth: UUID().uuidString, gender: UUID().uuidString, departmentName: UUID().uuidString, managerName: UUID().uuidString)
88-
89-
let boundary = "---------------------------------\(UUID().uuidString)"
90-
91-
// ACT
92-
let formData = request.convertToMultiPartFormData(boundary: boundary)
93-
94-
// ASSERT
95-
XCTAssertNotNil(formData)
85+
XCTAssertTrue(result!.absoluteString.contains("name=\(objSimple.name)"))
86+
XCTAssertTrue(result!.absoluteString.contains("id=\(objSimple.id)"))
87+
XCTAssertTrue(result!.absoluteString.contains("salary=25000"))
88+
XCTAssertTrue(result!.absoluteString.contains("isOnContract=0"))
9689
}
9790
}

HttpUtilityTests/IntegrationTests/HttpUtilityIntegrationTests.swift

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -21,7 +21,7 @@ class HttpUtilityIntegrationTests: XCTestCase {
2121
let expectation = XCTestExpectation(description: "Data received from server")
2222

2323
// ACT
24-
_utility.getApiData(requestUrl: requestUrl!, resultType: Employees.self) { (response) in
24+
_utility.getData(requestUrl: requestUrl!, resultType: Employees.self) { (response) in
2525

2626
switch response
2727
{
@@ -51,8 +51,8 @@ class HttpUtilityIntegrationTests: XCTestCase {
5151
let registerUserBody = try! JSONEncoder().encode(registerUserRequest)
5252
let expectation = XCTestExpectation(description: "Data received from server")
5353
// ACT
54-
_utility.postApiData(requestUrl: requestUrl!, requestBody: registerUserBody, resultType: RegisterResponse.self) { (response) in
55-
switch response
54+
_utility.postData(requestUrl: requestUrl!, requestBody: registerUserBody, resultType: RegisterResponse.self) { (response) in
55+
switch response
5656
{
5757
case .success(let registerResponse):
5858

@@ -78,7 +78,7 @@ class HttpUtilityIntegrationTests: XCTestCase {
7878
let requestUrl = request.convertToQueryStringUrl(urlString:"https://api-dev-scus-demo.azurewebsites.net/api/Product/GetSmartPhone")
7979

8080
// ACT
81-
_utility.getApiData(requestUrl: requestUrl!, resultType: PhoneResponse.self) { (response) in
81+
_utility.getData(requestUrl: requestUrl!, resultType: PhoneResponse.self) { (response) in
8282

8383
switch response
8484
{

HttpUtilityTests/TestModel/Requests.swift

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ struct PhoneRequest: Encodable
3030

3131
struct MultiPartFormRequest: Encodable
3232
{
33-
let name, lastName, dateOfJoining, dateOfBirth, gender, departmentName, managerName: String
33+
let name, lastName, gender, departmentName, managerName: String
34+
let dateOfJoining, dateOfBirth: String
3435

3536
enum CodingKeys: String, CodingKey {
3637

HttpUtilityTests/TestModel/Response.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -44,3 +44,14 @@ struct PhoneResponse: Decodable {
4444
struct Phone: Decodable {
4545
let name, operatingSystem, manufacturer, color: String?
4646
}
47+
48+
// MARK: - MultiPartResponse
49+
struct MultiPartResponse: Decodable {
50+
let errorMessage: String?
51+
let data: MultipartMessage?
52+
}
53+
54+
// MARK: - MultipartMessage
55+
struct MultipartMessage: Decodable {
56+
let message: String?
57+
}

0 commit comments

Comments
 (0)