Skip to content

Commit 2df2b38

Browse files
committed
adding multipart form data code inital
this code is work in progress and will be updated soon
1 parent 4416ef1 commit 2df2b38

File tree

6 files changed

+197
-8
lines changed

6 files changed

+197
-8
lines changed

HttpUtility.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
/* Begin PBXBuildFile section */
1010
86277C2024CB399B0078EB37 /* LazyImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86277C1F24CB399B0078EB37 /* LazyImageView.swift */; };
11+
86521B5625C6FD7200E05422 /* HURequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86521B5525C6FD7100E05422 /* HURequest.swift */; };
12+
86521B5C25C7023D00E05422 /* HUMultiPartFormData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 86521B5B25C7023D00E05422 /* HUMultiPartFormData.swift */; };
1113
8656BC582483E3C60023549D /* EncodableExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8656BC572483E3C60023549D /* EncodableExtension.swift */; };
1214
8656BC5B2483E43D0023549D /* EncodableExtensionUnitTest.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8656BC5A2483E43D0023549D /* EncodableExtensionUnitTest.swift */; };
1315
8656BC5E2484313F0023549D /* HttpUtilityIntegrationTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8656BC5D2484313F0023549D /* HttpUtilityIntegrationTests.swift */; };
@@ -32,6 +34,8 @@
3234

3335
/* Begin PBXFileReference section */
3436
86277C1F24CB399B0078EB37 /* LazyImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LazyImageView.swift; sourceTree = "<group>"; };
37+
86521B5525C6FD7100E05422 /* HURequest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HURequest.swift; sourceTree = "<group>"; };
38+
86521B5B25C7023D00E05422 /* HUMultiPartFormData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HUMultiPartFormData.swift; sourceTree = "<group>"; };
3539
8656BC572483E3C60023549D /* EncodableExtension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncodableExtension.swift; sourceTree = "<group>"; };
3640
8656BC5A2483E43D0023549D /* EncodableExtensionUnitTest.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EncodableExtensionUnitTest.swift; sourceTree = "<group>"; };
3741
8656BC5D2484313F0023549D /* HttpUtilityIntegrationTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HttpUtilityIntegrationTests.swift; sourceTree = "<group>"; };
@@ -123,10 +127,12 @@
123127
children = (
124128
86CAEFE525BBBE98006A7791 /* HUNetworkError.swift */,
125129
86CAEFE925BBBEDE006A7791 /* HUHttpMethods.swift */,
130+
86521B5525C6FD7100E05422 /* HURequest.swift */,
126131
8656BC562483E3B20023549D /* Extensions */,
127132
86719E9924720BD1002A2AB0 /* HttpUtility.h */,
128133
86719E9A24720BD1002A2AB0 /* Info.plist */,
129134
86719EB024720E40002A2AB0 /* HttpUtility.swift */,
135+
86521B5B25C7023D00E05422 /* HUMultiPartFormData.swift */,
130136
);
131137
path = HttpUtility;
132138
sourceTree = "<group>";
@@ -256,6 +262,8 @@
256262
86277C2024CB399B0078EB37 /* LazyImageView.swift in Sources */,
257263
86719EB124720E40002A2AB0 /* HttpUtility.swift in Sources */,
258264
8656BC582483E3C60023549D /* EncodableExtension.swift in Sources */,
265+
86521B5C25C7023D00E05422 /* HUMultiPartFormData.swift in Sources */,
266+
86521B5625C6FD7200E05422 /* HURequest.swift in Sources */,
259267
86CAEFEA25BBBEDE006A7791 /* HUHttpMethods.swift in Sources */,
260268
);
261269
runOnlyForDeploymentPostprocessing = 0;
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
//
2+
// HUMultiPartFormData.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+
12+
public class HUMultiPartFormData
13+
{
14+
private var multiPartData : Data = Data()
15+
private var boundary = "-----------------------------\(UUID().uuidString)"
16+
private let lineBreak = "\r\n"
17+
18+
public func appendBodyPart(parameterName: String, WithParameterData data: String, fileName: String? = nil, mimeType: String? = nil) {
19+
20+
multiPartData.append("Content-Disposition: form-data; name=\"\(parameterName)\" \(lineBreak + lineBreak)" .data(using: .utf8)!)
21+
multiPartData.append("\(data + lineBreak)".data(using: .utf8)!)
22+
}
23+
24+
public func appendInitalBoundary() {
25+
multiPartData.append("--\(boundary + lineBreak)" .data(using: .utf8)!)
26+
}
27+
28+
public func appendLineBreakForNextParameter() {
29+
multiPartData.append("--\(boundary + lineBreak)" .data(using: .utf8)!)
30+
}
31+
32+
public func appendClosingBoundary() {
33+
multiPartData.append("--\(boundary)--\(lineBreak)" .data(using: .utf8)!)
34+
}
35+
36+
public func getMultiPartBody() -> Data {
37+
multiPartData
38+
}
39+
40+
public func getBoundary() -> String {
41+
boundary
42+
}
43+
}

HttpUtility/HURequest.swift

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
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+
public struct HURequest {
12+
13+
let url: URL
14+
let method: HUHttpMethods
15+
}

HttpUtility/HttpUtility.swift

Lines changed: 28 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,13 @@ public struct HttpUtility
3838
}
3939
}
4040

41+
// MARK: - Multipart
42+
43+
public func requestWithMultiPart<T:Decodable>(request: HURequest, resultType: T.Type, postBody: HUMultiPartFormData, completionHandler:@escaping(Result<T?, HUNetworkError>)-> Void) {
44+
45+
postMultiPartFormData(requestUrl: request.url, multiPartFormData: postBody, resultType: resultType.self) { completionHandler($0) }
46+
}
47+
4148
// MARK: - Private functions
4249
private func createJsonDecoder() -> JSONDecoder
4350
{
@@ -93,6 +100,23 @@ public struct HttpUtility
93100
}
94101
}
95102

103+
private func postMultiPartFormData<T:Decodable>(requestUrl: URL, multiPartFormData: HUMultiPartFormData, resultType: T.Type, completionHandler:@escaping(Result<T?, HUNetworkError>)-> Void)
104+
{
105+
var urlRequest = self.createUrlRequest(requestUrl: requestUrl)
106+
urlRequest.httpMethod = HUHttpMethods.post.rawValue
107+
108+
let body = multiPartFormData.getMultiPartBody()
109+
let boundary = multiPartFormData.getBoundary()
110+
111+
urlRequest.addValue("multipart/form-data; boundary=\(boundary)", forHTTPHeaderField: "Content-Type")
112+
urlRequest.addValue("\(body.count)", forHTTPHeaderField: "Content-Length")
113+
urlRequest.httpBody = body
114+
115+
performOperation(requestUrl: urlRequest, responseType: T.self) { (result) in
116+
completionHandler(result)
117+
}
118+
}
119+
96120
// MARK: - PUT Api
97121
private func putData<T:Decodable>(requestUrl: URL, resultType: T.Type, completionHandler:@escaping(Result<T?, HUNetworkError>)-> Void)
98122
{
@@ -121,20 +145,16 @@ public struct HttpUtility
121145
URLSession.shared.dataTask(with: requestUrl) { (data, httpUrlResponse, error) in
122146

123147
let statusCode = (httpUrlResponse as? HTTPURLResponse)?.statusCode
124-
if(error == nil && data != nil && data?.count != 0)
125-
{
148+
if(error == nil && data != nil && data?.count != 0) {
126149
let response = self.decodeJsonResponse(data: data!, responseType: responseType)
127-
128-
if(response != nil){
150+
if(response != nil) {
129151
completionHandler(.success(response))
130152
}else {
131-
132153
let responseJsonString = String(data: data!, encoding: .utf8)
133-
completionHandler(.failure(HUNetworkError(reason: "response decoding error = \(String(describing: responseJsonString))", httpStatusCode: statusCode)))
154+
completionHandler(.failure(HUNetworkError(reason: "error = \(String(describing: responseJsonString))", httpStatusCode: statusCode)))
134155
}
135156
}
136-
else
137-
{
157+
else {
138158
let networkError = HUNetworkError(reason: error.debugDescription,httpStatusCode: statusCode)
139159
completionHandler(.failure(networkError))
140160
}

HttpUtilityTests/IntegrationTests/HttpUtilityIntegrationTests.swift

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -150,4 +150,96 @@ class HttpUtilityIntegrationTests: XCTestCase {
150150

151151
wait(for: [expectation], timeout: 10.0)
152152
}
153+
154+
func test_MultiPartFormDataUpload_WithSmallRequestBody_Returns_Success()
155+
{
156+
// ARRANGE
157+
let expectation = XCTestExpectation(description: "Multipart form data test")
158+
let requestUrl = URL(string: "https://api-dev-scus-demo.azurewebsites.net/TestMultiPart")
159+
let name = "Code"
160+
let lastName = "Cat"
161+
162+
// todo: this code can be improved
163+
let multiPartFormData = HUMultiPartFormData()
164+
multiPartFormData.appendInitalBoundary() // what if the developer forgets to add this line?
165+
multiPartFormData.appendBodyPart(parameterName: "Name", WithParameterData: name)
166+
multiPartFormData.appendLineBreakForNextParameter() // I don't like this
167+
multiPartFormData.appendBodyPart(parameterName: "LastName", WithParameterData: lastName)
168+
multiPartFormData.appendClosingBoundary() // why does the dev has to specify all this???
169+
170+
let request = HURequest(url: requestUrl!, method: .post)
171+
172+
// ACT
173+
_utility.requestWithMultiPart(request: request, resultType: TestMultiPartResponse.self, postBody: multiPartFormData) { (response) in
174+
// ASSERT
175+
switch response
176+
{
177+
case .success(let serviceResponse):
178+
179+
// ASSERT
180+
XCTAssertNotNil(serviceResponse)
181+
XCTAssertNotNil(serviceResponse?.data)
182+
XCTAssertEqual(name, serviceResponse?.data.name)
183+
XCTAssertEqual(lastName, serviceResponse?.data.lastName)
184+
185+
case .failure(let error):
186+
XCTAssertNil(error.reason)
187+
}
188+
expectation.fulfill()
189+
}
190+
191+
wait(for: [expectation], timeout: 10.0)
192+
}
193+
194+
func test_multiPartFormDataUpload_WithValidRequest_Returns_Success()
195+
{
196+
// ARRANGE
197+
let expectation = XCTestExpectation(description: "Multipart form data test")
198+
let requestUrl = URL(string: "https://api-dev-scus-demo.azurewebsites.net/api/Employee/MultiPartCodeChallenge")
199+
let multiPartFormData = HUMultiPartFormData()
200+
201+
multiPartFormData.appendInitalBoundary()
202+
203+
multiPartFormData.appendBodyPart(parameterName: "Name", WithParameterData: "Codecat")
204+
multiPartFormData.appendLineBreakForNextParameter()
205+
206+
multiPartFormData.appendBodyPart(parameterName: "LastName", WithParameterData: "HellWorld")
207+
multiPartFormData.appendLineBreakForNextParameter()
208+
209+
multiPartFormData.appendBodyPart(parameterName: "DateofJoining", WithParameterData: "12-12-2012")
210+
multiPartFormData.appendLineBreakForNextParameter()
211+
212+
multiPartFormData.appendBodyPart(parameterName: "DateofBirth", WithParameterData: "12-12-1992")
213+
multiPartFormData.appendLineBreakForNextParameter()
214+
215+
multiPartFormData.appendBodyPart(parameterName: "Gender", WithParameterData: "HumanBeing")
216+
multiPartFormData.appendLineBreakForNextParameter()
217+
218+
multiPartFormData.appendBodyPart(parameterName: "DepartmentName", WithParameterData: "Technology")
219+
multiPartFormData.appendLineBreakForNextParameter()
220+
221+
multiPartFormData.appendBodyPart(parameterName: "ManagerName", WithParameterData: "MyManagerName")
222+
multiPartFormData.appendClosingBoundary()
223+
224+
let request = HURequest(url: requestUrl!, method: .post)
225+
226+
// ACT
227+
_utility.requestWithMultiPart(request: request, resultType: MultiPartResponse.self, postBody: multiPartFormData) { (response) in
228+
// ASSERT
229+
switch response
230+
{
231+
case .success(let serviceResponse):
232+
233+
// ASSERT
234+
XCTAssertNotNil(serviceResponse)
235+
XCTAssertNotNil(serviceResponse?.data)
236+
237+
case .failure(let error):
238+
XCTAssertNil(error.reason)
239+
}
240+
expectation.fulfill()
241+
}
242+
243+
wait(for: [expectation], timeout: 10.0)
244+
}
153245
}

HttpUtilityTests/TestModel/Response.swift

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,17 @@ struct Phone: Decodable {
4545
let name, operatingSystem, manufacturer, color: String?
4646
}
4747

48+
// MARK: - DataClass
49+
struct TestMultiPartResponse: Decodable {
50+
let errorMessage: String
51+
let data: DataClass
52+
}
53+
54+
// MARK: - DataClass
55+
struct DataClass: Decodable {
56+
let name, lastName: String
57+
}
58+
4859
// MARK: - MultiPartResponse
4960
struct MultiPartResponse: Decodable {
5061
let errorMessage: String?

0 commit comments

Comments
 (0)