Alamofire POST request with progress - ios

I'm using Alamofire to do a POST request.
As this POST request can take a while and I want to keep track of the progress and display it as a ProgressView.
Alamofire.request(.POST, ApiLink.create_post, parameters: parameters, encoding: .JSON)
.progress { (bytesRead, totalBytesRead, totalBytesExpectedToRead) -> Void in
println("ENTER .PROGRESSS")
println("\(totalBytesRead) of \(totalBytesExpectedToRead)")
self.progressView.setProgress(Float(totalBytesRead) / Float(totalBytesExpectedToRead), animated: true)
}
.responseJSON { (_, _, mydata, _) in
println(mydata)
}
However, I've noticed that the .progress block only get called after the post request has ended instead of getting called multiple times to actually keep track of the progress.
println("ENTER .PROGRESSS") gets called only once (at the end)
How can I make .progress works with Alamofire.request POST ?
Also : My parameters include a base64 encoded image string. I'm using a back-end Ruby on Rails to process the image. It's that process that is taking quite some time.

What you can do instead is first use the ParameterEncoding enum to generate the HTTPBody data. Then you can pull that data out and pass it off to the Alamofire upload method. Here's a modified version of your same function that compiles in a playground and instead uses the upload function.
struct ApiLink {
static let create_post = "/my/path/for/create/post"
}
let parameters: [String: AnyObject] = ["key": "value"] // Make sure this has your image as well
let mutableURLRequest = NSMutableURLRequest(URL: NSURL(string: ApiLink.create_post)!)
mutableURLRequest.HTTPMethod = Method.POST.rawValue
let encodedURLRequest = ParameterEncoding.JSON.encode(mutableURLRequest, parameters: parameters).0
let data = encodedURLRequest.HTTPBody!
let progressView = UIProgressView()
Alamofire.upload(mutableURLRequest, data)
.progress { _, totalBytesRead, totalBytesExpectedToRead in
println("ENTER .PROGRESSS")
println("\(totalBytesRead) of \(totalBytesExpectedToRead)")
progressView.setProgress(Float(totalBytesRead) / Float(totalBytesExpectedToRead), animated: true)
}
.responseJSON { _, _, mydata, _ in
println(mydata)
}
This will certainly have progress updates as #mattt originally mentioned in his comment above.

As #cnoon said you can use upload method to track progress with some modifications. Here is what exactly worked with me:
let jsonData = NSJSONSerialization.dataWithJSONObject(jsonObject, options: .PrettyPrinted)
Alamofire.upload(.POST, "API URL String", headers: ["Content-Type": "application/json"], data: jsonData)
.validate()
.responseJSON(completionHandler: { (response: Response<AnyObject, NSError>) in
//Completion handler code
})
.progress({ (bytesWritten: Int64, totalBytesWritten: Int64, totalBytesExpectedToWrite: Int64) in
//Progress handler code
})
Note that you must set the "Content-Type" http header field value to "application/json" if the data is formatted as json to be decoded correctly at the backend.

There's a separate method in Alamofire to upload. Please check their documentation here.
They have sub-sections
Uploading with Progress
Uploading MultipartFormData
Which describes uploading.

Related

FAILURE: responseSerializationFailed(reason: Alamofire.AFError.ResponseSerializationFailureReason.inputDataNilOrZeroLength)

I'm trying to read the JSON using Alamofire. Here is my code:
let headers = ["Content-Type": "application/json"]
func getConfirmationCode(params: [String:Any], block: #escaping(_ data : JSON, _ success : Bool) -> Void) {
if let url = URL(string: baseURL + "getConfirmationCode.php") {
Alamofire.request(url, method: .post, parameters: params, encoding: JSONEncoding.default, headers: headers).responseJSON { (response) in
DispatchQueue.main.async {
if let value = response.result.value {
let json = JSON(value)
block(json, true)
} else {
block(JSON.null, false)
}
}
}
}
}
The JSON is valid, everything works perfectly in Postman, but I can't make it work. None of the suggestions on the web helped (changing the responseJSON to responseString/responseData, changing the JSONEncoding.default to URLEncoding.default, etc.)
Can anyone help me?
You should try to check response in postman in "Preview" tab of response section. sometime it displays proper json in "Pretty" section. but in "Preview" section it many contains some print statements of API response. and those print statements makes problem in json decoding.
also, if you want to get swift code of postman response you can always get it from postman's code feature:
Hope this helps..!

Set progressive view for multiple Alamofire calls in swift

In one Viewcontrollers, I have three Alamofire API calls...I wish to set progressive view's progress to 100% when the three API calls are completed?
PS: Also, can someone teach me how to do it for a single Alamofire API call too?
Thanks in advance.
Alamofire offering progress block for giving status about api progress status, you can use this to show your progress of you request or response like below for single api,
Alamofire.request("url", method: "httpMethod", parameters: "parameter", encoding: JSONEncoding.default, headers: "headers").downloadProgress(closure: { (progress) in
// Progress block called frequently during the lifecycle of api.
let fractionCompletedINpercentage = progress?.fractionCompleted * 100
}).validate().responseData(completionHandler: { (response) in
if response.result.isSuccess {
// Download success
} else if response.result.isFailure {
// Download failed
}
})
For handling multiple api, you can use Bool for each api and show your progress by taking minimum or average of fraction completed from all api progress.
Thanks
The download progress is available right in the Alamofire's doc
let utilityQueue = DispatchQueue.global(qos: .utility)
Alamofire.download("https://httpbin.org/image/png")
.downloadProgress(queue: utilityQueue) { progress in
print("Download Progress: \(progress.fractionCompleted)")
}
.responseData { response in
if let data = response.result.value {
let image = UIImage(data: data)
}
}
That said, it probably wouldn't work if the API response is gzipped or the content-length header isn't set.
Working method to use Alamofire
let strURL:String = "https://xxxx"
let parameters: Parameters = ["keyword": "current_timestamp"]
Alamofire.request(strURL, method: .post, parameters: parameters, encoding: URLEncoding.default).responseJSON { (response) in
print(response.data!)
print(response.result)
if let JSON = response.result.value {
print("JSON: \(JSON)")
}
}

How to set method, header, parameter in Alamofire 4 and Swift 3

In past version of Alamofire, for send method,header and parameter I used to do like this:
Alamofire.request(.GET, URLRequest, headers:headers, parameters: parameters)
but version 4 and swift 3 is different.
How can I set method, send header & parameter?
The migration guide at Alamofire github explains this very well.
Take a look here:
// Alamofire 3
let parameters: [String: AnyObject] = ["foo": "bar"]
Alamofire.request(.GET, urlString, parameters: parameters, encoding: .JSON)
.progress { bytesRead, totalBytesRead, totalBytesExpectedToRead in
print("Bytes: \(bytesRead), Total Bytes: \(totalBytesRead), Total Bytes Expected: \(totalBytesExpectedToRead)")
}
.validate { request, response in
// Custom evaluation closure (no access to server data)
return .success
}
.responseJSON { response in
debugPrint(response)
}
// Alamofire 4
let parameters: Parameters = ["foo": "bar"]
Alamofire.request(urlString, method: .get, parameters: parameters, encoding: JSONEncoding.default)
.downloadProgress(queue: DispatchQueue.utility) { progress in
print("Progress: \(progress.fractionCompleted)")
}
.validate { request, response, data in
// Custom evaluation closure now includes data (allows you to parse data to dig out error messages if necessary)
return .success
}
.responseJSON { response in
debugPrint(response)
}
The migration guide explained well, but there are no headers in the example, just to avoid confusions, bellow I add the example of a GET request to add them.
Alamofire.request(URL, method: .get, parameters: parameters, headers: headers)
.validate { request, response, data in
return .success
}
.responseJSON { response in
switch response.result {
case .success:
// do something
break
case .failure(let error):
// handle error
break
}
}
I found this information in here so, go and check it there if you have question related with the headers in the request.

Progress of a Alamofire request

Will it be possible to show progress for
Alamofire.request(.POST, URL, parameters: parameter, encoding: .JSON)
.responseJSON { response in
// Do your stuff
}
I get my images/documents as a base64 string then I convert it as files in mobile
Can I show a progress bar with percentage?
I am using Alamofire, Swift 2
The way you monitor progress in Alamofire is using the progress closure on a Request. More details on usage can be found in the README. While that example in the README demonstrates usage on a download request, it still works on a data request as well.
The one important note is that you do not always get perfect reporting back from the server for a data request. The reason is that the server does not always report an accurate content length before streaming the data. If the content length is unknown, the progress closure will be called, but the totalExpectedBytesToRead will always be -1.
In this situation, you can only report accurate progress if you know the approximate size of the data being downloaded. You could then use your approximate number in conjunction with the totalBytesRead value to compute an estimated download progress value.
Alamofire 4.0 and Swift 4.x
func startDownload(audioUrl:String) -> Void {
let fileUrl = self.getSaveFileUrl(fileName: audioUrl)
let destination: DownloadRequest.DownloadFileDestination = { _, _ in
return (fileUrl, [.removePreviousFile, .createIntermediateDirectories])
}
Alamofire.download(audioUrl, to:destination)
.downloadProgress { (progress) in
self.surahNameKana.text = (String)(progress.fractionCompleted)
}
.responseData { (data) in
// at this stage , the downloaded data are already saved in fileUrl
self.surahNameKana.text = "Completed!"
}
}
To make a download with progress for Swift 2.x users with Alamofire >= 3.0:
let url = "https://httpbin.org/stream/100" // this is for example..
let destination = Alamofire.Request.suggestedDownloadDestination(directory: .DocumentDirectory, domain: .UserDomainMask)
Alamofire.download(.GET, url, parameters: params, encoding: ParameterEncoding.URL,destination:destination)
.progress { bytesRead, totalBytesRead, totalBytesExpectedToRead in
// This closure is NOT called on the main queue for performance
// reasons. To update your ui, dispatch to the main queue.
dispatch_async(dispatch_get_main_queue()) {
// Here you can update your progress object
print("Total bytes read on main queue: \(totalBytesRead)")
print("Progress on main queue: \(Float(totalBytesRead) / Float(totalBytesExpectedToRead))")
}
}
.response { request, _, _, error in
print("\(request?.URL)") // original URL request
if let error = error {
let httpError: NSError = error
let statusCode = httpError.code
} else { //no errors
let filePath = NSSearchPathForDirectoriesInDomains(.DocumentDirectory, .UserDomainMask, true)[0]
print("File downloaded successfully: \(filePath)")
}
}

Alamofire uploading progress

I need to send a file with the parameters and tracking the progress of uploading. Method
Alamofire.request(.POST, "http://httpbin.org/post", parameters: parameters, encoding: .JSON)
don't track progress uploading. Method
Alamofire.upload(.POST, "http://httpbin.org/post", file: fileURL)
.progress { (bytesWritten, totalBytesWritten, totalBytesExpectedToWrite) in
println(totalBytesWritten)
}
.responseJSON { (request, response, JSON, error) in
println(JSON)
}
is not able to set the parameters
is it possible to send a file with the parameters and tracking the progress of uploading?
You have to use .uploadProgress instead of .progress.
Use this way
activeVidoeCell.uploadRequest = Alamofire.upload(fileData as Data, to: url, method: .put, headers: nil).uploadProgress(closure: { (progress) in
print(progress.fractionCompleted)
activeVidoeCell.downloadButton.setProgress(CGFloat(progress.fractionCompleted), animated: true)
}).responseJSON(completionHandler: { (result) in
completionHandler(result)
})

Resources