I have a data structure that looks like this in JSON:
[{
"value": "1",
"optionId": "be69fa23-6eca-4e1b-8c78-c01daaa43c88"
}, {
"value": "0",
"optionId": "f75da6a9-a34c-4ff6-8070-0d27792073df"
}]
Basically it is an array of dictionaries. I would prefer to use the default Alamofire methods and would not like to build the request manually. Is there a way to give Alamofire my parameters and Alamofire does the rest?
If I create everything by hand I get an error from the server that the send data would not be correct.
var parameters = [[String:AnyObject]]()
for votingOption in votingOptions{
let type = votingOption.votingHeaders.first?.type
let parameter = ["optionId":votingOption.optionID,
"value": votingOption.votingBoolValue
]
parameters.append(parameter)
}
let jsonData = try! NSJSONSerialization.dataWithJSONObject(parameters, options: [])
let json = try! NSJSONSerialization.JSONObjectWithData(jsonData, options: .AllowFragments)
if let url = NSURL(string:"myprivateurl"){
let request = NSMutableURLRequest(URL: url)
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.HTTPMethod = Method.POST.rawValue
request.HTTPBody = try! NSJSONSerialization.dataWithJSONObject(parameters, options: [])
AlamofireManager.Configured.request(request)
.responseJSON { response in
//Handle result
}
}
I have the same issue and resolved this way:
I created a new struct implementing the Alamofire's ParameterEncoding protocol:
struct JSONArrayEncoding: ParameterEncoding {
private let array: [Parameters]
init(array: [Parameters]) {
self.array = array
}
func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
var urlRequest = try urlRequest.asURLRequest()
let data = try JSONSerialization.data(withJSONObject: array, options: [])
if urlRequest.value(forHTTPHeaderField: "Content-Type") == nil {
urlRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
}
urlRequest.httpBody = data
return urlRequest
}
}
Then, I can do this:
let parameters : [Parameters] = bodies.map( { $0.bodyDictionary() })
Alamofire.request(url, method: .post, encoding: JSONArrayEncoding(array: parameters), headers: headers).responseArray { ... }
It worked for me. Hope can help someone else.
You can do something like this:
Alamofire.request(.POST, urlPath, parameters: params).responseJSON{ request, response, data in
//YOUR_CODE
}
Where parameters is [String:AnyObject] and yes Alamofire takes care of the rest.
Since it looks like you are using a manager you can do this
YOUR_ALAMOFIRE_MANAGER.request(.POST, url, parameters: params).responseJSON{ request, response, JSON in
//enter code here
}
Here is the source code:
public func request(
method: Method,
_ URLString: URLStringConvertible,
parameters: [String: AnyObject]? = nil,
encoding: ParameterEncoding = .URL,
headers: [String: String]? = nil)
-> Request
{
let mutableURLRequest = URLRequest(method, URLString, headers: headers)
let encodedURLRequest = encoding.encode(mutableURLRequest, parameters: parameters).0
return request(encodedURLRequest)
}
EDIT:
Since your data is currently [[String:AnyObject]] you will need to modify it so it is in the form [String:AnyObject]. One way you could do this i by doing this ["data":[[String:AnyObject]]]. You will probably have to change your api end point though.
You can provide parameter encoding for JSON POST request and it will send the data as JSON in request body.
Alamofire.request(.POST, "https://httpbin.org/post", parameters: parameters, encoding: .JSON)
This is described in the ReadMe file of Alamofire github - https://github.com/Alamofire/Alamofire#post-request-with-json-encoded-parameters
let parameters = [
"foo": [1,2,3],
"bar": [
"baz": "qux"
]
]
Alamofire.request(.POST, "https://httpbin.org/post", parameters: parameters, encoding: .JSON)
// HTTP body: {"foo": [1, 2, 3], "bar": {"baz": "qux"}}
Related
FIGURED IT OUT, See below:
I'm trying to create a program that can pass the contents of a text file to a http POST request in swift. I am storing filters for API queries that I am running in the text files, and would like to pass them as JSON objects(? I think, anyway) to request.httpBody in the request. I'm having problems converting the txt files to something that the httpBody can accept as data (json object?).
Here an example txt file. Filters in the same array are combined using OR logic. Arrays of filters are combined using AND logic, so I have to account for both cases.:
zero_RC.txt
{
"query": "Zero Response Code",
"filters": [
{
"filters": [
{
"field": "inventoryState",
"value": "CONFIRMED",
"type": "IN"
},
{
"field": "responseCode",
"value": "0",
"type": "EQ"
},
{
"field": "exception",
"value": "DNS lookup failed",
"type": "EQ"
}]
}]
}
This is the block that I'm trying to get to work. I believe that I need a JSON object, and can pass that to httpBody in the request below. But, still a beginner at this stuff.
// get JSON, somehow
let file = Bundle.main.path(forResource: "zero_RC", ofType: "txt")
let jsonData = file!.data(using: .utf8)
let JSON = try! JSONSerialization.data(withJSONObject: jsonData as Any, options: [])
if JSONSerialization.isValidJSONObject(JSON) {
print("Oh Yeah")
} else {
print("Nah bud, that ain't working")
}
// make the request
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Accept")
request.addValue("Basic \(loginData!)", forHTTPHeaderField: "Authorization")
request.httpBody = JSON
So am I taking a string and converting to data, then into a JSON object? I'm thoroughly confused as to how best to do this. I've searched and searched and all I'm finding is parsing articles, which don't exactly help.
Thanks ahead of time.
ANSWERED:
The problem was in the request.setValue. I needed to use Content-Type instead of Accept.
// get JSON
let path = Bundle.main.path(forResource: "zero_RC", ofType: "txt")
let data = try! Data(contentsOf: URL(fileURLWithPath: path!), options: .mappedIfSafe)
// make the request
var request = URLRequest(url: url)
request.httpMethod = "POST"
request.setValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("Basic \(loginData!)", forHTTPHeaderField: "Authorization")
request.httpBody = data
Here's the solution firstly we will decode your json file into a data model, then will encode that data model object to the httpBody.
let path = Bundle.main.path(forResource: "zero_RC", ofType: "txt")
let data = try! Data(contentsOf: URL(fileURLWithPath: path), options: .mappedIfSafe)
let jsonDecoder = JSONDecoder()
let json = try! jsonDecoder.decode(JsonObjModel.self, from: data)
// now encoding that jsonData to the http body
let encoder = JSONEncoder()
urlRequest.httpBody = try! encoder.encode(json)
The 'JsonObjModel' will be the following
// MARK: - JSONObjModel
struct JSONObjModel: Codable {
let query: String
let filters: [JSONObjModelFilter]
}
// MARK: - JSONObjModelFilter
struct JSONObjModelFilter: Codable {
let filters: [FilterFilter]
}
// MARK: - FilterFilter
struct FilterFilter: Codable {
let field, value, type: String
}
I'm having a problem sending a POST request with Alamofire.
I need to send the usser and password fields as application/x-www-form-urlencode and also some query data in the url.
I am creating a URLRequest to handle the process, but I'm getting always a 400 response from the server, so I guess the problem must be in the way I create the request.
This is the example in Postman:
I need to send a param in the url and two more in as application/x-www-form-urlencode
Postman 1 - Parameters
Postman 2 - ContentType
I need to do this (that i have in Android)
#FormUrlEncoded
#POST(Constants.AUTH_LDAP)
Call<ResponseBody> authLdap(
#Query(value = Constants.PARAM_REQ, encoded = true) String req,
#Field(Constants.PARAM_LOGIN) String login,
#Field(Constants.PARAM_PASSWORD) String password
);
And this is what I have in swift
let queryParamters = [Constants.Params.PARAM_REQ:req]
let headers = ["Content-Type": "application/x-www-form-urlencoded"]
let fieldParameters = [
Constants.Params.PARAM_LOGIN : user,
Constants.Params.PARAM_PASSWORD : pass]
let url = URL(string: Constants.EndPoints.AUTH_LDAP)
let request = URLRequest(url: url!)
let encoding = try URLEncoding.default.encode(request, with: queryParamters as Parameters)
let encodingpa = try URLEncoding.httpBody.encode(request, with: fieldParameters as Parameters)
var urlRequest = encodingpa
urlRequest.url = encoding.url
urlRequest.allHTTPHeaderFields = headers
Alamofire.request(urlRequest).responseData(completionHandler: { response in
switch response.result {
case .success:
print("sucess")
print(response.response)
case .failure(let error):
print(error)
}
})
Thanks for your help.
Try to create url from queryParameters using URLComponents like
var urlComponents = URLComponents(string: Constants.EndPoints.AUTH_LDAP)!
urlComponents.queryItems = [
URLQueryItem(name: Constants.Params.PARAM_REQ, value: req)
]
let headers = ["Content-Type": "application/x-www-form-urlencoded"]
var request = URLRequest(url: urlComponents.url!)
request.httpMethod = "POST"
request.httpBody = try? JSONSerialization.data(withJSONObject: fieldParameters)
request.allHTTPHeaderFields = headers
Alamofire.request(request).responseJSON { response in
}
I created a Router to generate URL Requests.
enum Router: URLRequestConvertible {
static let baseURLString = "SERVERIP"
case GetAEDInRange(String)
// match URLRequest routes to Alamofire methods
var URLRequest: NSMutableURLRequest {
var method: Alamofire.Method {
switch self {
case .GetAEDInRange:
return .GET
}
}
// The output contains the path and parameters like ("aeds", newAED)
let result: (path: String, parameters: [String: AnyObject]?) = {
switch self {
case .GetAEDInRange(let parameters):
return ("aeds", parameters)
}()
// Generate URL Request
let URL = NSURL(string: Router.baseURLString)!
// Append the path components from the result
print(result.path)
let URLRequest = NSURLRequest(URL: URL.URLByAppendingPathComponent(result.path))
// Create URLRequest inclunding the encoded parameters
let encoding = Alamofire.ParameterEncoding.JSON
let (encodedRequest, _) = encoding.encode(URLRequest, parameters: result.parameters)
encodedRequest.HTTPMethod = method.rawValue
return encodedRequest
}
}
The output I expect is: http://BASEURL/v1/aed?latitude=100&longitude=100
When I use Alamofire to make a GET request with parameters attached, it works fine:
Alamofire.request(.GET, "http://SERVER/v1/aeds", parameters: parameters).responseJSON { (response) -> Void in
print(response.result.value)
}
When I use my router instead, the output is not generated as expected:
Alamofire.request(Router.GetAEDInRange(parameters)).responseJSON { (response) -> Void in
print(response.result.value)
}
When I print the URL String, I get: `http://SERVER/v1/aeds/``
How do I need to change my router? I struggle with the parameter component somehow.
Change this line
let encoding = Alamofire.ParameterEncoding.JSON
to this:
let encoding = Alamofire.ParameterEncoding.URLEncodedInURL
To understand the difference between Parameter Encodings in Alamofire take a look here.
Swift3
'Alamofire', '~> 4.0'
let parameters: [String: Any] = [
"parameter1" : data1,
"parameter2" : data2
]
Alamofire.request("https://httpbin.org/get", parameters: parameters, encoding: URLEncoding(destination: .queryString))
I'm using Alamofire and want to encode my parameters with content type "text/html; charset=utf-8". I followed the documentation https://github.com/Alamofire/Alamofire for custom parameter encoding and created
let custom: (URLRequestConvertible, parameters: [String: AnyObject]?) -> (NSURLRequest, NSError?) = {
(URLRequest, parameters) in
let mutableURLRequest = URLRequest.URLRequest.mutableCopy() as! NSMutableURLRequest
mutableURLRequest.setValue("text/html; charset=utf-8", forHTTPHeaderField: "Content-Type")
mutableURLRequest.body = // don't know if I need to set this
return (mutableURLRequest, nil)
}
func postData(){
Alamofire.request(.POST, baseUrl, parameters: parameters, encoding: .Custom(custom))
.responseString{ (request, response, data, error) in
println("blah")
}
}
I have a problem when I try to use custom in my Alamofire request and get the error "Cannot make responseString with argument list of type ( _, _, _, _)-> _ )" However, this isn't a problem if the encoding is changed to a preset like .URL so the issue seems to be in my implementation of custom?
If it makes a difference my parameters are set here:
var parameters = [String: AnyObject]()
func setParams(){
parameters = [
"CONTRACT ID" : chosenContract!.iD.toInt()!,
"FEE AMOUNT" : 0,
"TRANSACT DATE" : today
]
}
You have a couple questions in here. Let's break them down 1x1.
Compiler Issue
Your compiler issue is due to the fact that your return tuple is the wrong type. In Alamofire 1.3.0, we changed the return type to be an NSMutableURLRequest which ends up making things much easier overall. That should fix your compiler issue.
Setting the HTTPBody
Now you have a couple options here.
Option 1 - Encode Data as JSON
let options = NSJSONWritingOptions()
let data = try NSJSONSerialization.dataWithJSONObject(parameters!, options: options)
mutableURLRequest.setValue("application/json", forHTTPHeaderField: "Content-Type")
mutableURLRequest.HTTPBody = data
From what you posted I'm assuming you actually want .URL encoding for the parameters.
Option 2 - Use the .URL Case
let parameters: [String: AnyObject] = [:] // fill in
let encodableURLRequest = NSURLRequest(URL: URL)
let encodedURLRequest = ParameterEncoding.URL.encode(encodableURLRequest, parameters).0
let mutableURLRequest = NSMutableURLRequest(URL: encodedURLRequest.URLString)
mutableURLRequest.HTTPMethod = "POST"
mutableURLRequest.setValue("text/html; charset=utf-8", forHTTPHeaderField: "Content-Type")
Alamofire.request(mutableURLRequest)
.response { request, response, data, error in
print(request)
print(response)
print(error)
}
Hopefully that helps get you going. Best of luck!
The Locu API provides this example using CURL to perform a location sensitive query:
curl -X POST https://api.locu.com/v2/venue/search -d '{
"api_key" : "f165c0e560d0700288c2f70cf6b26e0c2de0348f",
"fields" : [ "name", "location", "contact" ],
"venue_queries" : [
{
"location" : {
"geo" : {
"$in_lat_lng_radius" : [-37.7750, 122.4183, 5000]
}
}
}]
}'
This is my attempt in Swift:
let LOCU_API_KEY = "<API_KEY>"
let centerLatitude = mapView.region.center.latitude
let centerLongitude = mapView.region.center.longitude
let arr = [centerLatitude,centerLongitude,5000]
let url = NSURL(string: "https://api.locu.com/v2/venue/search")
var request = NSMutableURLRequest(URL: url!)
request.HTTPMethod = "POST"
var params = ["api_key":LOCU_API_KEY,"fields":["name","location","contact"], "venue_queries":[["location":["geo":["$in_lat_lng_radius":arr]]]]] as [String:AnyObject!]
var err: NSError?
NSJSONSerialization.dataWithJSONObject(params, options: nil, error: &err)
request.addValue("application/json", forHTTPHeaderField: "Content-Type")
request.addValue("application/json", forHTTPHeaderField: "Accept")
NSURLConnection.sendAsynchronousRequest(request, queue: NSOperationQueue.mainQueue(), completionHandler: { (response:NSURLResponse!, data:NSData!, error:NSError!) -> Void in
if error == nil {
var err:NSError?
let httpResponse = response as NSHTTPURLResponse!
println(response.description)
if httpResponse.statusCode == 200 {
if var json = NSJSONSerialization.JSONObjectWithData(data, options: NSJSONReadingOptions.MutableContainers, error:&err) as? NSDictionary {
println(json)
}
}
}
})
I only get an HTTP 400 - Bad request for this and various simplifications, like just providing the api_key. Version 1_0 of the Locu works fine, although it doesn't have the features I need.
afRequestOperationManager.GET is performing a GET request rather than a POST request.
Furthermore, the request data must be in JSON, whereas you are using URL parameters.
As others have said you need to use .POST or in someway do a POST request not a GET.
Also, it looks like this line:
let urlString = "https://api.locu.com/v2/venue/"
Should be:
let urlString = "https://api.locu.com/v2/venue/search"
Right? Notice the "search" at the end. That is why you are getting 400 I assume (404 I guess?!).
Let us know if it worked.
The '-X POST part of the curl command means you need to do an HTTP POST, not an HTTP GET.
Try using afRequestOperationManager.POST instead.