array as params is not working with Alamofire - swift

I have an API where I need to send data as object so there I pass data as below and it works fine.
["fname" : "First 1", "lname": "Last 1"]
But for one of the API, web developer need API as Array as below.
[["fname" : "First 1", "lname": "Last 1"]]
Any idea what is going wrong?
Below is the code I have
parameters = ..... data that I passed as [String : Any] // e.x. ["fname" : "First 1", "lname": "Last 1"]
var finalWebParams : Any
var webParams2 : [[String : Any]] = [[String : Any]]()
if (webserviceFor=="array") {
webParams = parameters as [String:Any]
webParams2.append(webParams)
}
if (webserviceFor=="array") {
finalWebParams = webParams2
} else {
finalWebParams = webParams
}
print("finalWebParams==\(finalWebParams)")
request(url, method: webMethod, parameters: finalWebParams as? Parameters, encoding: myEncoding, headers: headers)
For print I get result as below, means I am passing correct data however I get 500 error.
[["fname" : "First 1", "lname": "Last 1"]]
Any idea what I am doing wrong?
Edit 1
below is the model that web developer need
[
{
"fname" : "First 1",
"lname" : "Last 1"
}
]

The answer is I need to add ArrayEncoding as below.
var finalWebParams : [String : Any]
var webParams2 : [[String : Any]] = [[String : Any]]()
if (webserviceFor=="array") {
webParams = parameters as [String:Any]
webParams2.append(webParams)
}
if (webserviceFor=="array") {
finalWebParams = webParams2.asParameters()
} else {
finalWebParams = webParams
}
Now add extension
private let arrayParametersKey = "arrayParametersKey"
/// Extenstion that allows an array be sent as a request parameters
extension Array {
/// Convert the receiver array to a `Parameters` object.
func asParameters() -> Parameters {
return [arrayParametersKey: self]
}
}
/// Convert the parameters into a json array, and it is added as the request body.
/// The array must be sent as parameters using its `asParameters` method.
struct ArrayEncoding: ParameterEncoding {
/// The options for writing the parameters as JSON data.
public let options: JSONSerialization.WritingOptions
/// Creates a new instance of the encoding using the given options
///
/// - parameter options: The options used to encode the json. Default is `[]`
///
/// - returns: The new instance
public init(options: JSONSerialization.WritingOptions = []) {
self.options = options
}
public func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
var urlRequest = try urlRequest.asURLRequest()
guard let parameters = parameters,
let array = parameters[arrayParametersKey] else {
return urlRequest
}
do {
let data = try JSONSerialization.data(withJSONObject: array, options: options)
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
}
urlRequest.httpBody = data
} catch {
throw AFError.parameterEncodingFailed(reason: .jsonEncodingFailed(error: error))
}
return urlRequest
}
}

Related

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 filter multiple values from JSON and append the results to the TableView Swift4

I am trying to find a solution of how to filter multiple values from a local JSON file at the same time. The values come from UserDefaults (I have a viewController that has some buttons inside, when the button is in an active state, it saves a value to UserDefaults. Which basically means user's interest). Possibly there can be more than one interest. The JSON looks like this:
{
"article" : [
{
"title" : "Basketball",
"subtitle" : "NBA",
"interest" : "sports"
},
{
"title" : "Africa",
"subtitle" : "Sahara",
"interest" : "travel"
},
{
"title" : "Space",
"subtitle" : "Satelitte",
"interest" : "science"
}
]
}
I need to filter the "interest" key and if it matches the values that are being filtered (comes from UserDefaults), then append those articles to the tableView. This should be done before the tableview loads. The variables from userDefaults:
let sportsInterest = UserDefaults.standard.object(forKey: "Sports") as! String // contains "sports"
let travelInterest = UserDefaults.standard.object(forKey: "Travel") as! String // contains "travel"
The model looks like this:
class Articles: Codable {
let article: [Article]
init(article: [Article]) {
self.article = article
}
}
class Article: Codable{
let title: String
let subtitle: String
let interest: String
init(title: String, subtitle: String, interest: String) {
self.title = title
self.subtitle = subtitle
self.interest = interest
}
}
And I am parsing JSON this way:
private var articles = [Article]()
var fileName: String = "Tasks"
func downloadJSON() {
let url = Bundle.main.url(forResource: fileName, withExtension: "json")!
do {
let data = try Data(contentsOf: url)
let articleList = try JSONDecoder().decode(Articles.self, from: data)
self.articles = articleList.article
DispatchQueue.main.async {
self.articlesTV.reloadData()
}
} catch {
print("Error occured during Parsing", error)
}
}
filter is the right keyword.
let sportsArticles = articles.filter { $0.interest == sportsInterest }
or for more criteria
let criteria = [travelInterest, sportsInterest]
let filteredArticles = articles.filter { criteria.contains($0.interest) }
If the articles are supposed to be filtered right after loading the data write
let articleList = try JSONDecoder().decode(Articles.self, from: data)
self.articles = articleList.article.filter { criteria.contains($0.interest) }
It might be unknown but the initializers in the classes are redundant if the class adopts Codable 😉.

`Extra argument method in call` in Alamofire

I am trying to send the following data using Alamofire version 3 and Swift 3. And the error that I get is Extra argument method in call.
Here is what I have done so far:
struct userGoalServerConnection {
let WeightsUserGoal_POST = "http://api.umchtech.com:3000/AddWeightsGoal"
}
struct wieghtsUserGoal {
let id : Int
let token : String
let data : [ String : String ]
}
func syncInitialUserGaolValuesWithServer (userID : Int , userToken : String ){
let serverConnection = userGoalServerConnection()
let weightValue = wieghtsUserGoal.init(id: userID,
token: userToken,
data: ["weight_initial":"11","weight_end":"11","weight_difference":"11","calories_deficit":"11","difficulties":"11","weight_loss_week":"11","start_date":"2016-12-12","end_date":"2016-12-23","days_needed":"11"])
Alamofire.request(userGoalServerConnection.WeightsUserGoal_POST, method:.post, parameters: weightValue, encoding: JSONEncoding.default, headers:nil).responseJSON { response in
print(response.request as Any) // original URL request
print(response.response as Any) // URL response
print(response.result.value as Any) // result of response serialization
}
I am quite new to this so please dont mind if my question is a little bit too noob. I have gone through similar questions here but they did not help me out figuring where am i making a mistake :)
You are passing weightValue as POST param in request, it can't be. If you are using JSONEncoding then pass type of Parameters object.
let params: Parameters = [
"Key1": "value1",
"key2": "value2"
]
You can only pass dictionary type a alamofire request, you cannot post a struct type.
you can send [String : String], [String : Any] etc in a Alamofire request
You can try this way out
let WeightsUserGoal_POST = "http://api.umchtech.com:3000/AddWeightsGoal"
func Serialization(object: AnyObject) -> String{
do {
let stringData = try JSONSerialization.data(withJSONObject: object, options: [])
if let string = String(data: stringData, encoding: String.Encoding.utf8){
return string
}
}catch _ {
}
return "{\"element\":\"jsonError\"}"
}
func syncInitialUserGaolValuesWithServer (userID : Int , userToken : String ){
let data = ["weight_initial":"11","weight_end":"11","weight_difference":"11","calories_deficit":"11","difficulties":"11","weight_loss_week":"11","start_date":"2016-12-12","end_date":"2016-12-23","days_needed":"11"] as! [String : String]
let dataSerialized = Serialization(object: data as AnyObject)
let param = ["id" : userID,
"token" : userToken,
"data" : dataSerialized ]
Alamofire.request(WeightsUserGoal_POST, method: .post, parameters: param ,encoding: JSONEncoding.default, headers: nil).responseJSON { response in
print(response.request as Any) // original URL request
print(response.response as Any) // URL response
print(response.result.value as Any) // result of response serialization
}
}

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