Multipart form data upload with multiple images + parameters (Alamofire 5.2) - swift

I am trying to upload multiple images and parameters to the server using 'Alamofire 5.2)'
This is what the server expects
[{"key":"media1","description":"","type":"file","value":["/C:/Users/x/x/x/x.jpg"]},
[{"key":"media2","description":"","type":"file","value":["/C:/Users/x/x/x/x.jpg"]},
[{"key":"media3","description":"","type":"file","value":["/C:/Users/x/x/x/x.jpg"]},
{"key":"model","value":"{\"emp_Id\": \"6\",\"device_Identifier\":\"Device 01\",\"timestamp\":\"123\,”\description\”:\”description\",”handoverStatus”:”false”}","type":"text"}]
//Please note the array part
This is how i add picked image to an image array
var dictImage : [UIImage]?
if pickedImage.pngData() != nil
{
dictImage!.append(pickedImage)
}
This is how im trying to call the alamofire upload
func xyz()
{
let param : [String: Any] = [
"emp_Id" : "",
"device_Identifier" : "",
"timestamp" : "",
"description" : "",
"handoverStatus" : ""
]
let urlString = URL(string: APIDetails.BaseUrl + "api/123")!
let url = (try? URLRequest(url: urlString, method: .post, headers: APIDetails.header))!
AF.upload(multipartFormData: { (multipartData) in
let img1 = arrImage![0]
let img2 = arrImage![1]
let img3 = arrImage![2]
multipartData.append(img1.pngData(), withName:"media1", fileName: "\(Date().timeIntervalSince1970).jpg", mimeType: "file")
multipartData.append(img2.pngData(), withName:"media2", fileName: "\(Date().timeIntervalSince1970).jpg", mimeType: "file")
multipartData.append(img3.pngData(), withName:"media3", fileName: "\(Date().timeIntervalSince1970).jpg", mimeType: "file")
for (key, value) in param {
multipartData.append((value as AnyObject).data(using: String.Encoding.utf8.rawValue)!, withName: key)
}
}, with: url)
}
How do i upload every image and its details as a dictionary?
How do i add upload progress and result that shows if anything failed or not ?
I tried with
}, with: url,encodingCompletion:{
EncodingResult in
switch EncodingResult{
case .success(let upload, _, _):
upload.responseJSON { response in
debugPrint("SUCCESS RESPONSE: \(response)")
}
case .failure(let encodingError):
print("ERROR RESPONSE: \(encodingError)")
} })
But it gives error Extra argument 'encodingCompletion' in call
(This is my first time trying to upload multipart form data, the code/part of it was taken from another answer)

try this code, I have not tested in Alamofire(5.2)
let baseUrl = "your URL"
let fullUrl = baseUrl + strUrl
var headers : HTTPHeaders = HTTPHeaders(["Content-type" : "multipart/form-data"])
if let header = header{
headers = header
}
guard let url = try? URLRequest(url: fullUrl, method: .post, headers: headers) else {return}
AF.upload(multipartFormData: { (multipartData) in
for i in 0 ..< arrImage.count{
if let imageData = arrImage[i].pngData(){
let mediaName = "media\(i + 1)"
multipartData.append(imageData, withName:mediaName, fileName: "\(Date().timeIntervalSince1970).jpg", mimeType: "file")
}
}
for (key, value) in parameter {
multipartData.append((value as AnyObject).data(using: String.Encoding.utf8.rawValue)!, withName: key)
}
}, to: url).responseJSON(queue: .main, options: .allowFragments) { (response) in
switch response.result{
case .success(let value):
print("Json: \(value)")
case .failure(let error):
print("Error: \(error.localizedDescription)")
}
}.uploadProgress { (progress) in
print("Progress: \(progress.fractionCompleted)")
}

Related

Module 'Alamofire' has no member named 'upload' error after migrating

After migrating to 5.0 receive this error - > Module 'Alamofire' has no member named 'upload'
Changing Alamofire to AF doesn't help.
Can't run build in xcode due to this error.
Alamofire.upload(
multipartFormData: { (multipartFormData) in
formData.forEach({ (key, value) in
guard let valueData = "\(value)".data(using: .utf8) else {
return
}
multipartFormData.append(valueData, withName: key)
})
switch uploadOption {
case .data(let data, let meta):
multipartFormData.append(
data,
withName: "file",
fileName: meta.fileName,
mimeType: meta.mimeType
)
case .stream(let stream, let length, let meta):
multipartFormData.append(
stream,
withLength: length,
name: "file",
fileName: meta.fileName,
mimeType: meta.mimeType
)
}
},
to: url,
encodingCompletion: { (encodingResult) in
switch encodingResult {
case .failure(let error):
completion(.failure(error: error))
case .success(let request, _, _):
cancelable.request = request
request.responseData(completionHandler: { (response) in
if let error = response.error {
completion(.failure(error: error))
return
}
completion(.success)
})
}
})
I have Alamofire version 5.2, it seems ok,
some syntax changed after you migrate to 5.x,
plz check it or update it to 5.2

'Invalid type in JSON write (UIImage)' Alamofire API request

i am trying to send an image to an api using alamofire heres what i got:
var uploadedProfileImage: UIImage = UIImage()
let body: Parameters = [
"profilePic": uploadedProfileImage,
"name": "John Doe"
]
Alamofire.request(BASE_URL",method: .post,parameters: body,encoding: JSONEncoding.default).responseData { response in
debugPrint("All Response Info: \(response)")
if let data = response.result.value, let utf8Text = String(data: data, encoding: .utf8) {
print("Data: \(utf8Text)")
}
}
so this is the code i am using uploadProfileImage has an image that the user picked from the library and my api receives a json body parameter in which it has profilePic which is off type file when i run this its giving me an error saying 'Invalid type in JSON write (UIImage)'.theres is also a terminating with uncaught exception of type NSException error. what am i doing wrong and how to fix it?
You can't upload image with
"profilePic": uploadedProfileImage
as it's used for types that are encode-able such as strings , you need
upload image to server using Alamofire
func uploadImageAndData(_ url:String,parameters1:Parameters,headers1:HTTPHeaders,images:[UIImage]){
Alamofire.upload(multipartFormData: { multipartFormData in
// import image to request
var i=0
for imageData in images {
// multipartFormData.append(self.resizeImage(image: imageData, targetSize: CGSize(width: 400, height:
multipartFormData.append(imageData.pngData()!, withName: "image", fileName: "image"+".jpeg", mimeType: "image/jpeg")
i += 1
}
for (key, value) in parameters1 {
multipartFormData.append((value as AnyObject).data(using: String.Encoding.utf8.rawValue)!, withName: key)
}
} ,to: url,method:.post,
headers:[:],
encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.uploadProgress(closure: { (Progress) in
print("Upload Progress: \(Progress.fractionCompleted)")
//SwiftSpinner.show(progress: Progress.fractionCompleted*100, title: "تحميل")
})
upload.responseJSON { response in
switch response.response?.statusCode {
case 200 :
break
case 400 :
break
case 401 :
break
case 302 :
break
case 500 :
break
default: break
}
}
return
case .failure(let encodingError):
}
})
}
var head = [String:Any]()
head["Accept"]="application/json"
head["Authorization"] = "Bearer \(tokenIfExists)"

Alamofire request body nil when uploading image using multipartFormData

I seem to have a problem regarding image upload and passing parameters using Alamofire. I have a quite simple function for multipart data which looks like:
sessionManager.upload(multipartFormData: { (multipartFormData) in
for (key, value) in parameters {
print("\(value)")
multipartFormData.append("\(value)".data(using: String.Encoding.utf8)!, withName: key as String)
}
multipartFormData.append(UIImagePNGRepresentation(image)!, withName: "document", mimeType: "image/png")
}, to: baseURL + "/documents", encodingCompletion: { (result) in
switch result{
case .success(let upload, _, _):
upload.validate().responseJSON { response in
print("Succesfully uploaded")
}
case .failure(let error):
print("Error in upload: \(error.localizedDescription)")
}
})
I have OAuth2Handler which i've implemented for headers and authorisation and it works for all other requests. I have also tried to implement this without wrapper using Alamofire object directly but still no luck. When I inspect the request i notice that httpBody is always nil, which corresponds to error I get from the server and the message says that I do not pass required parameters.
This is Working for me Swift 4
func callPostApiImage(api:String, parameters:[String:AnyObject]?,image:UIImage,Name:String, mime_type:String = "image/jpg", complition:#escaping (AnyObject)->Void){
// Encode Data
let base64EncodedString = toBase64EncodedString(toJsonString(parameters: parameters!))
let File_name = "image_" + String(arc4random()) + ".jpg"
Alamofire.upload(multipartFormData: { (multipartFormData) in
multipartFormData.append(UIImageJPEGRepresentation(image, 0.5)!, withName: Name, fileName: File_name, mimeType: mime_type)
multipartFormData.append(base64EncodedString.data(using: String.Encoding.utf8)!, withName: "jsondata")
}, to:api){ (result) in
switch result {
case .success(let upload, _, _):
upload.uploadProgress(closure: { (progress) in
print(progress)
})
upload.responseJSON { response in
print(response.result)
}
case .failure(let encodingError):
print("",encodingError.localizedDescription)
break
}
}
}
// Base64EncodedString
func toBase64EncodedString(_ jsonString : String) -> String
{
let utf8str = jsonString.data(using: .utf8)
let base64Encoded = utf8str?.base64EncodedString(options: [])
return base64Encoded!
}

Upload file to mailgun server with swift

I am trying to attach file to Mailgun. Here is my curl command:
curl -s --user 'api:key-XXX'
https://api.mailgun.net/v3/sandbox---.mailgun.org/messages
-F from='Excited User<mailgun#sandbox---.mailgun.org>'
-F to=mail#gmail.com
-F subject='Hello'
-F text='Testing some Mailgun awesomness!'
-F attachment='#/Users/.../mytestfile.jpeg'
Result: Ok, the file has been attached to the message and been successfully transferred.
After that, I tried doing it with URLRequest:
guard let filePath = Bundle.main.url(forResource: "domru", withExtension: "jpeg") else {
print(">>>can't find path")
return
}
let parameters: HTTPHeaders = [
"from": "Excited User<mailgun#sandbox---.mailgun.org>",
"to": "mail#gmail.com",
"subject": "hello",
"text": "my text message" ]
let data = encodeRequest(parameters: parameters, attachment: filePath)
var request = URLRequest(url: URL(string:"https://api.mailgun.net/v3/sandbox---.mailgun.org/messages")!)
request.httpMethod = "POST"
request.httpBody = data.data(using: .utf8)
request.addValue("Basic \(credentialData!)", forHTTPHeaderField: "Authorization")
let task = URLSession.shared.dataTask(with: request, completionHandler:{(data, response, error) in
if let err = error {
print(err)
}
if let response = response {
print("url = \(response.url!)")
print("response = \(response)")
let httpResponse = response as! HTTPURLResponse
print("response code = \(httpResponse.statusCode)")
}
})
task.resume()
private func encodeRequest(parameters:[String:String], attachment: URL) -> String {
var result = ""
for (key, value) in parameters {
result.append("\(key)="+"\(value)".addingPercentEncoding(withAllowedCharacters: .urlHostAllowed)! + "&")
}
result.append("attachment="+attachment.path.addingPercentEncoding(withAllowedCharacters: .urlPathAllowed)!)
return result
}
Result: The text of the message has been delivered, but the file has not been attached.
I also tried to solve that with Alamofire:
let imageData = UIImageJPEGRepresentation(UIImage(named:"domru.jpeg")!, 1)
let header : HTTPHeaders = ["Authorization":"Basic \(credentialData!)"]
Alamofire.upload(
// parameters are the same as in the previous part of code
multipartFormData: { (multipartFormData) in
for (key, value) in parameters {
multipartFormData.append("\(value)".data(using: String.Encoding.utf8)!, withName: key)
}
multipartFormData.append(imageData!, withName: "domru", fileName: "domru.jpeg", mimeType: "image/jpeg")
},
to: "https://api.mailgun.net/v3/sandbox---.mailgun.org/messages",
method: .post,
headers: header,
encodingCompletion: { (result) in
debugPrint(result)
}
Result the same: The attachment has not been attached to the message.
How attach a file to a message in this case?
For anyone still working on this. It seems that the MailGun API looks for the name to be attachment on each of the attachments included so this line:
multipartFormData.append(imageData!, withName: "domru", fileName: "domru.jpeg", mimeType: "image/jpeg")
would need to be changed to:
multipartFormData.append(imageData!, withName: "attachment", fileName: "domru.jpeg", mimeType: "image/jpeg")
not sure if that entirely fixes it

Alamofire Upload get returned JSON

question on getting at the result data in an Alamofire.upload call.
I am sending multipart form data to a server using the following code:
sessionManager.upload(
multipartFormData: { multipartFormData in
if let name = currentPlace.name,
let data = name.data(using:String.Encoding.utf8) {
multipartFormData.append(data, withName: "placename", mimeType: "text/plain")
}
if let lat = currentPlace.location?.coordinate.latitude {
multipartFormData.append(String(describing: lat).data(using:String.Encoding.utf8)!, withName: "latitude", mimeType: "text/plain")
}
if let lon = currentPlace.location?.coordinate.longitude {
multipartFormData.append(String(describing: lon).data(using:String.Encoding.utf8)!, withName: "longitude", mimeType: "text/plain")
}
multipartFormData.append((User.current.scanMode.rawValue).data(using:String.Encoding.utf8)!, withName: "state", mimeType: "text/plain")
},
to: (url?.absoluteString)!, headers: headers,
encodingCompletion: { encodingResult in
switch encodingResult {
case .success(let upload, _, _):
upload.responseJSON { response in
print(response.request) // original URL request
print(response.response) // URL response
print(response.data) // server data
print(response.result) // result of response serialization
if let JSON = response.result.value as? NSArray {
for element in JSON {
print(element)
}
}
}
case .failure(let encodingError):
print(encodingError)
}
})
My challenge is to get the value of response.result.value in a form that I can validate. The current debug output is:
(lldb) po response.result.value
▿ Optional<Any>
▿ some : 4 elements
▿ 0 : 2 elements
- key : code
- value : 200
▿ 1 : 2 elements
- key : id
- value : 78230c53954a3adbf14b49cda127bf55
▿ 2 : 2 elements
- key : message
- value : Successfully updated OID to DISTRIBUTED.
▿ 3 : 2 elements
- key : state
- value : DISTRIBUTED
Looks like a array, but can't seem to get this casted or reflected into something I can use. Usually I would use EVOReflection to get at this data as in:
sessionManager.request((url?.absoluteString)!, method: .get, parameters: postParameters, encoding: URLEncoding.httpBody, headers: headers)
.responseObject { (response: DataResponse<AssetStateResponse>) in
if let response = response.result.value {
completionHandler(response)
} else {
// LATER: Better error handling
}
}
Any help appreciated, have been looking at this for a few days now, missing something simple I suspect.
That respose.result.value looks like a dict, not an array.
Did you try casting it that way?
if let jsonDict = response.result.value as? NSDictionary {
print(jsonDict)
}
Alamofire response will result in a dictionary.
So your response.result.value is a dictionary
print(response.result) After this line call a function funcJSONParsing
self.funcJSONParsing(response.result.value!)
Write the parsing in a separate function like below.
func funcJSONParsing(let json:AnyObject)
{
if json is NSDictionary{
let jsonResponse = json as! NSDictionary
//Parse the below jsonResponse according to your needs
}
}
upload.responseJSON { (result) in
do {
let decoder = JSONDecoder()
let model = try decoder.decode(FormResponse2.self,from: result.data!)
let selectDoctorForChat = self.storyboard?.instantiateViewController(withIdentifier: "SelectDoctorForChat") as! SelectDoctorForChat
selectDoctorForChat.chatFormId = model.chat?.id
self.navigationController?.pushViewController(selectDoctorForChat, animated: true)
} catch let parsingError {
print("Error", parsingError)
}
// MARK: - FormResponse
struct FormResponse2: Codable {
let chat: Chat2?
}
// MARK: - Chat
struct Chat2: Codable {
let id: Int?
let patient, doctor: String?
let specialty: Specialty22?
let state, createdAt: String?
enum CodingKeys: String, CodingKey {
case id, patient, doctor, specialty, state
case createdAt = "created_at"
}
}
// MARK: - Specialty
struct Specialty22: Codable {
let id: Int?
let name: String?
}