Skip to content

Commit e2827c5

Browse files
authored
feat: add helper methods to ParseFileTransferable protocol (#411)
* feat: add helper methods to ParseFileTransferable protocol * add more tests
1 parent 9ab5271 commit e2827c5

File tree

6 files changed

+426
-30
lines changed

6 files changed

+426
-30
lines changed

CHANGELOG.md

Lines changed: 11 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,23 @@
11
# Parse-Swift Changelog
22

33
### main
4-
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/4.12.0...main)
4+
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/4.13.0...main)
55
* _Contributing to this repo? Add info about your change here to be included in the next release_
66

7+
### 4.13.0
8+
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/4.12.0...4.13.0)
9+
10+
__New features__
11+
- Add helper methods to ParseFileTransferable protocol to assist with creating propper responses to file uploads ([#411](https://github.com/parse-community/Parse-Swift/pull/411)), thanks to [Corey Baker](https://github.com/cbaker6).
12+
13+
__Fixes__
14+
- Remove cached error responses when decoding errors occur ([#411](https://github.com/parse-community/Parse-Swift/pull/411)), thanks to [Corey Baker](https://github.com/cbaker6).
15+
716
### 4.12.0
817
[Full Changelog](https://github.com/parse-community/Parse-Swift/compare/4.11.0...4.12.0)
918

1019
__New features__
11-
- Add the ParseFileTransferable protocol for overriding the default transfer behavior for ParseFile's. Allows for direct uploads to other file storage
12-
providers ([#408](https://github.com/parse-community/Parse-Swift/pull/408)), thanks to [Corey Baker](https://github.com/cbaker6).
20+
- Add the ParseFileTransferable protocol for overriding the default transfer behavior for ParseFile's. Allows for direct uploads to other file storage providers ([#410](https://github.com/parse-community/Parse-Swift/pull/410)), thanks to [Corey Baker](https://github.com/cbaker6).
1321
- Add the become method to ParseInstallation which allows any ParseInstallation to be copied to the current installation. This method can be used to migrate any ParseInstallation to the current installation in the Swift SDK ([#407](https://github.com/parse-community/Parse-Swift/pull/407)), thanks to [Corey Baker](https://github.com/cbaker6).
1422

1523
__Fixes__

ParseSwift.xcodeproj/project.pbxproj

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -327,6 +327,9 @@
327327
704E781D28CFFAF80075F952 /* ParseFileDefaultTransfer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 704E781B28CFFAF80075F952 /* ParseFileDefaultTransfer.swift */; };
328328
704E781E28CFFAF80075F952 /* ParseFileDefaultTransfer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 704E781B28CFFAF80075F952 /* ParseFileDefaultTransfer.swift */; };
329329
704E781F28CFFAF80075F952 /* ParseFileDefaultTransfer.swift in Sources */ = {isa = PBXBuildFile; fileRef = 704E781B28CFFAF80075F952 /* ParseFileDefaultTransfer.swift */; };
330+
704E782128D0D73E0075F952 /* ParseFileTransferableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 704E782028D0D73E0075F952 /* ParseFileTransferableTests.swift */; };
331+
704E782228D0D73E0075F952 /* ParseFileTransferableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 704E782028D0D73E0075F952 /* ParseFileTransferableTests.swift */; };
332+
704E782328D0D73E0075F952 /* ParseFileTransferableTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 704E782028D0D73E0075F952 /* ParseFileTransferableTests.swift */; };
330333
705025992842FD3B008D6624 /* ParseCLPTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705025982842FD3B008D6624 /* ParseCLPTests.swift */; };
331334
7050259A2842FD3B008D6624 /* ParseCLPTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705025982842FD3B008D6624 /* ParseCLPTests.swift */; };
332335
7050259B2842FD3B008D6624 /* ParseCLPTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 705025982842FD3B008D6624 /* ParseCLPTests.swift */; };
@@ -1244,6 +1247,7 @@
12441247
704C886B28BE69A8008E6B01 /* ParseConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseConfiguration.swift; sourceTree = "<group>"; };
12451248
704E781628CFD8A00075F952 /* ParseFileTransferable.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseFileTransferable.swift; sourceTree = "<group>"; };
12461249
704E781B28CFFAF80075F952 /* ParseFileDefaultTransfer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseFileDefaultTransfer.swift; sourceTree = "<group>"; };
1250+
704E782028D0D73E0075F952 /* ParseFileTransferableTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseFileTransferableTests.swift; sourceTree = "<group>"; };
12471251
705025982842FD3B008D6624 /* ParseCLPTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseCLPTests.swift; sourceTree = "<group>"; };
12481252
7050259C2843F0CF008D6624 /* ParseSchemaAsyncTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseSchemaAsyncTests.swift; sourceTree = "<group>"; };
12491253
705025A02843F0E7008D6624 /* ParseSchemaCombineTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ParseSchemaCombineTests.swift; sourceTree = "<group>"; };
@@ -1620,6 +1624,7 @@
16201624
7044C1F825C5CFAB0011F6E7 /* ParseFileCombineTests.swift */,
16211625
705A99F8259807F900B3547F /* ParseFileManagerTests.swift */,
16221626
705727882593FF8000F0ADD5 /* ParseFileTests.swift */,
1627+
704E782028D0D73E0075F952 /* ParseFileTransferableTests.swift */,
16231628
70BC0B32251903D1001556DB /* ParseGeoPointTests.swift */,
16241629
70F03A592780EAB000E5AFB4 /* ParseGitHubCombineTests.swift */,
16251630
70F03A5D2780EAC700E5AFB4 /* ParseGitHubTests.swift */,
@@ -2926,6 +2931,7 @@
29262931
7044C20625C5D6780011F6E7 /* ParseQueryCombineTests.swift in Sources */,
29272932
70C5508525B4A68700B5DBC2 /* ParseOperationTests.swift in Sources */,
29282933
7023800F2747FCCD00EFC443 /* ExtensionsTests.swift in Sources */,
2934+
704E782128D0D73E0075F952 /* ParseFileTransferableTests.swift in Sources */,
29292935
917BA43E2703E84000F8D747 /* ParseOperationAsyncTests.swift in Sources */,
29302936
7037DAB226384DE1005D7E62 /* TestParseEncoder.swift in Sources */,
29312937
7004C24D25B69207005E0AD9 /* ParseRoleTests.swift in Sources */,
@@ -3246,6 +3252,7 @@
32463252
7044C20825C5D6780011F6E7 /* ParseQueryCombineTests.swift in Sources */,
32473253
70C5508725B4A68700B5DBC2 /* ParseOperationTests.swift in Sources */,
32483254
702380112747FCCD00EFC443 /* ExtensionsTests.swift in Sources */,
3255+
704E782328D0D73E0075F952 /* ParseFileTransferableTests.swift in Sources */,
32493256
917BA4402703E84000F8D747 /* ParseOperationAsyncTests.swift in Sources */,
32503257
7037DAB426384DE1005D7E62 /* TestParseEncoder.swift in Sources */,
32513258
7004C26125B6920B005E0AD9 /* ParseRoleTests.swift in Sources */,
@@ -3369,6 +3376,7 @@
33693376
7044C20725C5D6780011F6E7 /* ParseQueryCombineTests.swift in Sources */,
33703377
70C5508625B4A68700B5DBC2 /* ParseOperationTests.swift in Sources */,
33713378
702380102747FCCD00EFC443 /* ExtensionsTests.swift in Sources */,
3379+
704E782228D0D73E0075F952 /* ParseFileTransferableTests.swift in Sources */,
33723380
917BA43F2703E84000F8D747 /* ParseOperationAsyncTests.swift in Sources */,
33733381
7037DAB326384DE1005D7E62 /* TestParseEncoder.swift in Sources */,
33743382
7004C25725B6920A005E0AD9 /* ParseRoleTests.swift in Sources */,

Sources/ParseSwift/API/Responses.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -137,7 +137,7 @@ internal struct LoginSignupResponse: Codable {
137137
}
138138

139139
// MARK: ParseFile
140-
internal struct FileUploadResponse: Decodable {
140+
internal struct FileUploadResponse: Codable {
141141
let name: String
142142
let url: URL
143143

Sources/ParseSwift/Extensions/URLSession.swift

Lines changed: 36 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -95,13 +95,15 @@ internal extension URLSession {
9595
do {
9696
responseData = try ParseCoding.jsonEncoder().encode(pushStatus)
9797
} catch {
98+
URLSession.parse.configuration.urlCache?.removeCachedResponse(for: request)
9899
return .failure(ParseError(code: .unknownError, message: error.localizedDescription))
99100
}
100101
}
101102
}
102103
do {
103104
return try .success(mapper(responseData))
104105
} catch {
106+
URLSession.parse.configuration.urlCache?.removeCachedResponse(for: request)
105107
guard let parseError = error as? ParseError else {
106108
guard JSONSerialization.isValidJSONObject(responseData),
107109
let json = try? JSONSerialization
@@ -226,26 +228,42 @@ internal extension URLSession {
226228
) {
227229
var task: URLSessionTask?
228230
if let data = data {
229-
task = ParseSwift
230-
.configuration
231-
.parseFileTransfer
232-
.upload(with: request, from: data) { (responseData, urlResponse, responseError) in
233-
completion(self.makeResult(request: request,
234-
responseData: responseData,
235-
urlResponse: urlResponse,
236-
responseError: responseError,
237-
mapper: mapper))
231+
do {
232+
task = try ParseSwift
233+
.configuration
234+
.parseFileTransfer
235+
.upload(with: request,
236+
from: data) { (responseData, urlResponse, updatedRequest, responseError) in
237+
completion(self.makeResult(request: updatedRequest ?? request,
238+
responseData: responseData,
239+
urlResponse: urlResponse,
240+
responseError: responseError,
241+
mapper: mapper))
242+
}
243+
} catch {
244+
let defaultError = ParseError(code: .unknownError,
245+
message: "Error uploading file: \(String(describing: error))")
246+
let parseError = error as? ParseError ?? defaultError
247+
completion(.failure(parseError))
238248
}
239249
} else if let file = file {
240-
task = ParseSwift
241-
.configuration
242-
.parseFileTransfer
243-
.upload(with: request, fromFile: file) { (responseData, urlResponse, responseError) in
244-
completion(self.makeResult(request: request,
245-
responseData: responseData,
246-
urlResponse: urlResponse,
247-
responseError: responseError,
248-
mapper: mapper))
250+
do {
251+
task = try ParseSwift
252+
.configuration
253+
.parseFileTransfer
254+
.upload(with: request,
255+
fromFile: file) { (responseData, urlResponse, updatedRequest, responseError) in
256+
completion(self.makeResult(request: updatedRequest ?? request,
257+
responseData: responseData,
258+
urlResponse: urlResponse,
259+
responseError: responseError,
260+
mapper: mapper))
261+
}
262+
} catch {
263+
let defaultError = ParseError(code: .unknownError,
264+
message: "Error uploading file: \(String(describing: error))")
265+
let parseError = error as? ParseError ?? defaultError
266+
completion(.failure(parseError))
249267
}
250268
} else {
251269
completion(.failure(ParseError(code: .unknownError, message: "data and file both cannot be nil")))

Sources/ParseSwift/Protocols/ParseFileTransferable.swift

Lines changed: 65 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -23,11 +23,14 @@ public protocol ParseFileTransferable: AnyObject {
2323
request type, and so on.
2424
- parameter fileURL: The URL of the file to upload.
2525
- parameter completion: The completion handler to call when the load request
26-
is complete. This handler is executed on the delegate queue.
26+
is complete. Should be in the form `(Data?, URLResponse?, URLRequest?, Error?)`.
27+
`Data` and `URLResponse` should be created using `makeSuccessfulUploadResponse()`.
28+
`URLRequest` is the request used to upload the file if available. `Error` is any error that occured
29+
that prevented the file upload.
2730
*/
2831
func upload(with request: URLRequest,
2932
fromFile fileURL: URL,
30-
completion: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionUploadTask
33+
completion: @escaping (Data?, URLResponse?, URLRequest?, Error?) -> Void) throws -> URLSessionUploadTask
3134

3235
/**
3336
Creates a task that performs an HTTP request for the specified URL request
@@ -36,23 +39,77 @@ public protocol ParseFileTransferable: AnyObject {
3639
request type, and so on.
3740
- parameter bodyData: The body data for the request.
3841
- parameter completion: The completion handler to call when the load request
39-
is complete. This handler is executed on the delegate queue.
42+
is complete. Should be in the form `(Data?, URLResponse?, URLRequest?, Error?)`.
43+
`Data` and `URLResponse` should be created using `makeSuccessfulUploadResponse()`.
44+
`URLRequest` is the request used to upload the file if available. `Error` is any error that occured
45+
that prevented the file upload.
4046
*/
4147
func upload(with request: URLRequest,
4248
from bodyData: Data?,
43-
completion: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionUploadTask
49+
completion: @escaping (Data?, URLResponse?, URLRequest?, Error?) -> Void) throws -> URLSessionUploadTask
50+
51+
/**
52+
Compose a valid file upload response with a name and url.
53+
54+
Use this method after uploading a file to any file storage to
55+
respond to the Swift SDK upload request.
56+
- parameter name: The name of the file.
57+
- parameter url: The url of the file that was stored.
58+
- returns: A tuple of `(Data, HTTPURLResponse?)` where `Data` is the
59+
JSON encoded file upload response and `HTTPURLResponse` is the metadata
60+
associated with the response to the load request.
61+
*/
62+
func makeSuccessfulUploadResponse(_ name: String, url: URL) throws -> (Data, HTTPURLResponse?)
63+
64+
/**
65+
Compose a dummy upload task.
66+
67+
Use this method if you do not need the Parse Swift SDK to start
68+
your upload task for you.
69+
- returns: A dummy upload task that starts an upload of zero bytes
70+
to localhost.
71+
- throws: An error of type `ParseError`.
72+
*/
73+
func makeDummyUploadTask() throws -> URLSessionUploadTask
4474
}
4575

76+
// MARK: Default Implementation - Internal
4677
extension ParseFileTransferable {
4778
func upload(with request: URLRequest,
4879
fromFile fileURL: URL,
49-
completion: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionUploadTask {
50-
URLSession.parse.uploadTask(with: request, fromFile: fileURL, completionHandler: completion)
80+
// swiftlint:disable:next line_length
81+
completion: @escaping (Data?, URLResponse?, URLRequest?, Error?) -> Void) throws -> URLSessionUploadTask {
82+
URLSession.parse.uploadTask(with: request, fromFile: fileURL) { (data, response, error) in
83+
completion(data, response, request, error)
84+
}
5185
}
5286

5387
func upload(with request: URLRequest,
5488
from bodyData: Data?,
55-
completion: @escaping (Data?, URLResponse?, Error?) -> Void) -> URLSessionUploadTask {
56-
URLSession.parse.uploadTask(with: request, from: bodyData, completionHandler: completion)
89+
// swiftlint:disable:next line_length
90+
completion: @escaping (Data?, URLResponse?, URLRequest?, Error?) -> Void) throws -> URLSessionUploadTask {
91+
URLSession.parse.uploadTask(with: request, from: bodyData) { (data, response, error) in
92+
completion(data, response, request, error)
93+
}
94+
}
95+
}
96+
97+
// MARK: Default Implementation - Public
98+
public extension ParseFileTransferable {
99+
func makeSuccessfulUploadResponse(_ name: String, url: URL) throws -> (Data, HTTPURLResponse?) {
100+
let responseData = FileUploadResponse(name: name, url: url)
101+
let response = HTTPURLResponse(url: url,
102+
statusCode: 200,
103+
httpVersion: nil,
104+
headerFields: nil)
105+
let encodedResponseData = try ParseCoding.jsonEncoder().encode(responseData)
106+
return (encodedResponseData, response)
107+
}
108+
109+
func makeDummyUploadTask() throws -> URLSessionUploadTask {
110+
guard let url = URL(string: "http://localhost") else {
111+
throw ParseError(code: .unknownError, message: "Could not create URL")
112+
}
113+
return URLSession.shared.uploadTask(with: .init(url: url), from: Data())
57114
}
58115
}

0 commit comments

Comments
 (0)