88import Foundation
99
1010/// The errors a TUSAPI can return
11- public enum TUSAPIError : Error {
11+ public enum TUSAPIError : Error , LocalizedError {
1212 case underlyingError( Error )
1313 case couldNotFetchStatus
1414 case couldNotFetchServerInfo
1515 case couldNotRetrieveOffset
1616 case couldNotRetrieveLocation
17- case failedRequest( HTTPURLResponse )
18-
17+ case failedRequest( HTTPURLResponse , Data ? )
18+
1919 public var localizedDescription : String {
2020 switch self {
2121 case . underlyingError( let error) :
@@ -28,10 +28,18 @@ public enum TUSAPIError: Error {
2828 return " Could not retrieve offset from response. "
2929 case . couldNotRetrieveLocation:
3030 return " Could not retrieve location from response. "
31- case . failedRequest( let response) :
32- return " Failed request with status code \( response. statusCode) . "
31+ case . failedRequest( let response, let data) :
32+ if let data, let message = String ( data: data, encoding: . utf8) {
33+ return " Failed request with status code \( response. statusCode) : \( message) "
34+ } else {
35+ return " Failed request with status code \( response. statusCode) . "
36+ }
3337 }
3438 }
39+
40+ public var errorDescription : String ? {
41+ localizedDescription
42+ }
3543}
3644
3745/// The status of an upload.
@@ -57,8 +65,9 @@ final class TUSAPI {
5765 private let sessionDelegate = SessionDataDelegate ( )
5866 private let queue = DispatchQueue ( label: " com.tus.TUSAPI " )
5967 private var backgroundHandler : ( ( ) -> Void ) ? = nil
60- private var callbacks : [ String : ( Result < HTTPURLResponse , Error > ) -> Void ] = [ : ]
61-
68+ private var callbacks : [ String : ( Result < ( Data ? , HTTPURLResponse ) , Error > ) -> Void ] = [ : ]
69+ private var taskData : [ String : Data ] = [ : ]
70+
6271 deinit {
6372 if session. delegate is SessionDataDelegate {
6473 session. finishTasksAndInvalidate ( )
@@ -137,10 +146,10 @@ final class TUSAPI {
137146 queue. sync {
138147 callbacks [ identifier] = { result in
139148 processResult ( completion: completion) {
140- let response = try result. get ( )
141-
149+ let ( data , response) = try result. get ( )
150+
142151 guard ( 200 ... 299 ) . contains ( response. statusCode) else {
143- throw TUSAPIError . failedRequest ( response)
152+ throw TUSAPIError . failedRequest ( response, data )
144153 }
145154
146155 guard let lengthStr = response. allHeaderFields [ caseInsensitive: " upload-Length " ] as? String ,
@@ -176,10 +185,10 @@ final class TUSAPI {
176185 queue. sync {
177186 callbacks [ identifier] = { result in
178187 processResult ( completion: completion) {
179- let response = try result. get ( )
180-
188+ let ( data , response) = try result. get ( )
189+
181190 guard ( 200 ... 299 ) . contains ( response. statusCode) else {
182- throw TUSAPIError . failedRequest ( response)
191+ throw TUSAPIError . failedRequest ( response, data )
183192 }
184193
185194 guard let location = response. allHeaderFields [ caseInsensitive: " location " ] as? String ,
@@ -281,10 +290,10 @@ final class TUSAPI {
281290 queue. sync {
282291 callbacks [ metaData. id. uuidString] = { result in
283292 processResult ( completion: completion) {
284- let response = try result. get ( )
285-
293+ let ( data , response) = try result. get ( )
294+
286295 guard ( 200 ... 299 ) . contains ( response. statusCode) else {
287- throw TUSAPIError . failedRequest ( response)
296+ throw TUSAPIError . failedRequest ( response, data )
288297 }
289298
290299 guard let offsetStr = response. allHeaderFields [ caseInsensitive: " upload-offset " ] as? String ,
@@ -332,7 +341,7 @@ final class TUSAPI {
332341 queue. sync {
333342 self . callbacks [ metaData. id. uuidString] = { result in
334343 processResult ( completion: completion) {
335- let response = try result. get ( )
344+ let ( data , response) = try result. get ( )
336345 guard let offsetStr = response. allHeaderFields [ caseInsensitive: " upload-offset " ] as? String ,
337346 let offset = Int ( offsetStr) else {
338347 throw TUSAPIError . couldNotRetrieveOffset
@@ -349,7 +358,7 @@ final class TUSAPI {
349358 queue. sync {
350359 self . callbacks [ metadata. id. uuidString] = { result in
351360 processResult ( completion: completion) {
352- let response = try result. get ( )
361+ let ( data , response) = try result. get ( )
353362 guard let offsetStr = response. allHeaderFields [ caseInsensitive: " upload-offset " ] as? String ,
354363 let offset = Int ( offsetStr) else {
355364 throw TUSAPIError . couldNotRetrieveOffset
@@ -436,7 +445,15 @@ extension Dictionary {
436445private extension TUSAPI {
437446 final class SessionDataDelegate : NSObject , URLSessionDataDelegate {
438447 weak var api : TUSAPI ?
439-
448+
449+ func urlSession( _ session: URLSession , dataTask: URLSessionDataTask , didReceive data: Data ) {
450+ guard let api, let identifier = dataTask. taskDescription else {
451+ return
452+ }
453+
454+ api. taskData [ identifier, default: Data ( ) ] . append ( data)
455+ }
456+
440457 func urlSession( _ session: URLSession , task: URLSessionTask , didCompleteWithError error: Error ? ) {
441458 api? . handleCompletionOfTask ( task, withError: error)
442459 }
@@ -454,6 +471,7 @@ private extension TUSAPI {
454471
455472 defer {
456473 callbacks. removeValue ( forKey: identifier)
474+ taskData. removeValue ( forKey: identifier)
457475 }
458476
459477 guard let completion = callbacks [ identifier] else {
@@ -469,8 +487,10 @@ private extension TUSAPI {
469487 completion ( . failure( TUSAPIError . underlyingError ( NetworkError . noHTTPURLResponse) ) )
470488 return
471489 }
472-
473- completion ( . success( response) )
490+
491+ let data = taskData [ identifier]
492+ let success = ( data, response)
493+ completion ( . success( success) )
474494 }
475495 }
476496
0 commit comments