I wonder how I could make the request executed one by one. It's about image processing. And the server cannot process more than one image. The problem is that I need to send 10 pictures at a time, and if I send everything at once, I have a problem with the timeout, and now I'm interested in how to send the next request when response from the previous one arrives?
func clearingImage() {
if clearingImageArray.count < 0 || indexOfClearImage >= clearingImageArray.count
{
guard let imageName = ImageFilenameUtilities.getNameOfImageFromPath(filePath: clearingImageArray[indexOfClearImage]) else
{
indexOfClearImage+=1
clearingImage()
return
}
guard let clearType = ImageFilenameUtilities.getClearTypeFromFilename(filename: imageName) else
{
indexOfClearImage+=1
clearingImage()
return
}
guard let image = gallery?.getImageForPath(path: clearingImageArray[indexOfClearImage]) else
{
indexOfClearImage+=1
clearingImage()
return
}
sendImageToServer(image: image, imageName: imageName)
}
}
func sendImageToServer(image:UIImage,imageName:String)
{
let url = "example.com"
let headers : HTTPHeaders = [
"Connection": "Keep-Alive"
// "Content-Type": "application/x-www-form-urlencoded"
]
let manager = Alamofire.SessionManager.default
manager.session.configuration.timeoutIntervalForRequest = 12000
manager.session.configuration.timeoutIntervalForResource = 12000
manager.upload(multipartFormData: { multipleData in
for (key, value) in parameter {
multipleData.append(value.data(using: String.Encoding.utf8)!, withName: key)
}
multipleData.append(
imageData, withName: "image", fileName: imageName, mimeType: "image/jpg")}, to: url, method: .post, headers: headers){
(result) in
switch result {
case .success(let upload, _, _):
upload.responseJSON { response in
self.indexOfClearImage += 1
if let image = UIImage(data: response.data!) {
//save image to galery
}
else
{
if let error = response.result.error {
if error._code == NSURLErrorTimedOut {
self.indexOfClearImage -= 1
}
}
}
}
case .failure(let encodingError):
//send next image to server
self.indexOfClearImage += 1
}
}
self.clearingImage()
}
Just send the 2nd request on the 1st completion block, when you get the response, and so on.
func firstRequest() {
AF.request("url/here").response { response in
self.secondRequest()
}
}
func secondRequest() {
AF.request("url/here").response { response in
// call 3rd request and so on...
}
}
Related
I am beginner iOS developer and I don't know how to upload a file using Alamofire 5, I know there are a lot of similar questions, but all the topics here are from very old versions and I couldn't get it working. I tried this code, but I couldn't fit to my case, it gives me success, but file is not uploaded and the result I get is not what I get in postman. This is the code:
func uploadFile(_ parameters: Parameters) {
AF.upload(multipartFormData: { multipartFormData in
URLEncoding.default.queryParameters(parameters).forEach { (key, value) in
if let data = value.data(using: .utf8) {
multipartFormData.append(data, withName: key)
}
}
}, to: url)
.responseDecodable(of: FileCreation.self) { response in
switch response.result {
case .success(let data):
print(data, "success")
case .failure(let error):
print(error)
}
}
}
usage:
#IBAction func upload(_ sender: UIButton) {
guard let data = image?.jpegData(compressionQuality: 0.5)! else { return }
let parameters = ["addFiles": data]
uploadFile(parameters)
}
Here's Xcode output:
Here you can see postman response after I upload file:
Alamofire.upload(multipartFormData: {
multipartFormData in
if let imageData = image[0].jpegData(compressionQuality: 0.6) {
multipartFormData.append(imageData, withName: "addFiles", fileName: "file.pdf", mimeType: "application/pdf")
}
for (key, value) in param {
multipartFormData.append((value as AnyObject).data(using: String.Encoding.utf8.rawValue)!, withName: key)
}
},to: apiurl, method: .post, headers: headers, encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.responseJSON {
response in
print(response.result)
}
//break
case .failure(let encodingError):
break
}
})
Try This
func uploadFilesToServer(_ url: String, method: HTTPMethod, parameters: [String:Any]?, file: [String:Any]?, fileType: String, fileName: String, headers:HTTPHeaders?, completionHandler: #escaping (_ result: Data?, _ success: Bool, _ status: String) -> ()) {
var status = Bool()
var message = String()
let url = URL(string: url)
AF.upload(multipartFormData: { multiPart in
if let files = file {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM-dd-yyyy-hh-mm-ss"
let dateString = dateFormatter.string(from: Date())
for (key, value) in files {
if fileType == "pdf" {
let filePath = URL(fileURLWithPath: value as! String)
multiPart.append(filePath, withName: "\(key)", fileName: fileName, mimeType: "file/pdf")
} else {
multiPart.append(value as! Data, withName: "\(key)", fileName: "Uploads\(dateString).png", mimeType: "image/png")
}
}
}
if let params = parameters {
for (key, value) in params {
multiPart.append((value as AnyObject).data(using: String.Encoding.utf8.rawValue)!, withName: key)
}
}
}, to: url!, method: method, headers: headers ?? nil)
.validate()
.uploadProgress(closure: { progress in
print(progress)
})
.responseJSON { response in
switch response.result {
case .success(let responseData):
print(responseData)
case .failure(let networkErr):
switch networkErr {
case .responseSerializationFailed(reason: _):
message = "Something went wrong"
case .sessionTaskFailed(error: let err):
message = err.localizedDescription
default:
message = "Something went wrong"
}
completionHandler(nil, false, message)
break
}
}
}
usage
uploadFilesToServer(url, method: .post, parameters: params, file: uploadFile, fileType: "pdf", fileName: fileNme, headers: tokenHeader) { [self] responseData, isSuccess, responseMsg in
if isSuccess {
} else {
}
}
I have a collection view which reloads once the data is fetched from the api.
I have condition that when a array count is greater than zero,it should reload the collection view.
The code is as given below:
if self.mainarray.count>0
{
self.collectionview1.reloadData()
}
If the mainarray count is 2,then it is reloading the collection view when the count is 1 itself.
How to make it reload only when the full data is fetched?
func getcart() {
print("getcart function",self.token)
let headers: HTTPHeaders = [
"Content-Type":"application/json",
"Authorization":"Bearer "+self.token!
]
AF.request("https:XXXX", method: .get, parameters: nil, encoding: JSONEncoding.default, headers: headers).responseJSON { response in
switch response.result {
case .success(let json):
// print("Validation Successful",json)
if let res = json as? [[String: Any]]{
print("res count is",res.count)
self.skuarray.removeAll()
for item in 0..<res.count
{
self.sku = res[item]["name"] as! String
self.skuarray.append(self.sku!)
let qty:Double = res[item]["qty"] as! Double
}
if self.skuarray.count > 0
{
for i in 0..<self.skuarray.count
{
self.getimage(sku:self.skuarray[i])
}
}
}
else
{
self.showAlert(message: "No products in the cart")
}
case let .failure(error):
print(error)
}
}
}
func getimage(sku: String)
{
AF.request("https:XXXXXX" + sku, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: nil).responseJSON { response in
//New
let decoder = JSONDecoder()
do {
let user = try decoder.decode(ProductDetailDataModel.self, from: response.data!)
self.detailmodel = user
// self.orgsearchDataModel = user
DispatchQueue.main.async
{
self.doLabelChange()
print("total images aree",self.mainarray)
if !self.mainarray.isEmpty
{
self.tableview1.reloadData()
}
}
} catch {
print(error.localizedDescription)
}
}
}
func doLabelChange(){
let sku = UserDefaults.standard.set((self.detailmodel?.sku)!, forKey: "sku")
let array = self.detailmodel?.mediaGalleryEntries
print("array is",array)
let image = array![0].file
print("image is ",image)
let imageurl = "https://adamas-intl.com/pub/media/catalog/product/" + image
print("imageurl is",imageurl)
self.mainarray.append(imageurl)
}
we are working to get an upload to an API with a POST method. We are trying to push an image. We are getting the next error
success(request: 2018-02-03 16:27:53.087629-0800 Optimio-iOS[5425:201118] CredStore - performQuery - Error copying matching creds. Error=-25300, query={
class = inet;
"m_Limit" = "m_LimitAll";
ptcl = htps;
"r_Attributes" = 1;
sdmn = "llegoelbigotes.ubiqme.es";
srvr = "llegoelbigotes.ubiqme.es";
sync = syna;
}
$ curl -v \
-X POST \
-H "Content-Type: multipart/form-data; boundary=alamofire.boundary.7105b62be90b880b" \
-H "Accept-Language: en;q=1.0, es-ES;q=0.9" \
-H "Authorization: Token 1b0ad885a95334154ca3bfa79b3f52ca35c25e0e" \
-H "User-Agent: Optimio-iOS/1.0 (Ubiqme-com.ubiqme.ubiqme; build:1; iOS 11.2.0) Alamofire/4.6.0" \
-H "Accept-Encoding: gzip;q=1.0, compress;q=0.5" \
"https://llegoelbigotes.ubiqme.es/api/new-ticket", streamingFromDisk: true, streamFileURL: Optional(file:///Users/polvallsortiz/Library/Developer/CoreSimulator/Devices/EE663E81-6611-4B69-ABD6-AF97D19D6FB7/data/Containers/Data/Application/E7B9F357-DD51-4512-8173-37C932C0E4B9/tmp/org.alamofire.manager/multipart.form.data/74C9B75E-614D-4FCD-9DBC-1F5D07D773C4))
Our code is the next one:
func myImageUploadRequest() {
guard let urlStr = "https://llegoelbigotes.ubiqme.es/api/new-ticket".addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed),
let url = URL(string: urlStr)
else {return}
let isPaymentCard = card ? "true" : "false"
let headers: HTTPHeaders = ["Authorization": "Token \(token!)"]
let param = [ "comment": comment, "category": String(category), "amount": String(money), "payment_card": isPaymentCard ]
Alamofire.upload(multipartFormData: { multipartFormData in
if let imageData = UIImageJPEGRepresentation(self.image,0.5)
{ multipartFormData.append(imageData,withName:"image",fileName:"image.jpeg",mimeType:"image/jpeg") }
for(key,value) in param {
multipartFormData.append("\(value)".data(using: String.Encoding.utf8)!, withName: key as String)
}
}, usingThreshold: UInt64.init(),
to: urlStr,
method: .post,
headers: headers) {(result) in
print(result)
switch result{
case .success(let upload, _, _):
upload.responseJSON {
response in
print("Succesfully uploaded")
if let err = response.error{
print(err)
return
}
}
case .failure(let error):
print("Error in upload: \(error.localizedDescription)")
print(error)
}
}
}
Anyone can show us some tips or say what might be wrong?
Thanks to all the community!
-> I have created a dynamic function for uploading Image and Files you can even upload multiple images and files my this
func makeFormDataAPICall<T: Codable>(url: String, params: [String: Any], objectType: T.Type,
success: #escaping(_ responseObject: T) -> Void,
failure: #escaping (_ error: String) -> Void) {
let name = self.randomNumberWith(digits: 6)
self.headers = [kAuthorization: self.userData?.authToken ?? ""]
//Network check
if Utilities.isConnectedToNetwork() {
guard let urlIs = URL(string: url) else {
failure(ServerAPI.errorMessages.kServerDownError)
return
}
// Almofire upload request
AF.upload(multipartFormData: { multiPartData in
for (key, value) in params {
if value is UIImage {
let img = (value as? UIImage)?.jpegData(compressionQuality: 0.5)
multiPartData.append(img!, withName: key, fileName: "img_\(name).jpeg", mimeType: "image/jpeg")
} else if value is NSArray {
for childValue in value as? NSArray ?? [] {
if childValue is UIImage {
let img = (childValue as? UIImage)?.jpegData(compressionQuality: 0.5)
multiPartData.append(img!, withName: key, fileName: "profile_image.png", mimeType: "image/jpeg")
}
}
} else if value is NSURL || value is URL {
do {
guard let pdf: URL = value as? URL else { return }
let name = pdf.lastPathComponent
let mimeType = pdf.mimeType()
let pdfData = try Data(contentsOf: pdf)
multiPartData.append(pdfData, withName: key, fileName: name, mimeType: mimeType)
} catch { print(error) }
} else {
if value is String || value is Int || value is Bool {
multiPartData.append("\(value)".data(using: .utf8)!, withName: key)
}
}
}
}, to: urlIs,method: .post, headers: headers).responseDecodable(of: FetchAPI<T>.self) { response in
// preetify data
if let data = response.data {
let str = data.prettyPrintedJSONString!
Logger.logResponse(ofAPI: "\(urlIs)", logType: .success, object: str)
}
// response result
switch response.result {
case .success(let data):
// check auth token Exp condition
if data.statusCode == 401 {
let appDelegate = AppDelegate.shared()
let rootVC = R.storyboard.auth.authNavigationController()!
appDelegate.window?.rootViewController = rootVC
return
}
// check flag status
if data.flag! { success(data.data!) } else { failure(data.message!) }
case .failure(let error):
failure("Something went wrong.")
Logger.log(logType: .error, object: error.localizedDescription)
}
}
} else { failure(ServerAPI.errorMessages.kNoInternetConnectionMessage) }
}
-> Usage
APIManager.shared.makeFormDataAPICall(url: ServerAPI.editProfile, params: params, objectType: User.self, success: { response in
success(response)
print(response)
}, failure: { error in
failure(error)
}) // 1st Parameter is URL 2nd Parameter are params to send
//3rd parameter is Object to which you want to convert the Json data into
//4th and 5th parameter are compilation handler which gives you response and errors accordingly
-> you can remove auth token conditions which I have written for myself
-> if you are passing data in json you just have to write JSONEncoding: JSONEncoding.default before "header parameter" in almofire function
I have an NetworkRequest class, where all my alamofire requests made:
class NetworkRequest {
static let request = NetworkRequest()
var currentRequest: Alamofire.Request?
let dataManager = DataManager()
let networkManager = NetworkReachabilityManager()
let priority = DISPATCH_QUEUE_PRIORITY_DEFAULT
func downloadData<T: Film>(slug: String, provider: String, section: String, dynamic: String, anyClass: T, completion: ([T]?) -> Void ) {
var token: String = ""
if LOGGED_IN == true {
token = "\(NSUserDefaults.standardUserDefaults().valueForKey(TOKEN)!)"
}
let headers = [
"Access": "application/json",
"Authorization": "Bearer \(token)"
]
let dataUrl = "\(BASE_URL)\(slug)\(provider)\(section)\(dynamic)"
print(headers)
print(dataUrl)
if networkManager!.isReachable {
currentRequest?.cancel()
dispatch_async(dispatch_get_global_queue(priority, 0)) {
if let url = NSURL(string: dataUrl) {
let request = Alamofire.request(.GET, url, headers: headers)
request.validate().responseJSON { response in
switch response.result {
case .Success:
if let data = response.result.value as! [String: AnyObject]! {
let receivedData = self.dataManager.parseDataToFilms(data, someClass: anyClass)
completion(receivedData)
}
case .Failure(let error):
print("Alamofire error: \(error)")
if error.code == 1001 {
self.goToNoConnectionVC()
}
print("canceled")
}
}
}
}
} else {
goToNoConnectionVC()
}
}
}
And I need to cancel previous downloadData request, when the new one starts, tried to cancel using currentRequest?.cancel(), but it doesn't help.
Already tried to cancelOperations using NSOperationsBlock, but it doesn't cancels current operation.
I block UI now, so that user can't send another request. But this is not correct, causes some errors later...
Pls, help
Now on Alamofire 4 the Alamofire.Manager.sharedInstance.session is not available you should use this solution:
let sessionManager = Alamofire.SessionManager.default
sessionManager.session.getTasksWithCompletionHandler { dataTasks, uploadTasks, downloadTasks in
dataTasks.forEach { $0.cancel() }
uploadTasks.forEach { $0.cancel() }
downloadTasks.forEach { $0.cancel() }
}
and if you want to cancel (suspend, resume) a particular request you can check the request url in your .forEach block like this:
dataTasks.forEach {
if ($0.originalRequest?.url?.absoluteString == url) {
$0.cancel()
}
}
Found needed solution:
func stopAllSessions() {
Alamofire.Manager.sharedInstance.session.getAllTasksWithCompletionHandler { tasks in
tasks.forEach { $0.cancel() }
}
}
Update for Alamofire 5
func stopAllSessions() {
AF.session.getTasksWithCompletionHandler { (sessionDataTask, uploadData, downloadData) in
sessionDataTask.forEach { $0.cancel() }
uploadData.forEach { $0.cancel() }
downloadData.forEach { $0.cancel() }
}
}
If you want to cancel the request, you need to trace the requests made and try to cancel it. You can store it in an array and cancel every previous request stored.
In your code you create a request:
let request = Alamofire.request(.GET, url, headers: headers)
but you try to cancel the currentRequest?.cancel() that is never valued.
Swift 5
To cancel all requests use
Alamofire.Session.default.session.getTasksWithCompletionHandler({ dataTasks, uploadTasks, downloadTasks in
dataTasks.forEach { $0.cancel() }
uploadTasks.forEach { $0.cancel() }
downloadTasks.forEach { $0.cancel() }
})
To cancel a request with a particular url use
Alamofire.Session.default.session.getTasksWithCompletionHandler({ dataTasks, uploadTasks, downloadTasks in
dataTasks.forEach {
if ($0.originalRequest?.url?.absoluteString == "www.google.com") {
$0.cancel()
}
}
uploadTasks.forEach {
if ($0.originalRequest?.url?.absoluteString == "www.google.com") {
$0.cancel()
}
}
downloadTasks.forEach {
if ($0.originalRequest?.url?.absoluteString == "www.google.com") {
$0.cancel()
}
}
})
How can i upload a file with the alamofire Router? Struggling with it.
I'm also interested how i can upload file and parameters in the same request with Alamofire router.
Thanks!
I upload an image using the following code. For a given file type, you should be able to adjust the multipartEncoding block accordingly.
let json: [String : Any] = ["key", "value"]
let image = UIImage(named: "Something")!
guard let imageData = UIImagePNGRepresentation(image) else {
// We couldn't convert the image into data :(
}
let multipartEncoding: (MultipartFormData) -> Void = { multipartFormData in
multipartFormData.append(imageData, withName: "image", fileName: "image.png", mimeType: "image/png")
}
let request = Router.uploadPicture(parameters: json) // This is just a plain POST
sessionManager.upload(
multipartFormData: multipartEncoding,
with: request,
encodingCompletion: { (encodingResult) in
switch encodingResult {
case .success(let request, _, _):
request.validate().responseJSON(completionHandler: { (dataResponse) in
if let error = dataResponse.result.error {
// Network request failed :(
}
// SUCCESS!
})
case .failure(_):
// Couldn't encode the image :(
}
})
This is assuming your Router looks something like this.
enum Router: URLRequestConvertible {
case uploadPicture(parameters: Parameters)
static var baseUrl = "https://website.com/"
var method: HTTPMethod {
switch self {
case .uploadPicture(_):
return .post
}
}
var path: String {
switch self {
case .uploadPicture(_):
return "api/upload-picture/"
}
}
// MARK: - URLRequestConvertible
func asURLRequest() throws -> URLRequest {
let url = try Router.baseUrl.asURL().appendingPathComponent(path)
var urlRequest = try URLRequest(url: url, method: method)
var encodedUrlRequest: URLRequest?
switch self {
case .uploadPicture(let parameters):
encodedUrlRequest = try? JSONEncoding.default.encode(urlRequest, with: parameters)
}
return encodedUrlRequest ?? urlRequest
}
}