Skip to content

Commit c7ac7f3

Browse files
committed
Allow complex custom fields
1 parent 1f8aff8 commit c7ac7f3

File tree

5 files changed

+329
-10
lines changed

5 files changed

+329
-10
lines changed

.gitignore

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,7 @@ DerivedData/
5757
*.perspectivev3
5858
!default.perspectivev3
5959
xcuserdata/
60+
.vscode/
6061

6162
## Other
6263
*.moved-aside
@@ -121,7 +122,7 @@ playground.xcworkspace
121122
# Package.resolved
122123
.build/
123124
# Add this line if you want to avoid checking in Xcode SPM integration.
124-
# .swiftpm/xcode
125+
.swiftpm/xcode
125126

126127
# CocoaPods
127128
# We recommend against adding the Pods directory to your .gitignore. However

Sources/TransloaditKit/Transloadit.swift

Lines changed: 229 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@ public enum TransloaditError: Error {
88
case couldNotCreateAssembly(underlyingError: Error)
99
case couldNotUploadFile(underlyingError: Error)
1010
case couldNotClearCache(underlyingError: Error)
11+
case invalidCustomFields
1112
}
1213

1314
public protocol TransloaditFileDelegate: AnyObject {
@@ -134,6 +135,31 @@ public final class Transloadit {
134135
throw TransloaditError.couldNotClearCache(underlyingError: error)
135136
}
136137
}
138+
139+
/// Create an assembly, do not upload a file.
140+
///
141+
/// This is useful for when you want to import a file from a different source, such as a third party storage service.
142+
///
143+
/// If you wish to upload a file and check for its processing status, please refer to ` public func createAssembly(steps: andUpload files: completion:)`
144+
///
145+
/// - Parameter steps: The steps of an Assembly.
146+
/// - Parameter expectedNumberOfFiles: The number of expected files to upload to this assembly
147+
/// - Parameter customFieldsObject: Encodable object that will be passed to the assembly creation
148+
/// - Parameter completion: The created assembly
149+
public func createAssembly<T: Encodable>(
150+
steps: [Step],
151+
expectedNumberOfFiles: Int = 1,
152+
customFieldsObject: T,
153+
completion: @escaping (Result<Assembly, TransloaditError>) -> Void
154+
) throws {
155+
try api.createAssembly(
156+
steps: steps,
157+
expectedNumberOfFiles: expectedNumberOfFiles,
158+
customFieldsObject: customFieldsObject) { result in
159+
let transloaditResult = result.mapError { error in TransloaditError.couldNotCreateAssembly(underlyingError: error) }
160+
completion(transloaditResult)
161+
}
162+
}
137163

138164
/// Create an assembly, do not upload a file.
139165
///
@@ -159,6 +185,33 @@ public final class Transloadit {
159185
completion(transloaditResult)
160186
}
161187
}
188+
189+
/// Create an assembly, do not upload a file.
190+
///
191+
/// This is useful for when you want to import a file from a different source, such as a third party storage service.
192+
///
193+
/// If you wish to upload a file and check for its processing status, please refer to ` public func createAssembly(templateId: andUpload files: completion:)`
194+
///
195+
/// - Parameters:
196+
/// - templateId: The templateId to use for this assembly
197+
/// - expectedNumberOfFiles: The number of expected files to upload to this assembly
198+
/// - customFieldsObject: JSON-encodable dictionary of custom parameters to pass to the assembly creation
199+
/// - completion: The created Assembly
200+
public func createAssembly<T: Encodable>(
201+
templateId: String,
202+
expectedNumberOfFiles: Int = 1,
203+
customFieldsObject: T,
204+
completion: @escaping (Result<Assembly, TransloaditError>) -> Void
205+
) throws {
206+
try api.createAssembly(
207+
templateId: templateId,
208+
expectedNumberOfFiles: expectedNumberOfFiles,
209+
customFieldsObject: customFieldsObject
210+
) { result in
211+
let transloaditResult = result.mapError { error in TransloaditError.couldNotCreateAssembly(underlyingError: error) }
212+
completion(transloaditResult)
213+
}
214+
}
162215

163216

164217
/// Create an assembly, do not upload a file.
@@ -187,6 +240,56 @@ public final class Transloadit {
187240
completion(transloaditResult)
188241
}
189242
}
243+
244+
@discardableResult
245+
public func createAssembly<T: Encodable>(
246+
templateId: String,
247+
andUpload files: [URL],
248+
customFieldsObject: T,
249+
completion: @escaping (Result<Assembly, TransloaditError>) -> Void
250+
) throws -> TransloaditPoller {
251+
func makeMetadata(assembly: Assembly) -> [String: String] {
252+
[:]
253+
}
254+
255+
let poller = TransloaditPoller(transloadit: self, didFinish: { [weak self] in
256+
guard let self = self else { return }
257+
self.pollers[files] = nil
258+
})
259+
260+
if let existingPoller = self.pollers[files], existingPoller === poller {
261+
assertionFailure("Transloadit: Somehow already got a poller for this url and these files")
262+
}
263+
264+
try createAssembly(
265+
templateId: templateId,
266+
expectedNumberOfFiles: files.count,
267+
customFieldsObject: customFieldsObject,
268+
completion: { [weak self] result in
269+
guard let self = self else { return }
270+
271+
do {
272+
let assembly = try result.get()
273+
try self.tusClient.uploadFiles(
274+
filePaths: files,
275+
uploadURL: assembly.tusURL,
276+
customHeaders: makeMetadata(assembly: assembly),
277+
context: ["assembly": assembly.description, "fieldname": "file-input", "assembly_url": assembly.url.absoluteString]
278+
)
279+
280+
poller.assemblyURL = assembly.url
281+
282+
completion(.success(assembly))
283+
} catch let error where error is TransloaditAPIError {
284+
completion(.failure(TransloaditError.couldNotCreateAssembly(underlyingError: error)))
285+
} catch {
286+
completion(.failure(TransloaditError.couldNotUploadFile(underlyingError: error)))
287+
}
288+
})
289+
290+
pollers[files] = poller
291+
return poller
292+
}
190293

191294
@discardableResult
192295
public func createAssembly(
@@ -237,6 +340,78 @@ public final class Transloadit {
237340
pollers[files] = poller
238341
return poller
239342
}
343+
344+
/// Create an assembly and upload one or more files to it using the TUS protocol.
345+
///
346+
/// Returns a poller that you can use to check its processing status. You don't need to retain the poller, the `TransloadIt` instance will do that for you.
347+
///
348+
/// TIP: You can set transloadit's `delegate` for details about the file uploading.
349+
/// - Parameters:
350+
/// - steps: The steps of an assembly.
351+
/// - files: Paths to the files to upload
352+
/// - customFields: JSON-encodable dictionary of extra parameters to send along with assembly creation
353+
/// - completion: completion handler, called when upload is complete
354+
///
355+
/// Below you can see how you can create an assembly and poll for its upload status
356+
///```swift
357+
///
358+
/// transloadit.createAssembly(steps: [resizeStep], andUpload: files, completion: { assemblyResult in
359+
/// // received assembly response
360+
/// print(assemblyResult)
361+
/// }).pollAssemblyStatus { pollingResult in
362+
/// // received polling status
363+
/// print(pollingResult)
364+
/// }
365+
///```
366+
@discardableResult
367+
public func createAssembly<T: Encodable>(
368+
steps: [Step],
369+
andUpload files: [URL],
370+
customFieldsObject: T,
371+
completion: @escaping (Result<Assembly, TransloaditError>) -> Void
372+
) throws -> TransloaditPoller {
373+
func makeMetadata(assembly: Assembly) -> [String: String] {
374+
[:]
375+
}
376+
377+
let poller = TransloaditPoller(transloadit: self, didFinish: { [weak self] in
378+
guard let self = self else { return }
379+
self.pollers[files] = nil
380+
})
381+
382+
if let existingPoller = self.pollers[files], existingPoller === poller {
383+
assertionFailure("Transloadit: Somehow already got a poller for this url and these files")
384+
}
385+
386+
try createAssembly(
387+
steps: steps,
388+
expectedNumberOfFiles: files.count,
389+
customFieldsObject: customFieldsObject,
390+
completion: { [weak self] result in
391+
guard let self = self else { return }
392+
393+
do {
394+
let assembly = try result.get()
395+
try self.tusClient.uploadFiles(
396+
filePaths: files,
397+
uploadURL: assembly.tusURL,
398+
customHeaders: makeMetadata(assembly: assembly),
399+
context: ["assembly": assembly.description, "fieldname": "file-input", "assembly_url": assembly.url.absoluteString]
400+
)
401+
402+
poller.assemblyURL = assembly.url
403+
404+
completion(.success(assembly))
405+
} catch let error where error is TransloaditAPIError {
406+
completion(.failure(TransloaditError.couldNotCreateAssembly(underlyingError: error)))
407+
} catch {
408+
completion(.failure(TransloaditError.couldNotUploadFile(underlyingError: error)))
409+
}
410+
})
411+
412+
pollers[files] = poller
413+
return poller
414+
}
240415

241416
/// Create an assembly and upload one or more files to it using the TUS protocol.
242417
///
@@ -358,6 +533,60 @@ public final class Transloadit {
358533
}
359534
})
360535
}
536+
537+
@available(macOS 10.15, iOS 13, *)
538+
public func createAssembly<T: Encodable>(
539+
steps: [Step],
540+
expectedNumberOfFiles: Int = 1,
541+
customFieldsObject: T
542+
) async throws -> Assembly {
543+
return try await withCheckedThrowingContinuation { continuation in
544+
do {
545+
try createAssembly(
546+
steps: steps,
547+
expectedNumberOfFiles: expectedNumberOfFiles,
548+
customFieldsObject: customFieldsObject
549+
) { result in
550+
switch result {
551+
case .success(let assembly):
552+
continuation.resume(returning: assembly)
553+
case .failure(let transloaditError):
554+
continuation.resume(throwing: transloaditError)
555+
}
556+
}
557+
} catch {
558+
continuation.resume(throwing: error)
559+
}
560+
}
561+
}
562+
563+
@available(macOS 10.15, iOS 13, *)
564+
public func createAssembly<T: Encodable>(
565+
steps: [Step],
566+
andUpload files: [URL],
567+
customFieldsObject: T
568+
) async throws -> (Assembly, TransloaditPoller) {
569+
return try await withCheckedThrowingContinuation({ continuation in
570+
do {
571+
var poller: TransloaditPoller!
572+
poller = try createAssembly(
573+
steps: steps,
574+
andUpload: files,
575+
customFieldsObject: customFieldsObject
576+
) { result in
577+
578+
switch result {
579+
case .success(let assembly):
580+
continuation.resume(returning: (assembly, poller))
581+
case .failure(let transloaditError):
582+
continuation.resume(throwing: transloaditError)
583+
}
584+
}
585+
} catch {
586+
continuation.resume(throwing: error)
587+
}
588+
})
589+
}
361590

362591
#endif
363592

Sources/TransloaditKit/TransloaditAPI.swift

Lines changed: 42 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,11 +56,25 @@ final class TransloaditAPI: NSObject {
5656
self.delegateQueue = nil
5757
super.init()
5858
}
59+
60+
func createAssembly<T: Encodable>(
61+
templateId: String,
62+
expectedNumberOfFiles: Int,
63+
customFieldsObject: T,
64+
completion: @escaping (Result<Assembly, TransloaditAPIError>) -> Void
65+
) throws {
66+
try createAssembly(
67+
templateId: templateId,
68+
expectedNumberOfFiles: expectedNumberOfFiles,
69+
customFields: customFieldsObject.toDictionary(),
70+
completion: completion
71+
)
72+
}
5973

6074
func createAssembly(
6175
templateId: String,
6276
expectedNumberOfFiles: Int,
63-
customFields: [String: String],
77+
customFields: [String: Any],
6478
completion: @escaping (Result<Assembly, TransloaditAPIError>) -> Void
6579
) {
6680
guard let request = try? makeAssemblyRequest(
@@ -98,11 +112,25 @@ final class TransloaditAPI: NSObject {
98112
})
99113
task.resume()
100114
}
101-
115+
116+
func createAssembly<T: Encodable>(
117+
steps: [Step],
118+
expectedNumberOfFiles: Int,
119+
customFieldsObject: T,
120+
completion: @escaping (Result<Assembly, TransloaditAPIError>) -> Void
121+
) throws {
122+
try createAssembly(
123+
steps: steps,
124+
expectedNumberOfFiles: expectedNumberOfFiles,
125+
customFields: customFieldsObject.toDictionary(),
126+
completion: completion
127+
)
128+
}
129+
102130
func createAssembly(
103131
steps: [Step],
104132
expectedNumberOfFiles: Int,
105-
customFields: [String: String],
133+
customFields: [String: Any],
106134
completion: @escaping (Result<Assembly, TransloaditAPIError>) -> Void
107135
) {
108136
guard let request = try? makeAssemblyRequest(
@@ -144,7 +172,7 @@ final class TransloaditAPI: NSObject {
144172
private func makeAssemblyRequest(
145173
templateId: String,
146174
expectedNumberOfFiles: Int,
147-
customFields: [String: String]
175+
customFields: [String: Any]
148176
) throws -> (request: URLRequest, httpBody: URL) {
149177

150178
func makeBody(includeSecret: Bool) throws -> [String: String] {
@@ -214,7 +242,7 @@ final class TransloaditAPI: NSObject {
214242
private func makeAssemblyRequest(
215243
steps: [Step],
216244
expectedNumberOfFiles: Int,
217-
customFields: [String: String]
245+
customFields: [String: Any]
218246
) throws -> (request: URLRequest, httpBody: URL) {
219247

220248
func makeBody(includeSecret: Bool) throws -> [String: String] {
@@ -349,6 +377,15 @@ final class TransloaditAPI: NSObject {
349377
}
350378
}
351379

380+
fileprivate extension Encodable {
381+
func toDictionary() throws -> [String: Any] {
382+
let data = try JSONEncoder().encode(self)
383+
guard let dict = try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any] else {
384+
throw TransloaditError.invalidCustomFields
385+
}
386+
return dict
387+
}
388+
}
352389

353390
extension String {
354391

0 commit comments

Comments
 (0)