Skip to content

Commit 444b967

Browse files
authored
Merge pull request #8 from codecat15/multipart-form-data-format
Merging changes for multipart form post requests
2 parents dc43940 + fc4a711 commit 444b967

File tree

11 files changed

+383
-94
lines changed

11 files changed

+383
-94
lines changed

HttpUtility.xcodeproj/project.pbxproj

Lines changed: 8 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@
77
objects = {
88

99
/* Begin PBXBuildFile section */
10-
86277C2024CB399B0078EB37 /* LazyImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86277C1F24CB399B0078EB37 /* LazyImageView.swift */; };
10+
86010D0725CE240300A4E362 /* batman.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 86010D0625CE240300A4E362 /* batman.jpg */; };
11+
86521B5625C6FD7200E05422 /* HURequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86521B5525C6FD7100E05422 /* HURequest.swift */; };
1112
8656BC582483E3C60023549D /* EncodableExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8656BC572483E3C60023549D /* EncodableExtension.swift */; };
1213
8656BC5B2483E43D0023549D /* EncodableExtensionUnitTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8656BC5A2483E43D0023549D /* EncodableExtensionUnitTest.swift */; };
1314
8656BC5E2484313F0023549D /* HttpUtilityIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8656BC5D2484313F0023549D /* HttpUtilityIntegrationTests.swift */; };
@@ -31,7 +32,8 @@
3132
/* End PBXContainerItemProxy section */
3233

3334
/* Begin PBXFileReference section */
34-
86277C1F24CB399B0078EB37 /* LazyImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyImageView.swift; sourceTree = "<group>"; };
35+
86010D0625CE240300A4E362 /* batman.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = batman.jpg; sourceTree = "<group>"; };
36+
86521B5525C6FD7100E05422 /* HURequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HURequest.swift; sourceTree = "<group>"; };
3537
8656BC572483E3C60023549D /* EncodableExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncodableExtension.swift; sourceTree = "<group>"; };
3638
8656BC5A2483E43D0023549D /* EncodableExtensionUnitTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncodableExtensionUnitTest.swift; sourceTree = "<group>"; };
3739
8656BC5D2484313F0023549D /* HttpUtilityIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HttpUtilityIntegrationTests.swift; sourceTree = "<group>"; };
@@ -70,7 +72,6 @@
7072
isa = PBXGroup;
7173
children = (
7274
8656BC572483E3C60023549D /* EncodableExtension.swift */,
73-
86277C1F24CB399B0078EB37 /* LazyImageView.swift */,
7475
);
7576
path = Extensions;
7677
sourceTree = "<group>";
@@ -123,6 +124,7 @@
123124
children = (
124125
86CAEFE525BBBE98006A7791 /* HUNetworkError.swift */,
125126
86CAEFE925BBBEDE006A7791 /* HUHttpMethods.swift */,
127+
86521B5525C6FD7100E05422 /* HURequest.swift */,
126128
8656BC562483E3B20023549D /* Extensions */,
127129
86719E9924720BD1002A2AB0 /* HttpUtility.h */,
128130
86719E9A24720BD1002A2AB0 /* Info.plist */,
@@ -134,6 +136,7 @@
134136
86719EA324720BD1002A2AB0 /* HttpUtilityTests */ = {
135137
isa = PBXGroup;
136138
children = (
139+
86010D0625CE240300A4E362 /* batman.jpg */,
137140
8656BC5F248495620023549D /* TestModel */,
138141
8656BC5C248431200023549D /* IntegrationTests */,
139142
8656BC592483E4200023549D /* ExtensionsTests */,
@@ -242,6 +245,7 @@
242245
isa = PBXResourcesBuildPhase;
243246
buildActionMask = 2147483647;
244247
files = (
248+
86010D0725CE240300A4E362 /* batman.jpg in Resources */,
245249
);
246250
runOnlyForDeploymentPostprocessing = 0;
247251
};
@@ -253,9 +257,9 @@
253257
buildActionMask = 2147483647;
254258
files = (
255259
86CAEFE625BBBE98006A7791 /* HUNetworkError.swift in Sources */,
256-
86277C2024CB399B0078EB37 /* LazyImageView.swift in Sources */,
257260
86719EB124720E40002A2AB0 /* HttpUtility.swift in Sources */,
258261
8656BC582483E3C60023549D /* EncodableExtension.swift in Sources */,
262+
86521B5625C6FD7200E05422 /* HURequest.swift in Sources */,
259263
86CAEFEA25BBBEDE006A7791 /* HUHttpMethods.swift in Sources */,
260264
);
261265
runOnlyForDeploymentPostprocessing = 0;

HttpUtility/Extensions/EncodableExtension.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -39,7 +39,7 @@ extension Encodable
3939
return nil
4040
}
4141

42-
private func convertToDictionary() -> [String: Any?]?
42+
func convertToDictionary() -> [String: Any?]?
4343
{
4444
do {
4545
let encoder = try JSONEncoder().encode(self)

HttpUtility/Extensions/LazyImageView.swift

Lines changed: 0 additions & 38 deletions
This file was deleted.

HttpUtility/HUNetworkError.swift

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,16 @@ public struct HUNetworkError : Error
1212
{
1313
let reason: String?
1414
let httpStatusCode: Int?
15+
let requestUrl: URL?
16+
let requestBody: String?
17+
let serverResponse: String?
18+
19+
init(withServerResponse response: Data? = nil, forRequestUrl url: URL, withHttpBody body: Data? = nil, errorMessage message: String, forStatusCode statusCode: Int)
20+
{
21+
self.serverResponse = response != nil ? String(data: response!, encoding: .utf8) : nil
22+
self.requestUrl = url
23+
self.requestBody = body != nil ? String(data: body!, encoding: .utf8) : nil
24+
self.httpStatusCode = statusCode
25+
self.reason = message
26+
}
1527
}

HttpUtility/HURequest.swift

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
//
2+
// HURequest.swift
3+
// HttpUtility
4+
//
5+
// Created by CodeCat15 on 1/31/21.
6+
// Copyright © 2021 CodeCat15. All rights reserved.
7+
//
8+
9+
import Foundation
10+
11+
protocol Request {
12+
var url: URL { get set }
13+
var method: HUHttpMethods {get set}
14+
}
15+
16+
public struct HURequest : Request {
17+
var url: URL
18+
var method: HUHttpMethods
19+
var requestBody: Data? = nil
20+
21+
init(withUrl url: URL, forHttpMethod method: HUHttpMethods, requestBody: Data? = nil) {
22+
self.url = url
23+
self.method = method
24+
self.requestBody = requestBody != nil ? requestBody : nil
25+
}
26+
}
27+
28+
// the HUMedia will be part of next release
29+
public struct HUMultiPartRequest : Request {
30+
var url: URL
31+
var method: HUHttpMethods
32+
var request : Encodable
33+
//var media : [HUMedia]? = nil
34+
}
35+
36+
//public struct HUMedia
37+
//{
38+
// let fileName : String // the name of the file that you want to save on the server
39+
// let data: Data
40+
// let mimeType: String // mime type of the file image/jpeg or image/png etc
41+
// let parameterName : String // api parameter name
42+
//
43+
// init(withMediaData data: Data, name: String, mimeType: HUMimeType, parameterName: String) {
44+
//
45+
// self.data = data
46+
// self.fileName = name
47+
// self.mimeType = mimeType.rawValue
48+
// self.parameterName = parameterName
49+
// }
50+
//}
51+
//
52+
//public enum HUMimeType : String
53+
//{
54+
// // images mime type
55+
// case gif = "image/gif"
56+
// case jpeg = "image/jpeg"
57+
// case pjpeg = "image/pjpeg"
58+
// case png = "image/png"
59+
// case svgxml = "image/svg+xml"
60+
// case tiff = "image/tiff"
61+
// case bmp = "image/bmp"
62+
//
63+
// // document mime type
64+
// case csv = "text/csv"
65+
// case wordDocument = "application/msword"
66+
// case pdf = "application/pdf"
67+
// case richTextFormat = "application/rtf"
68+
// case plainText = "text/plain"
69+
//
70+
//}

HttpUtility/HttpUtility.swift

Lines changed: 67 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -16,28 +16,33 @@ public struct HttpUtility
1616

1717
private init(){}
1818

19-
public func request<T:Decodable>(requestUrl: URL, method: HUHttpMethods, requestBody: Data? = nil, resultType: T.Type, completionHandler:@escaping(Result<T?, HUNetworkError>)-> Void)
19+
public func request<T:Decodable>(huRequest: HURequest, resultType: T.Type, completionHandler:@escaping(Result<T?, HUNetworkError>)-> Void)
2020
{
21-
switch method
21+
switch huRequest.method
2222
{
2323
case .get:
24-
getData(requestUrl: requestUrl, resultType: resultType) { completionHandler($0)}
24+
getData(requestUrl: huRequest.url, resultType: resultType) { completionHandler($0)}
2525
break
2626

2727
case .post:
28-
postData(requestUrl: requestUrl, requestBody: requestBody!, resultType: T.self) { completionHandler($0)}
28+
postData(request: huRequest, resultType: resultType) { completionHandler($0)}
2929
break
3030

3131
case .put:
32-
putData(requestUrl: requestUrl, resultType: resultType) { completionHandler($0)}
32+
putData(requestUrl: huRequest.url, resultType: resultType) { completionHandler($0)}
3333
break
3434

3535
case .delete:
36-
deleteData(requestUrl: requestUrl, resultType: resultType) { completionHandler($0)}
36+
deleteData(requestUrl: huRequest.url, resultType: resultType) { completionHandler($0)}
3737
break
3838
}
3939
}
4040

41+
// MARK: - Multipart
42+
public func requestWithMultiPartFormData<T:Decodable>(multiPartRequest: HUMultiPartRequest, responseType: T.Type, completionHandler:@escaping(Result<T?, HUNetworkError>)-> Void) {
43+
postMultiPartFormData(request: multiPartRequest) { completionHandler($0) }
44+
}
45+
4146
// MARK: - Private functions
4247
private func createJsonDecoder() -> JSONDecoder
4348
{
@@ -61,9 +66,9 @@ public struct HttpUtility
6166
private func decodeJsonResponse<T: Decodable>(data: Data, responseType: T.Type) -> T?
6267
{
6368
let decoder = createJsonDecoder()
64-
do{
69+
do {
6570
return try decoder.decode(responseType, from: data)
66-
}catch let error{
71+
}catch let error {
6772
debugPrint("deocding error =>\(error.localizedDescription)")
6873
}
6974
return nil
@@ -81,18 +86,64 @@ public struct HttpUtility
8186
}
8287

8388
// MARK: - POST Api
84-
private func postData<T:Decodable>(requestUrl: URL, requestBody: Data, resultType: T.Type, completionHandler:@escaping(Result<T?, HUNetworkError>)-> Void)
89+
private func postData<T:Decodable>(request: HURequest, resultType: T.Type, completionHandler:@escaping(Result<T?, HUNetworkError>)-> Void)
8590
{
86-
var urlRequest = self.createUrlRequest(requestUrl: requestUrl)
91+
var urlRequest = self.createUrlRequest(requestUrl: request.url)
8792
urlRequest.httpMethod = HUHttpMethods.post.rawValue
88-
urlRequest.httpBody = requestBody
93+
urlRequest.httpBody = request.requestBody
8994
urlRequest.addValue("application/json", forHTTPHeaderField: "content-type")
9095

9196
performOperation(requestUrl: urlRequest, responseType: T.self) { (result) in
9297
completionHandler(result)
9398
}
9499
}
95100

101+
private func postMultiPartFormData<T:Decodable>(request: HUMultiPartRequest, completionHandler:@escaping(Result<T?, HUNetworkError>)-> Void)
102+
{
103+
let boundary = "-----------------------------\(UUID().uuidString)"
104+
let lineBreak = "\r\n"
105+
var urlRequest = self.createUrlRequest(requestUrl: request.url)
106+
urlRequest.httpMethod = HUHttpMethods.post.rawValue
107+
urlRequest.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
108+
109+
var postBody = Data()
110+
111+
let requestDictionary = request.request.convertToDictionary()
112+
if(requestDictionary != nil)
113+
{
114+
requestDictionary?.forEach({ (key, value) in
115+
if(value != nil) {
116+
let strValue = value.map { String(describing: $0) }
117+
if(strValue != nil && strValue?.count != 0) {
118+
postBody.append("--\(boundary + lineBreak)" .data(using: .utf8)!)
119+
postBody.append("Content-Disposition: form-data; name=\"\(key)\" \(lineBreak + lineBreak)" .data(using: .utf8)!)
120+
postBody.append("\(strValue! + lineBreak)".data(using: .utf8)!)
121+
}
122+
}
123+
})
124+
125+
// TODO: Next release
126+
// if(huRequest.media != nil) {
127+
// huRequest.media?.forEach({ (media) in
128+
// postBody.append("--\(boundary + lineBreak)" .data(using: .utf8)!)
129+
// postBody.append("Content-Disposition: form-data; name=\"\(media.parameterName)\"; filename=\"\(media.fileName)\" \(lineBreak + lineBreak)" .data(using: .utf8)!)
130+
// postBody.append("Content-Type: \(media.mimeType + lineBreak + lineBreak)" .data(using: .utf8)!)
131+
// postBody.append(media.data)
132+
// postBody.append(lineBreak .data(using: .utf8)!)
133+
// })
134+
// }
135+
136+
postBody.append("--\(boundary)--\(lineBreak)" .data(using: .utf8)!)
137+
138+
urlRequest.addValue("\(postBody.count)", forHTTPHeaderField: "Content-Length")
139+
urlRequest.httpBody = postBody
140+
141+
performOperation(requestUrl: urlRequest, responseType: T.self) { (result) in
142+
completionHandler(result)
143+
}
144+
}
145+
}
146+
96147
// MARK: - PUT Api
97148
private func putData<T:Decodable>(requestUrl: URL, resultType: T.Type, completionHandler:@escaping(Result<T?, HUNetworkError>)-> Void)
98149
{
@@ -121,21 +172,16 @@ public struct HttpUtility
121172
URLSession.shared.dataTask(with: requestUrl) { (data, httpUrlResponse, error) in
122173

123174
let statusCode = (httpUrlResponse as? HTTPURLResponse)?.statusCode
124-
if(error == nil && data != nil && data?.count != 0)
125-
{
175+
if(error == nil && data != nil && data?.count != 0) {
126176
let response = self.decodeJsonResponse(data: data!, responseType: responseType)
127-
128-
if(response != nil){
177+
if(response != nil) {
129178
completionHandler(.success(response))
130179
}else {
131-
132-
let responseJsonString = String(data: data!, encoding: .utf8)
133-
completionHandler(.failure(HUNetworkError(reason: "response decoding error = \(String(describing: responseJsonString))", httpStatusCode: statusCode)))
180+
completionHandler(.failure(HUNetworkError(withServerResponse: data, forRequestUrl: requestUrl.url!, withHttpBody: requestUrl.httpBody, errorMessage: error.debugDescription, forStatusCode: statusCode!)))
134181
}
135182
}
136-
else
137-
{
138-
let networkError = HUNetworkError(reason: error.debugDescription,httpStatusCode: statusCode)
183+
else {
184+
let networkError = HUNetworkError(withServerResponse: data, forRequestUrl: requestUrl.url!, withHttpBody: requestUrl.httpBody, errorMessage: error.debugDescription, forStatusCode: statusCode!)
139185
completionHandler(.failure(networkError))
140186
}
141187

0 commit comments

Comments
 (0)