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
.data
instead of.dataFileURL
the 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 (upload
ordownload
is triggered multiple times during the transfer operation.failed
orresumed
is sent once only when download has failed, as last track report, orresumed
as first track report followed byupload/download
events).progress
: the instance ofProgress
object you can use to update the UIcurrentLength
andexpectedLength
with 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)