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
}
}
Related
This is my function for verifying an OTP specific Payfort with value, but display (invalid value OTP), but in response in postman request display (success OTP) why this execute, and the problem in the OTP value must be alphanumeric, so how can I define an alphanumeric parameter?
func verifyOTP(getTotal: String, getRandom: String,getTransaction: String) {
let signatureStructure = "sportEyeByAbouElgoukhaccess_code=hRRVGXrIpHSYoH19Ebwtamount=\(getTotal)currency=EGPlanguage=enmerchant_identifier=e54638ebmerchant_order_id=\(getRandom)merchant_reference=\(getRandom)otp=\(123456)payment_option=VALUphone_number=01008606003service_command=OTP_VERIFYtotal_downpayment=0sportEyeByAbouElgoukh"
let base64Str = signatureStructure.sha256()
let parameters = ["otp":123456,"currency":"EGP","merchant_order_id":getRandom,"signature":base64Str,"merchant_reference":getRandom,"payment_option":"VALU","merchant_identifier":"e54638eb","amount":getTotal,"access_code":"hRRVGXrIpHSYoH19Ebwt","total_downpayment":"0","service_command":"OTP_VERIFY","phone_number":"01008606003","language":"en"] as [String : Any]
Alamofire.request(URLAPi.URL_Payment_Api , method : .post , parameters : parameters , encoding: JSONEncoding.default ).responseJSON { (response) in
if response.result.isSuccess {
let jsonpayfortrequest : JSON = JSON(response.result.value!)
SVProgressHUD.dismiss()
}
else {
print("error connection")
SVProgressHUD.dismiss()
}
}
}
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.
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)
]
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
}
}
I work with iOS app that use Alamofire, I want to write a generic function(s) which used to send and retrieve data from server to a decodable objects, my function was as below :
func pop <T : Codable> (_ Url: inout String, _ popedList: inout [T]) {
let url = URL(string:Url)
Alamofire.request(url!, method: .post).responseJSON { response in
let result = response.data
do {
let data = try JSONDecoder().decode(popedList, from: result!)// get error here
print(data[0])
let jsonEncoder = JSONEncoder()
let jsonData = try! jsonEncoder.encode(data[0])
let jsonString = String(data: jsonData, encoding: .utf8)
print("jsonString: \(String(describing: jsonString))")
} catch let e as NSError {
print("error : \(e)")
}
}
}
and a function to send an object to server as below:
func push <T : Codable> (_ Url: inout String, _ pushObject: inout T) {
let jsonData = try! JSONEncoder().encode(pushObject)
let jsonString = String(data: jsonData, encoding: .utf8)
print("jsonString: \(String(describing: jsonString))")
let url = URL(string:Url)
Alamofire.request(url!,
method: .post,
parameters:jsonString)//it's need to creat a Dictionary instate of String
.validate(statusCode: 200..<300)
.validate(contentType: ["application/json"])
.response { response in
// response handling code
let result = response.data
print(response.data)
}
}
I get an error in first function,
"Cannot invoke 'decode' with an argument list of type '([T], from: Data)'"
and
"Escaping closures can only capture inout parameters explicitly by value"
What is the best way to write these to function as generic?
After a few searches and trying to edit my functions I capable to rewrite my two functions in such away that I get what I need:
func pop<T: Decodable>(from: URL, decodable: T.Type, completion:#escaping (_ details: [T]) -> Void)
{
Alamofire.request(from, method: .post).responseJSON { response in
let result_ = response.data
do {
let data = try JSONDecoder().decode([T].self, from: result_!)
//let data = try JSONDecoder().decode(decodable, from: result_!)// get error here
//print(data[0])
print("data[0] : \(data[0])")
completion(data)
} catch let e as NSError {
print("error : \(e)")
}
}
}
func push <T : Codable> (_ Url: String, _ pushObject: T)
{
let jsonData = try! JSONEncoder().encode(pushObject)
let jsonString = String(data: jsonData, encoding: .utf8)
print("jsonString: \(String(describing: jsonString))")
let url = URL(string:Url)
Alamofire.request(url!,
method: .post,
parameters:convertToDictionary(text: jsonString!))//it's need to creat a Dictionary instate of String
.validate(statusCode: 200..<300)
.validate(contentType: ["application/json"])
.response { response in
// response handling code
print(response.data!)
if let jsonData = response.data {
let jsonString = String(data: jsonData, encoding: .utf8)
print("response.data: \(String(describing: jsonString))")
}
}
}
func convertToDictionary(text: String) -> [String: Any]? {
if let data = text.data(using: .utf8) {
do {
return try JSONSerialization.jsonObject(with: data, options: []) as? [String: Any]
} catch {
print(error.localizedDescription)
}
}
return nil
}
For the first function, JSONDecoder.decode() wants 2 parameters:
Type to decode to: the class/struct you want it to decode to. This is not an instantiated object, just the type.
Data to decode from: the generic Data object that will be converted to the type you specified.
So, in order to be able to write your function so that it has a generic URL and result object, you would need to pass it the object type and a callback to pass the result to, since network operations are asynchronous.
func dec<T: Decodable>(from: URL, decodable: T.Type, result: (T) -> Void) {
// your Alamofire logic
let data = try JSONDecoder().decode(popedList, from: result!)
result(data)
}
You can apply the same logic to the second function.
Note that this is not the best way to take care of eventual errors, just an example of how you can handle encoding with a generic function.
JSONDecoder().decode method takes type and data parameter. Pass type not popedList.
let data = try JSONDecoder().decode([T].self, from: result!)
Inout Paramaters
Function parameters are constants by default. Trying to change the value of a function parameter from within the body of that function results in a compile-time error. This means that you can’t change the value of a parameter by mistake. If you want a function to modify a parameter’s value, and you want those changes to persist after the function call has ended, define that parameter as an in-out parameter instead.
You are not changing value of popedList in both functions, so using inout is meaningless.