Alamofire Upload get returned JSON - swift

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?
}

Related

Could not cast value of type '__NSDictionaryI' to 'NSData'

I'm trying to decode the following data:
AF.upload(postData!, to: loginUrlString, headers: postmanManager.headers).responseJSON { response in
switch response.result {
case .success(let value):
print(value)
do {
let results = try JSONDecoder().decode(User.self, from: value as! Data)
DispatchQueue.main.async {
let id = results.Usuario[0].Id
let name = results.Usuario[0].Name
let userInfo = UserModel(Id: id, firstName: name)
print(userInfo)
}
} catch {
print(error)
}
case .failure(let error):
print(error)
}
}
The 'User' object belongs to the following struct:
struct User: Codable {
let Usuario: [UserData]
}
struct UserData: Codable {
let Id: Int
let Name: String
}
I tried looking up the answer and from what I understood, I tried changing value as! Data to value as! [String: Any], but I get another error:
Cannot convert value of type '[String : Any]' to expected argument type 'Data'
What else could I try changing? Thanks for the help!
You are using the wrong response type.
Replace
.responseJSON
with
.responseData
And please name functions and variables with starting lowercase letter.

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

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)")
}

Parse responseJSON to ObjectMapper

I'm currently making a migration from Android to iOS, better said Java to Swift, I got a generic response in JSON, but I'm not able to use it as an object and show it in the storyboard. I'm really new to Swift so I've been stuck for a while.
I've tried ObjectMapper and also JSON decode with no result at all.
I declared this response as I used in Java(Android)
class ResponseObjectMapper<T,R>: Mappable where T: Mappable,R:Mappable{
var data:T?
var message:String!
var error:R?
required init?(_ map: Map) {
self.mapping(map: map)
}
func mapping(map: Map) {
data <- map["data"]
message <- map["message"]
error <- map["error"]
}
}
class UserMapper :Mappable{
var email:String?
var fullName:String?
var id:CLong?
var phoneNumber:String?
var token:CLong?
required init?(_ map: Map) {
}
func mapping(map: Map) {
email <- map["email"]
fullName <- map["fullName"]
id <- map["id"]
phoneNumber <- map["phoneNumber"]
token <- map["token"]
phoneNumber <- map["phoneNumber"]
}
}
In my Android project I use the Gson dependency and I was able to use my JSON as an object
class ErrorMapper:Mappable{
var message:String?
var code:Int?
required init?(_ map: Map) {
}
func mapping(map: Map) {
message <- map["message"]
code <- map["code"]
}
}
This is the Alamofire that gave me the JSON.
func login(params: [String:Any]){Alamofire.request
("http://192.168.0.192:8081/SpringBoot/user/login", method: .post,
parameters: params,encoding: JSONEncoding.default, headers:
headers).responseJSON {
response in
switch response.result {
case .success:
let response = Mapper<ResponseObjectMapper<UserMapper,ErrorMapper>>.map(JSONString: response.data)
break
case .failure(let error):
print(error)
}
}
}
If I print the response with print(response) I got
SUCCESS: {
data = {
email = "vpozo#montran.com";
fullName = "Victor Pozo";
id = 6;
phoneNumber = 099963212;
token = 6;
};
error = "<null>";
message = SUCCESS;
}
and if I use this code I can got a result with key and value but I don't know how to use it as an object
if let result = response.result.value {
let responseDict = result as! [String : Any]
print(responseDict["data"])
}
console:
Optional({
email = "vpozo#gmail.com";
fullName = "Victor Pozo";
id = 6;
phoneNumber = 099963212;
token = 6;
})
I would like to use it in an Object, like user.token in a View Controller, probably I'm really confused, trying to map with generic attributes.
Type 'ResponseObjectMapper<UserMapper, ErrorMapper>' does not conform to protocol 'BaseMappable'
First of all you will need a Network Manager which uses Alamofire to make all your requests. I have made generalized one that looks something like this. You can modify it as you want.
import Foundation
import Alamofire
import SwiftyJSON
class NetworkHandler: NSObject {
let publicURLHeaders : HTTPHeaders = [
"Content-type" : "application/json"
]
let privateURLHeaders : HTTPHeaders = [
"Content-type" : "application/json",
"Authorization" : ""
]
enum RequestType {
case publicURL
case privateURL
}
func createNetworkRequestWithJSON(urlString : String , prametres : [String : Any], type : RequestType, completion:#escaping(JSON) -> Void) {
let internetIsReachable = NetworkReachabilityManager()?.isReachable ?? false
if !internetIsReachable {
AlertViewManager.sharedInstance.showAlertFromWindow(title: "", message: "No internet connectivity.")
} else {
switch type {
case .publicURL :
commonRequest(urlString: baseURL+urlString, parameters: prametres, completion: completion, headers: publicURLHeaders)
break
case .privateURL:
commonRequest(urlString: baseURL+urlString, parameters: prametres, completion: completion, headers: privateURLHeaders)
break
}
}
}
func commonRequest(urlString : String, parameters : [String : Any], completion : #escaping (JSON) -> Void , headers : HTTPHeaders){
print("urlString:"+urlString)
print("headers:")
print(headers)
print("parameters:")
print(parameters)
let url = NSURL(string: urlString)
var request = URLRequest(url: url! as URL)
request.httpMethod = "POST"
request.httpHeaders = headers
request.timeoutInterval = 10
let data = try! JSONSerialization.data(withJSONObject: parameters, options: JSONSerialization.WritingOptions.prettyPrinted)
let json = NSString(data: data, encoding: String.Encoding.utf8.rawValue)
if let json = json {
print("parameters:")
print(json)
}
request.httpBody = json!.data(using: String.Encoding.utf8.rawValue)
let alamoRequest = AF.request(request as URLRequestConvertible)
alamoRequest.validate(statusCode: 200..<300)
alamoRequest.responseJSON{ response in
print(response.response?.statusCode as Any )
if let status = response.response?.statusCode {
switch(status){
case 201:
print("example success")
SwiftLoader.hide()
case 200 :
if let json = response.value {
let jsonObject = JSON(json)
completion(jsonObject)
}
default:
SwiftLoader.hide()
print("error with response status: \(status)")
}
}else{
let jsonObject = JSON()
completion(jsonObject)
SwiftLoader.hide()
}
}
}
}
After this when ever you need to make a request you can use this function. This will take in parameters if any needed and once the request is complete it will execute a call back function in which you can handle the response. The response here will be of SWIFTYJSON format.
func makeNetworkRequest(){
let networkHandler = NetworkHandler()
var parameters : [String:String] = [:]
parameters["email"] = usernameTextField.text
parameters["pass"] = passwordTextField.text
networkHandler.createNetworkRequestWithJSON(urlString: "http://192.168.0.192:8081/SpringBoot/user/login", prametres: parameters, type: .publicURL, completion: self.handleResponseForRequest)
}
func handleResponseForRequest(response: JSON){
if let message = response["message"].string{
if message == "SUCCESS"{
if let email = response["data"]["email"].string{
//Do something with email.
}
if let fullName = response["data"]["fullName"].string{
//Do something with fullName.
}
if let id = response["data"]["id"].int{
//Do something with id.
}
if let phoneNumber = response["data"]["phoneNumber"].int64{
//Do something with phoneNumber.
}
if let token = response["data"]["token"].int{
//Do something with token.
}
}else{
//Error
}
}
}
Hope this helps. Let me know if you get stuck anywhere.

Alamofire convert bool on int

I have func for request and have dictionary for params. Dictionary contained [String, Any]. And i need send Bool in parameters but alamofire convert my Bool on Int and server can't get it. How i can send Bool?
my func:
func postDataFor(methodPath: String, params:[String:Any], completion: #escaping (Any)->()) {
guard let url = URL(string:baseURL+methodPath) else {print("fail url"); return}
let token_type = KeychainWrapper.standard.string(forKey: "token_type")
let access_token = KeychainWrapper.standard.string(forKey: "access_token")
let headers:HTTPHeaders? = ["Authorization": "\(token_type ?? "") \(access_token ?? "")"]
Alamofire.request(url, method: .post, parameters: params, encoding: URLEncoding.queryString, headers: headers).response { response in
if let data = response.data {
completion(data)
} else {
print("error")
}
}
}
This i try send:
▿ 2 elements
▿ 0 : 2 elements
- key : "promoId"
- value : 6
▿ 1 : 2 elements
- key : "isFavorite"
- value : true
But this send alamofire:
?isFavorite=1&promoId=6
I guess all these values are stored in params. If so, change isFavorite to a string:
let params = [
"promoId": 6,
"isFavorite": String(isFavorite)
]

how to deserialize a collection from an alamofire request

this is a pretty basic question of the syntax for deserializing responsejson collection in alamofire and swift
I have a GET service that returns an array of user objects e.g.
[
{"uname": "bob"},
{"uname": "jane"}
]
and here is my request
Alamofire.request(.GET, url , encoding:.JSON)
.responseJSON(options: .MutableContainers, completionHandler:{ (request, response, JSON, error) -> Void in
let result = JSON as? [Dictionary<String,String>]
if (response!.statusCode == 200 && error == nil){
//how should I deserialize the result here to return [String]
callback(success: true, errorMsg: nil, friends:friends)
}
else{
callback(success: false,errorMsg:error?.localizedDescription,friends:nil)
}
})
My question is how to deserialize the result, and am I correct to assume that the json result is a [Dictionary] and in order for me to change it to a [String] result, should I map it?
If I use the map syntax below I have a few questions
let friends = result?.map {
return $0["uname"] as String?
}
With the optional return value can I get it to return a [String] instead of a [String?] in a graceful way - isn't there a way to let map return only non null values somehow in a concise syntax?
Also is there a better syntax for map that lets me name the parameters instead of using $0?
I use Alamofire with SwiftyJSON to do the job and deserialise array of JSON objects to native objects. Here is an example of how I do it:
Alamofire.request(request)
.responseJSON(options: nil) { (request, response, json, error) -> Void in
if let requestError = error as NSError?{
callback!(nil, nil, requestError)
}
if let httpResponse = response as NSHTTPURLResponse?{
if(httpResponse.statusCode != 200){
callback!(nil, httpResponse, nil)
} else{
let jsonResult = JSON(json!) as JSON
var concerts = self.deserializeConcerts(jsonResult)
callback!(concerts, nil, nil)
}
}
}
Deserialize concerts looks like this:
class private func deserializeConcerts(concerts: SwiftyJSON.JSON) -> [ConcertModel]{
let concertsArray = concerts.object as! NSMutableArray
var concertObjs = [ConcertModel]()
for concert in concertsArray{
let concertObj = ConcertModel(concert: concert as! NSDictionary)
concertObjs.append(concertObj)
}
return concertObjs
}
And finally, ConcertModel initialiser just maps the values of the NSDictionary to the properties of the ConcertModel object.
Updated implementation for Alamofire 4.3
Alamofire.request(url, method: .get)
.responseJSON { response in
if response.data != nil {
let json = JSON(data: response.data!)
let name = json["data"][0]["name"].string
if name != nil {
print(name!)
}
}
}
That will parse this JSON input:
{
data: [
{ name: 'John' },
{ name: 'Dave' }
]
}