When sending relatively small amounts of data to a server using JSON or URL encoded parameters and data you don't need to setup anything.
If you need to send much larger amounts of data from Data in memory, a file URL, or an InputStream, we suggest setting the appropriate .largeData options for the transferMode property.
This allows you to track the progress of the upload:
let req = HTTPRequest {
$0.url = URL(string: "http://ipv4.download.thinkbroadband.com/5MB.zip")!
$0.transferMode = .largeData
$0.method = .get
}
// You can monitor the progress via Combine APIs
req.$progress.sink { progress in
print("Downloading percentage: \(progress?.percentage ?? 0)%")
}.store(in: &observerBag)
let response = try await req.fetch(client)When using .largeData transfer mode, the data is automatically downloaded into a temporary file located by the .dataFileURL property.
NOTE: If you call
.datainstead of.dataFileURLthe data contained in the file will be automatically loaded into RAM. This should be considered carefully for especially large file transfers.
The progress property is of type HTTPProgress which is a struct with the following properties:
event: kind of progress event (uploadordownloadis triggered multiple times during the transfer operation.failedorresumedis sent once only when download has failed, as last track report, orresumedas first track report followed byupload/downloadevents).progress: the instance ofProgressobject you can use to update the UIcurrentLengthandexpectedLengthwith the current status of the transfer and the total expected size (not all servers will return this data, so you may find both values set to 0).percentage: if tracking is available from the server side, then this value represents the percentage of the transfer.partialData: if the transfer fails, then this contains the partially downloaded data.
Bad things happened. If your download fails due to network failure or because your user wanted to cancel it, you may want to store the partially downloaded data in order to attempt to resume the download later.
If you want to cancel a download be sure to call cancel(byProducingResumeData:) with a valid callback.
The callback will contain a copy of the partial data that you can store in a temporary directory to resume the download.
For example:
// Somewhere in your code you may want to cancel the download
// and leave the option to resume it.
req.cancel(byProducingResumeData: { partialData in
let partialDataURL = URL(file: "/sandbox/location")
try partialData.write(to: partialDataURL) // resumable data
})If your download fails due to network error the last progress message contains the path to the resumable data:
req.$progress.sink { progress in
if progress?.event == .failed, let partialData = progress?.partialData {
// save it somewhere or assign directly to a new request's `.partialData`
saveDataInTempLocation(partialData)
}
}.store(in: &observerBag)Moreover, the HTTPResponse object returned contains the dataFileURL with the temporary data written to file.
NOTE: It is your responsibility to remove these partial data files from disk.
In order to resume downloads with partial data you set the partialData property of the HTTPRequest (and set transferMode to .largeData):
let req = HTTPRequest(...)
req.partialData = ... // your saved partial data
req.$progress.sink { progress in
// You will receive a first progress.event == .resumed message if
// resume has succeeded.
// If you don't receive it, then resume has failed and the download started over.
// The next progress messages will be download/upload or fail.
}.store(in: &observerBag)
let response = try await req.fetch(client)