pod-builder 1.9.2 → 2.0.0.beta.21

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (91) hide show
  1. checksums.yaml +4 -4
  2. data/.gitignore +9 -0
  3. data/Example/PodBuilder/.gitignore +6 -0
  4. data/Example/PodBuilder/.pod_builder/pod_builder +0 -0
  5. data/Example/{Frameworks → PodBuilder}/PodBuilder.json +10 -4
  6. data/Example/PodBuilder/Podfile +23 -0
  7. data/Example/PodBuilder/Podfile.restore +40 -0
  8. data/Example/PodBuilderExample.xcodeproj/project.pbxproj +3 -8
  9. data/Example/{PodBuilderExample.xcodeproj/xcuserdata/tomas.xcuserdatad/xcschemes/xcschememanagement.plist → PodBuilderExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist} +2 -8
  10. data/Example/PodBuilderExample/AppDelegate.swift +4 -0
  11. data/Example/Podfile +42 -1
  12. data/Example/Podfile.lock +426 -7
  13. data/Example/Pods-acknowledgements.md +210 -0
  14. data/Example/Pods-acknowledgements.plist +206 -0
  15. data/README.md +50 -13
  16. data/exe/pod_builder +39 -28
  17. data/lib/pod_builder/analyze.rb +32 -7
  18. data/lib/pod_builder/analyzer.rb +16 -0
  19. data/lib/pod_builder/command/build.rb +44 -161
  20. data/lib/pod_builder/command/build_all.rb +2 -2
  21. data/lib/pod_builder/command/clean.rb +34 -55
  22. data/lib/pod_builder/command/clear_lldbinit.rb +7 -3
  23. data/lib/pod_builder/command/deintegrate.rb +29 -7
  24. data/lib/pod_builder/command/generate_lfs.rb +3 -3
  25. data/lib/pod_builder/command/generate_podspec.rb +3 -2
  26. data/lib/pod_builder/command/info.rb +1 -1
  27. data/lib/pod_builder/command/init.rb +37 -14
  28. data/lib/pod_builder/command/install_sources.rb +21 -14
  29. data/lib/pod_builder/command/none.rb +2 -2
  30. data/lib/pod_builder/command/restore_all.rb +4 -4
  31. data/lib/pod_builder/command/switch.rb +137 -95
  32. data/lib/pod_builder/command/sync_podfile.rb +5 -3
  33. data/lib/pod_builder/command/update.rb +5 -6
  34. data/lib/pod_builder/command/update_lldbinit.rb +11 -9
  35. data/lib/pod_builder/configuration.rb +88 -13
  36. data/lib/pod_builder/core.rb +93 -12
  37. data/lib/pod_builder/info.rb +32 -98
  38. data/lib/pod_builder/install.rb +255 -195
  39. data/lib/pod_builder/licenses.rb +4 -4
  40. data/lib/pod_builder/podfile.rb +287 -73
  41. data/lib/pod_builder/podfile/post_actions.rb +9 -15
  42. data/lib/pod_builder/podfile_cp.rb +93 -0
  43. data/lib/pod_builder/podfile_item.rb +181 -82
  44. data/lib/pod_builder/podspec.rb +144 -135
  45. data/lib/pod_builder/rome/post_install.rb +240 -0
  46. data/lib/pod_builder/rome/pre_install.rb +6 -0
  47. data/lib/pod_builder/templates/build_podfile.template +3 -3
  48. data/lib/pod_builder/version.rb +1 -1
  49. data/pod-builder.gemspec +4 -4
  50. metadata +34 -65
  51. data/Example/Pods/Alamofire/LICENSE +0 -19
  52. data/Example/Pods/Alamofire/README.md +0 -242
  53. data/Example/Pods/Alamofire/Source/AFError.swift +0 -460
  54. data/Example/Pods/Alamofire/Source/Alamofire.swift +0 -465
  55. data/Example/Pods/Alamofire/Source/DispatchQueue+Alamofire.swift +0 -37
  56. data/Example/Pods/Alamofire/Source/MultipartFormData.swift +0 -580
  57. data/Example/Pods/Alamofire/Source/NetworkReachabilityManager.swift +0 -233
  58. data/Example/Pods/Alamofire/Source/Notifications.swift +0 -55
  59. data/Example/Pods/Alamofire/Source/ParameterEncoding.swift +0 -483
  60. data/Example/Pods/Alamofire/Source/Request.swift +0 -654
  61. data/Example/Pods/Alamofire/Source/Response.swift +0 -567
  62. data/Example/Pods/Alamofire/Source/ResponseSerialization.swift +0 -715
  63. data/Example/Pods/Alamofire/Source/Result.swift +0 -300
  64. data/Example/Pods/Alamofire/Source/ServerTrustPolicy.swift +0 -307
  65. data/Example/Pods/Alamofire/Source/SessionDelegate.swift +0 -725
  66. data/Example/Pods/Alamofire/Source/SessionManager.swift +0 -896
  67. data/Example/Pods/Alamofire/Source/TaskDelegate.swift +0 -466
  68. data/Example/Pods/Alamofire/Source/Timeline.swift +0 -136
  69. data/Example/Pods/Alamofire/Source/Validation.swift +0 -315
  70. data/Example/Pods/Manifest.lock +0 -16
  71. data/Example/Pods/Pods.xcodeproj/project.pbxproj +0 -673
  72. data/Example/Pods/Pods.xcodeproj/xcuserdata/tomas.xcuserdatad/xcschemes/Alamofire.xcscheme +0 -60
  73. data/Example/Pods/Pods.xcodeproj/xcuserdata/tomas.xcuserdatad/xcschemes/Pods-PodBuilderExample.xcscheme +0 -60
  74. data/Example/Pods/Pods.xcodeproj/xcuserdata/tomas.xcuserdatad/xcschemes/xcschememanagement.plist +0 -21
  75. data/Example/Pods/Target Support Files/Alamofire/Alamofire-dummy.m +0 -5
  76. data/Example/Pods/Target Support Files/Alamofire/Alamofire-prefix.pch +0 -12
  77. data/Example/Pods/Target Support Files/Alamofire/Alamofire-umbrella.h +0 -16
  78. data/Example/Pods/Target Support Files/Alamofire/Alamofire.modulemap +0 -6
  79. data/Example/Pods/Target Support Files/Alamofire/Alamofire.xcconfig +0 -9
  80. data/Example/Pods/Target Support Files/Alamofire/Info.plist +0 -26
  81. data/Example/Pods/Target Support Files/Pods-PodBuilderExample/Info.plist +0 -26
  82. data/Example/Pods/Target Support Files/Pods-PodBuilderExample/Pods-PodBuilderExample-acknowledgements.markdown +0 -26
  83. data/Example/Pods/Target Support Files/Pods-PodBuilderExample/Pods-PodBuilderExample-acknowledgements.plist +0 -58
  84. data/Example/Pods/Target Support Files/Pods-PodBuilderExample/Pods-PodBuilderExample-dummy.m +0 -5
  85. data/Example/Pods/Target Support Files/Pods-PodBuilderExample/Pods-PodBuilderExample-frameworks.sh +0 -153
  86. data/Example/Pods/Target Support Files/Pods-PodBuilderExample/Pods-PodBuilderExample-resources.sh +0 -118
  87. data/Example/Pods/Target Support Files/Pods-PodBuilderExample/Pods-PodBuilderExample-umbrella.h +0 -16
  88. data/Example/Pods/Target Support Files/Pods-PodBuilderExample/Pods-PodBuilderExample.debug.xcconfig +0 -11
  89. data/Example/Pods/Target Support Files/Pods-PodBuilderExample/Pods-PodBuilderExample.modulemap +0 -6
  90. data/Example/Pods/Target Support Files/Pods-PodBuilderExample/Pods-PodBuilderExample.release.xcconfig +0 -11
  91. data/lib/pod_builder/cocoapods/specification.rb +0 -27
@@ -1,37 +0,0 @@
1
- //
2
- // DispatchQueue+Alamofire.swift
3
- //
4
- // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
5
- //
6
- // Permission is hereby granted, free of charge, to any person obtaining a copy
7
- // of this software and associated documentation files (the "Software"), to deal
8
- // in the Software without restriction, including without limitation the rights
9
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
- // copies of the Software, and to permit persons to whom the Software is
11
- // furnished to do so, subject to the following conditions:
12
- //
13
- // The above copyright notice and this permission notice shall be included in
14
- // all copies or substantial portions of the Software.
15
- //
16
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
- // THE SOFTWARE.
23
- //
24
-
25
- import Dispatch
26
- import Foundation
27
-
28
- extension DispatchQueue {
29
- static var userInteractive: DispatchQueue { return DispatchQueue.global(qos: .userInteractive) }
30
- static var userInitiated: DispatchQueue { return DispatchQueue.global(qos: .userInitiated) }
31
- static var utility: DispatchQueue { return DispatchQueue.global(qos: .utility) }
32
- static var background: DispatchQueue { return DispatchQueue.global(qos: .background) }
33
-
34
- func after(_ delay: TimeInterval, execute closure: @escaping () -> Void) {
35
- asyncAfter(deadline: .now() + delay, execute: closure)
36
- }
37
- }
@@ -1,580 +0,0 @@
1
- //
2
- // MultipartFormData.swift
3
- //
4
- // Copyright (c) 2014-2018 Alamofire Software Foundation (http://alamofire.org/)
5
- //
6
- // Permission is hereby granted, free of charge, to any person obtaining a copy
7
- // of this software and associated documentation files (the "Software"), to deal
8
- // in the Software without restriction, including without limitation the rights
9
- // to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10
- // copies of the Software, and to permit persons to whom the Software is
11
- // furnished to do so, subject to the following conditions:
12
- //
13
- // The above copyright notice and this permission notice shall be included in
14
- // all copies or substantial portions of the Software.
15
- //
16
- // THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17
- // IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18
- // FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19
- // AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20
- // LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21
- // OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
22
- // THE SOFTWARE.
23
- //
24
-
25
- import Foundation
26
-
27
- #if os(iOS) || os(watchOS) || os(tvOS)
28
- import MobileCoreServices
29
- #elseif os(macOS)
30
- import CoreServices
31
- #endif
32
-
33
- /// Constructs `multipart/form-data` for uploads within an HTTP or HTTPS body. There are currently two ways to encode
34
- /// multipart form data. The first way is to encode the data directly in memory. This is very efficient, but can lead
35
- /// to memory issues if the dataset is too large. The second way is designed for larger datasets and will write all the
36
- /// data to a single file on disk with all the proper boundary segmentation. The second approach MUST be used for
37
- /// larger datasets such as video content, otherwise your app may run out of memory when trying to encode the dataset.
38
- ///
39
- /// For more information on `multipart/form-data` in general, please refer to the RFC-2388 and RFC-2045 specs as well
40
- /// and the w3 form documentation.
41
- ///
42
- /// - https://www.ietf.org/rfc/rfc2388.txt
43
- /// - https://www.ietf.org/rfc/rfc2045.txt
44
- /// - https://www.w3.org/TR/html401/interact/forms.html#h-17.13
45
- open class MultipartFormData {
46
-
47
- // MARK: - Helper Types
48
-
49
- struct EncodingCharacters {
50
- static let crlf = "\r\n"
51
- }
52
-
53
- struct BoundaryGenerator {
54
- enum BoundaryType {
55
- case initial, encapsulated, final
56
- }
57
-
58
- static func randomBoundary() -> String {
59
- return String(format: "alamofire.boundary.%08x%08x", arc4random(), arc4random())
60
- }
61
-
62
- static func boundaryData(forBoundaryType boundaryType: BoundaryType, boundary: String) -> Data {
63
- let boundaryText: String
64
-
65
- switch boundaryType {
66
- case .initial:
67
- boundaryText = "--\(boundary)\(EncodingCharacters.crlf)"
68
- case .encapsulated:
69
- boundaryText = "\(EncodingCharacters.crlf)--\(boundary)\(EncodingCharacters.crlf)"
70
- case .final:
71
- boundaryText = "\(EncodingCharacters.crlf)--\(boundary)--\(EncodingCharacters.crlf)"
72
- }
73
-
74
- return boundaryText.data(using: String.Encoding.utf8, allowLossyConversion: false)!
75
- }
76
- }
77
-
78
- class BodyPart {
79
- let headers: HTTPHeaders
80
- let bodyStream: InputStream
81
- let bodyContentLength: UInt64
82
- var hasInitialBoundary = false
83
- var hasFinalBoundary = false
84
-
85
- init(headers: HTTPHeaders, bodyStream: InputStream, bodyContentLength: UInt64) {
86
- self.headers = headers
87
- self.bodyStream = bodyStream
88
- self.bodyContentLength = bodyContentLength
89
- }
90
- }
91
-
92
- // MARK: - Properties
93
-
94
- /// The `Content-Type` header value containing the boundary used to generate the `multipart/form-data`.
95
- open lazy var contentType: String = "multipart/form-data; boundary=\(self.boundary)"
96
-
97
- /// The content length of all body parts used to generate the `multipart/form-data` not including the boundaries.
98
- public var contentLength: UInt64 { return bodyParts.reduce(0) { $0 + $1.bodyContentLength } }
99
-
100
- /// The boundary used to separate the body parts in the encoded form data.
101
- public let boundary: String
102
-
103
- private var bodyParts: [BodyPart]
104
- private var bodyPartError: AFError?
105
- private let streamBufferSize: Int
106
-
107
- // MARK: - Lifecycle
108
-
109
- /// Creates a multipart form data object.
110
- ///
111
- /// - returns: The multipart form data object.
112
- public init() {
113
- self.boundary = BoundaryGenerator.randomBoundary()
114
- self.bodyParts = []
115
-
116
- ///
117
- /// The optimal read/write buffer size in bytes for input and output streams is 1024 (1KB). For more
118
- /// information, please refer to the following article:
119
- /// - https://developer.apple.com/library/mac/documentation/Cocoa/Conceptual/Streams/Articles/ReadingInputStreams.html
120
- ///
121
-
122
- self.streamBufferSize = 1024
123
- }
124
-
125
- // MARK: - Body Parts
126
-
127
- /// Creates a body part from the data and appends it to the multipart form data object.
128
- ///
129
- /// The body part data will be encoded using the following format:
130
- ///
131
- /// - `Content-Disposition: form-data; name=#{name}` (HTTP Header)
132
- /// - Encoded data
133
- /// - Multipart form boundary
134
- ///
135
- /// - parameter data: The data to encode into the multipart form data.
136
- /// - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header.
137
- public func append(_ data: Data, withName name: String) {
138
- let headers = contentHeaders(withName: name)
139
- let stream = InputStream(data: data)
140
- let length = UInt64(data.count)
141
-
142
- append(stream, withLength: length, headers: headers)
143
- }
144
-
145
- /// Creates a body part from the data and appends it to the multipart form data object.
146
- ///
147
- /// The body part data will be encoded using the following format:
148
- ///
149
- /// - `Content-Disposition: form-data; name=#{name}` (HTTP Header)
150
- /// - `Content-Type: #{generated mimeType}` (HTTP Header)
151
- /// - Encoded data
152
- /// - Multipart form boundary
153
- ///
154
- /// - parameter data: The data to encode into the multipart form data.
155
- /// - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header.
156
- /// - parameter mimeType: The MIME type to associate with the data content type in the `Content-Type` HTTP header.
157
- public func append(_ data: Data, withName name: String, mimeType: String) {
158
- let headers = contentHeaders(withName: name, mimeType: mimeType)
159
- let stream = InputStream(data: data)
160
- let length = UInt64(data.count)
161
-
162
- append(stream, withLength: length, headers: headers)
163
- }
164
-
165
- /// Creates a body part from the data and appends it to the multipart form data object.
166
- ///
167
- /// The body part data will be encoded using the following format:
168
- ///
169
- /// - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header)
170
- /// - `Content-Type: #{mimeType}` (HTTP Header)
171
- /// - Encoded file data
172
- /// - Multipart form boundary
173
- ///
174
- /// - parameter data: The data to encode into the multipart form data.
175
- /// - parameter name: The name to associate with the data in the `Content-Disposition` HTTP header.
176
- /// - parameter fileName: The filename to associate with the data in the `Content-Disposition` HTTP header.
177
- /// - parameter mimeType: The MIME type to associate with the data in the `Content-Type` HTTP header.
178
- public func append(_ data: Data, withName name: String, fileName: String, mimeType: String) {
179
- let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType)
180
- let stream = InputStream(data: data)
181
- let length = UInt64(data.count)
182
-
183
- append(stream, withLength: length, headers: headers)
184
- }
185
-
186
- /// Creates a body part from the file and appends it to the multipart form data object.
187
- ///
188
- /// The body part data will be encoded using the following format:
189
- ///
190
- /// - `Content-Disposition: form-data; name=#{name}; filename=#{generated filename}` (HTTP Header)
191
- /// - `Content-Type: #{generated mimeType}` (HTTP Header)
192
- /// - Encoded file data
193
- /// - Multipart form boundary
194
- ///
195
- /// The filename in the `Content-Disposition` HTTP header is generated from the last path component of the
196
- /// `fileURL`. The `Content-Type` HTTP header MIME type is generated by mapping the `fileURL` extension to the
197
- /// system associated MIME type.
198
- ///
199
- /// - parameter fileURL: The URL of the file whose content will be encoded into the multipart form data.
200
- /// - parameter name: The name to associate with the file content in the `Content-Disposition` HTTP header.
201
- public func append(_ fileURL: URL, withName name: String) {
202
- let fileName = fileURL.lastPathComponent
203
- let pathExtension = fileURL.pathExtension
204
-
205
- if !fileName.isEmpty && !pathExtension.isEmpty {
206
- let mime = mimeType(forPathExtension: pathExtension)
207
- append(fileURL, withName: name, fileName: fileName, mimeType: mime)
208
- } else {
209
- setBodyPartError(withReason: .bodyPartFilenameInvalid(in: fileURL))
210
- }
211
- }
212
-
213
- /// Creates a body part from the file and appends it to the multipart form data object.
214
- ///
215
- /// The body part data will be encoded using the following format:
216
- ///
217
- /// - Content-Disposition: form-data; name=#{name}; filename=#{filename} (HTTP Header)
218
- /// - Content-Type: #{mimeType} (HTTP Header)
219
- /// - Encoded file data
220
- /// - Multipart form boundary
221
- ///
222
- /// - parameter fileURL: The URL of the file whose content will be encoded into the multipart form data.
223
- /// - parameter name: The name to associate with the file content in the `Content-Disposition` HTTP header.
224
- /// - parameter fileName: The filename to associate with the file content in the `Content-Disposition` HTTP header.
225
- /// - parameter mimeType: The MIME type to associate with the file content in the `Content-Type` HTTP header.
226
- public func append(_ fileURL: URL, withName name: String, fileName: String, mimeType: String) {
227
- let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType)
228
-
229
- //============================================================
230
- // Check 1 - is file URL?
231
- //============================================================
232
-
233
- guard fileURL.isFileURL else {
234
- setBodyPartError(withReason: .bodyPartURLInvalid(url: fileURL))
235
- return
236
- }
237
-
238
- //============================================================
239
- // Check 2 - is file URL reachable?
240
- //============================================================
241
-
242
- do {
243
- let isReachable = try fileURL.checkPromisedItemIsReachable()
244
- guard isReachable else {
245
- setBodyPartError(withReason: .bodyPartFileNotReachable(at: fileURL))
246
- return
247
- }
248
- } catch {
249
- setBodyPartError(withReason: .bodyPartFileNotReachableWithError(atURL: fileURL, error: error))
250
- return
251
- }
252
-
253
- //============================================================
254
- // Check 3 - is file URL a directory?
255
- //============================================================
256
-
257
- var isDirectory: ObjCBool = false
258
- let path = fileURL.path
259
-
260
- guard FileManager.default.fileExists(atPath: path, isDirectory: &isDirectory) && !isDirectory.boolValue else {
261
- setBodyPartError(withReason: .bodyPartFileIsDirectory(at: fileURL))
262
- return
263
- }
264
-
265
- //============================================================
266
- // Check 4 - can the file size be extracted?
267
- //============================================================
268
-
269
- let bodyContentLength: UInt64
270
-
271
- do {
272
- guard let fileSize = try FileManager.default.attributesOfItem(atPath: path)[.size] as? NSNumber else {
273
- setBodyPartError(withReason: .bodyPartFileSizeNotAvailable(at: fileURL))
274
- return
275
- }
276
-
277
- bodyContentLength = fileSize.uint64Value
278
- }
279
- catch {
280
- setBodyPartError(withReason: .bodyPartFileSizeQueryFailedWithError(forURL: fileURL, error: error))
281
- return
282
- }
283
-
284
- //============================================================
285
- // Check 5 - can a stream be created from file URL?
286
- //============================================================
287
-
288
- guard let stream = InputStream(url: fileURL) else {
289
- setBodyPartError(withReason: .bodyPartInputStreamCreationFailed(for: fileURL))
290
- return
291
- }
292
-
293
- append(stream, withLength: bodyContentLength, headers: headers)
294
- }
295
-
296
- /// Creates a body part from the stream and appends it to the multipart form data object.
297
- ///
298
- /// The body part data will be encoded using the following format:
299
- ///
300
- /// - `Content-Disposition: form-data; name=#{name}; filename=#{filename}` (HTTP Header)
301
- /// - `Content-Type: #{mimeType}` (HTTP Header)
302
- /// - Encoded stream data
303
- /// - Multipart form boundary
304
- ///
305
- /// - parameter stream: The input stream to encode in the multipart form data.
306
- /// - parameter length: The content length of the stream.
307
- /// - parameter name: The name to associate with the stream content in the `Content-Disposition` HTTP header.
308
- /// - parameter fileName: The filename to associate with the stream content in the `Content-Disposition` HTTP header.
309
- /// - parameter mimeType: The MIME type to associate with the stream content in the `Content-Type` HTTP header.
310
- public func append(
311
- _ stream: InputStream,
312
- withLength length: UInt64,
313
- name: String,
314
- fileName: String,
315
- mimeType: String)
316
- {
317
- let headers = contentHeaders(withName: name, fileName: fileName, mimeType: mimeType)
318
- append(stream, withLength: length, headers: headers)
319
- }
320
-
321
- /// Creates a body part with the headers, stream and length and appends it to the multipart form data object.
322
- ///
323
- /// The body part data will be encoded using the following format:
324
- ///
325
- /// - HTTP headers
326
- /// - Encoded stream data
327
- /// - Multipart form boundary
328
- ///
329
- /// - parameter stream: The input stream to encode in the multipart form data.
330
- /// - parameter length: The content length of the stream.
331
- /// - parameter headers: The HTTP headers for the body part.
332
- public func append(_ stream: InputStream, withLength length: UInt64, headers: HTTPHeaders) {
333
- let bodyPart = BodyPart(headers: headers, bodyStream: stream, bodyContentLength: length)
334
- bodyParts.append(bodyPart)
335
- }
336
-
337
- // MARK: - Data Encoding
338
-
339
- /// Encodes all the appended body parts into a single `Data` value.
340
- ///
341
- /// It is important to note that this method will load all the appended body parts into memory all at the same
342
- /// time. This method should only be used when the encoded data will have a small memory footprint. For large data
343
- /// cases, please use the `writeEncodedDataToDisk(fileURL:completionHandler:)` method.
344
- ///
345
- /// - throws: An `AFError` if encoding encounters an error.
346
- ///
347
- /// - returns: The encoded `Data` if encoding is successful.
348
- public func encode() throws -> Data {
349
- if let bodyPartError = bodyPartError {
350
- throw bodyPartError
351
- }
352
-
353
- var encoded = Data()
354
-
355
- bodyParts.first?.hasInitialBoundary = true
356
- bodyParts.last?.hasFinalBoundary = true
357
-
358
- for bodyPart in bodyParts {
359
- let encodedData = try encode(bodyPart)
360
- encoded.append(encodedData)
361
- }
362
-
363
- return encoded
364
- }
365
-
366
- /// Writes the appended body parts into the given file URL.
367
- ///
368
- /// This process is facilitated by reading and writing with input and output streams, respectively. Thus,
369
- /// this approach is very memory efficient and should be used for large body part data.
370
- ///
371
- /// - parameter fileURL: The file URL to write the multipart form data into.
372
- ///
373
- /// - throws: An `AFError` if encoding encounters an error.
374
- public func writeEncodedData(to fileURL: URL) throws {
375
- if let bodyPartError = bodyPartError {
376
- throw bodyPartError
377
- }
378
-
379
- if FileManager.default.fileExists(atPath: fileURL.path) {
380
- throw AFError.multipartEncodingFailed(reason: .outputStreamFileAlreadyExists(at: fileURL))
381
- } else if !fileURL.isFileURL {
382
- throw AFError.multipartEncodingFailed(reason: .outputStreamURLInvalid(url: fileURL))
383
- }
384
-
385
- guard let outputStream = OutputStream(url: fileURL, append: false) else {
386
- throw AFError.multipartEncodingFailed(reason: .outputStreamCreationFailed(for: fileURL))
387
- }
388
-
389
- outputStream.open()
390
- defer { outputStream.close() }
391
-
392
- self.bodyParts.first?.hasInitialBoundary = true
393
- self.bodyParts.last?.hasFinalBoundary = true
394
-
395
- for bodyPart in self.bodyParts {
396
- try write(bodyPart, to: outputStream)
397
- }
398
- }
399
-
400
- // MARK: - Private - Body Part Encoding
401
-
402
- private func encode(_ bodyPart: BodyPart) throws -> Data {
403
- var encoded = Data()
404
-
405
- let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData()
406
- encoded.append(initialData)
407
-
408
- let headerData = encodeHeaders(for: bodyPart)
409
- encoded.append(headerData)
410
-
411
- let bodyStreamData = try encodeBodyStream(for: bodyPart)
412
- encoded.append(bodyStreamData)
413
-
414
- if bodyPart.hasFinalBoundary {
415
- encoded.append(finalBoundaryData())
416
- }
417
-
418
- return encoded
419
- }
420
-
421
- private func encodeHeaders(for bodyPart: BodyPart) -> Data {
422
- var headerText = ""
423
-
424
- for (key, value) in bodyPart.headers {
425
- headerText += "\(key): \(value)\(EncodingCharacters.crlf)"
426
- }
427
- headerText += EncodingCharacters.crlf
428
-
429
- return headerText.data(using: String.Encoding.utf8, allowLossyConversion: false)!
430
- }
431
-
432
- private func encodeBodyStream(for bodyPart: BodyPart) throws -> Data {
433
- let inputStream = bodyPart.bodyStream
434
- inputStream.open()
435
- defer { inputStream.close() }
436
-
437
- var encoded = Data()
438
-
439
- while inputStream.hasBytesAvailable {
440
- var buffer = [UInt8](repeating: 0, count: streamBufferSize)
441
- let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize)
442
-
443
- if let error = inputStream.streamError {
444
- throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: error))
445
- }
446
-
447
- if bytesRead > 0 {
448
- encoded.append(buffer, count: bytesRead)
449
- } else {
450
- break
451
- }
452
- }
453
-
454
- return encoded
455
- }
456
-
457
- // MARK: - Private - Writing Body Part to Output Stream
458
-
459
- private func write(_ bodyPart: BodyPart, to outputStream: OutputStream) throws {
460
- try writeInitialBoundaryData(for: bodyPart, to: outputStream)
461
- try writeHeaderData(for: bodyPart, to: outputStream)
462
- try writeBodyStream(for: bodyPart, to: outputStream)
463
- try writeFinalBoundaryData(for: bodyPart, to: outputStream)
464
- }
465
-
466
- private func writeInitialBoundaryData(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
467
- let initialData = bodyPart.hasInitialBoundary ? initialBoundaryData() : encapsulatedBoundaryData()
468
- return try write(initialData, to: outputStream)
469
- }
470
-
471
- private func writeHeaderData(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
472
- let headerData = encodeHeaders(for: bodyPart)
473
- return try write(headerData, to: outputStream)
474
- }
475
-
476
- private func writeBodyStream(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
477
- let inputStream = bodyPart.bodyStream
478
-
479
- inputStream.open()
480
- defer { inputStream.close() }
481
-
482
- while inputStream.hasBytesAvailable {
483
- var buffer = [UInt8](repeating: 0, count: streamBufferSize)
484
- let bytesRead = inputStream.read(&buffer, maxLength: streamBufferSize)
485
-
486
- if let streamError = inputStream.streamError {
487
- throw AFError.multipartEncodingFailed(reason: .inputStreamReadFailed(error: streamError))
488
- }
489
-
490
- if bytesRead > 0 {
491
- if buffer.count != bytesRead {
492
- buffer = Array(buffer[0..<bytesRead])
493
- }
494
-
495
- try write(&buffer, to: outputStream)
496
- } else {
497
- break
498
- }
499
- }
500
- }
501
-
502
- private func writeFinalBoundaryData(for bodyPart: BodyPart, to outputStream: OutputStream) throws {
503
- if bodyPart.hasFinalBoundary {
504
- return try write(finalBoundaryData(), to: outputStream)
505
- }
506
- }
507
-
508
- // MARK: - Private - Writing Buffered Data to Output Stream
509
-
510
- private func write(_ data: Data, to outputStream: OutputStream) throws {
511
- var buffer = [UInt8](repeating: 0, count: data.count)
512
- data.copyBytes(to: &buffer, count: data.count)
513
-
514
- return try write(&buffer, to: outputStream)
515
- }
516
-
517
- private func write(_ buffer: inout [UInt8], to outputStream: OutputStream) throws {
518
- var bytesToWrite = buffer.count
519
-
520
- while bytesToWrite > 0, outputStream.hasSpaceAvailable {
521
- let bytesWritten = outputStream.write(buffer, maxLength: bytesToWrite)
522
-
523
- if let error = outputStream.streamError {
524
- throw AFError.multipartEncodingFailed(reason: .outputStreamWriteFailed(error: error))
525
- }
526
-
527
- bytesToWrite -= bytesWritten
528
-
529
- if bytesToWrite > 0 {
530
- buffer = Array(buffer[bytesWritten..<buffer.count])
531
- }
532
- }
533
- }
534
-
535
- // MARK: - Private - Mime Type
536
-
537
- private func mimeType(forPathExtension pathExtension: String) -> String {
538
- if
539
- let id = UTTypeCreatePreferredIdentifierForTag(kUTTagClassFilenameExtension, pathExtension as CFString, nil)?.takeRetainedValue(),
540
- let contentType = UTTypeCopyPreferredTagWithClass(id, kUTTagClassMIMEType)?.takeRetainedValue()
541
- {
542
- return contentType as String
543
- }
544
-
545
- return "application/octet-stream"
546
- }
547
-
548
- // MARK: - Private - Content Headers
549
-
550
- private func contentHeaders(withName name: String, fileName: String? = nil, mimeType: String? = nil) -> [String: String] {
551
- var disposition = "form-data; name=\"\(name)\""
552
- if let fileName = fileName { disposition += "; filename=\"\(fileName)\"" }
553
-
554
- var headers = ["Content-Disposition": disposition]
555
- if let mimeType = mimeType { headers["Content-Type"] = mimeType }
556
-
557
- return headers
558
- }
559
-
560
- // MARK: - Private - Boundary Encoding
561
-
562
- private func initialBoundaryData() -> Data {
563
- return BoundaryGenerator.boundaryData(forBoundaryType: .initial, boundary: boundary)
564
- }
565
-
566
- private func encapsulatedBoundaryData() -> Data {
567
- return BoundaryGenerator.boundaryData(forBoundaryType: .encapsulated, boundary: boundary)
568
- }
569
-
570
- private func finalBoundaryData() -> Data {
571
- return BoundaryGenerator.boundaryData(forBoundaryType: .final, boundary: boundary)
572
- }
573
-
574
- // MARK: - Private - Errors
575
-
576
- private func setBodyPartError(withReason reason: AFError.MultipartEncodingFailureReason) {
577
- guard bodyPartError == nil else { return }
578
- bodyPartError = AFError.multipartEncodingFailed(reason: reason)
579
- }
580
- }