Encode parameters for URL Request - swift

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

Related

Alamofire, HTTPheaders for post request [string:any]

I need to send a post request using alamofire to my server, one of the header to be sent is not a string value but is an Int
Reading the documentation of Alamofire look like the HTTPHeaders is only type [String: String]
Is there any way to customise the HTTPHeaders to [String:Any]?
I can't find to much understandable for me online.
thanks
Alamofire doesn't have such methods, but you can easily do it
["hey": 1].mapValues { String(describing: $0) } returns [String: String]
If you have many places where you're using it, you can:
Create extension for Dictionary
extension Dictionary where Key == String, Value == Any {
func toHTTPHeaders() -> HTTPHeaders {
HTTPHeaders(mapValues { String(describing: $0) })
}
}
// Usage
AF.request(URL(fileURLWithPath: ""), headers: ["": 1].toHTTPHeaders())
Create extension for HTTPHeaders
extension HTTPHeaders: ExpressibleByDictionaryLiteral {
public init(dictionaryLiteral elements: (String, Any)...) {
self.init()
elements.forEach { update(name: $0.0, value: String(describing: $0.1)) }
}
}
// usage
AF.request(URL(fileURLWithPath: ""), headers: HTTPHeaders(["": 1]))
Create extension for Session
extension Session {
open func request(_ convertible: URLConvertible,
method: HTTPMethod = .get,
parameters: Parameters? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: [String: Any],
interceptor: RequestInterceptor? = nil,
requestModifier: RequestModifier? = nil) -> DataRequest {
return request(convertible, method: method, parameters: parameters, encoding: encoding, headers: headers.mapValues { String(describing: $0) }, interceptor: interceptor, requestModifier: requestModifier)
}
}
// Usage
AF.request(URL(fileURLWithPath: ""), headers: ["": 1])
The reason there's no such option in Alamofire is type safety. When you use Any you can literary pass any value there and so probability of a mistake is much more. By requiring string library makes sure you're converting all values you need by yourself.
I'd go for the first variant, because it's more clear when you read the code that there's something going on there

Alamofire get method with parameters in the url with header swift

I have a get method with 3 parameters on the base url itself.I have tried the following code, but it is going to failure condition saying either not a valid url or not a valid JSON.
What is the correct way to approach this?
The code i have used is as below:
let header: HTTPHeaders = ["Content-Type":"application/json","x-token":self.token!]
let todosEndpoint: String = "https://reachwebdemo.com/2020/10/listcribdev/api/chatnotification?" + "channel_sid=\(self.channelsid!)&author=\(self.userid!)&message=\(inputMessage)"
if let encoded = todosEndpoint.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed),let url = URL(string: encoded)
{
print("notify url is",url)
AF.request(url, method: .get, parameters: nil, encoding: URLEncoding.default, headers: header).responseString { response in
switch response.result {
case .success(let json):
print("Validation Successful for push notification",json)
case let .failure(error):
print("error for push notificaton",error.errorDescription)
}
}
}
You user parameters like url and it's wrong way to make request like this.
You need add parameters on request method like parameters. And yes, you can use parameters on 'GET' requests.
let header: HTTPHeaders = ["Content-Type":"application/json","x-token":self.token!]
let todosEndpoint: String = "https://reachwebdemo.com/2020/10/listcribdev/api/chatnotification"
let params:[String: Any] = ["channel_sid":self.channelsid!, "author":self.userid!, "message": inputMessage]
if let encoded = todosEndpoint.addingPercentEncoding(withAllowedCharacters: .urlFragmentAllowed),
let url = URL(string: encoded) {
print("notify url is",url)
AF.request(url, method: .get, parameters: params, encoding: URLEncoding.default, headers: header).responseString { response in
switch response.result {
case .success(let json):
print("Validation Successful for push notification",json)
case let .failure(error):
print("error for push notificaton",error.errorDescription)
}
}
}

Where is the actual API call within this RxAlamofire source code?

I'm trying to figure out where data in an API call is coming from. Specifically, I want to know where req.user is coming from as from what I can tell there's no paramters being passed into the API call.
Here's the server-side code (in JS):
let APIHandler = (req, res) = > {
if (req.user) {
latitude = req.user.location.latitude
longitude = req.user.location.longitude
}
}
And here's the client-side code (in Swift):
let strURLTo = SERVICE.BASE_URL + apiName + limit
let headers = AuthorizationHeader.getAuthHeader(staticToken: false)
var urlComponents = URLComponents(string: strURLTo)
urlComponents?.queryItems = [URLQueryItem(name: "offset", value: String(offset)),
URLQueryItem(name: "limit", value: String(limit))]
let strURL = urlComponents?.url
RxAlamofire.requestJSON(.get,strURL!,parameters:[:],headers:headers)
.debug()
.subscribe(onNext: {(HeaderResponse, bodyResponse) in
if let dict = bodyResponse as? [String: AnyObject] {
if let respDict: [String: Any] = JSON(dict).dictionaryObject {
let response = ResponseModel.init(statusCode: HeaderResponse.statusCode, response:respDict)
self.subject_response.onNext(response)
}
}
}, onError: { (error) in
self.subject_response.onError(error)
})
.disposed(by: disposeBag)
And finally here's the RxAlamofire.requestJson definition:
public func requestJSON(_ method: Alamofire.HTTPMethod,
_ url: URLConvertible,
parameters: [String: Any]? = nil,
encoding: ParameterEncoding = URLEncoding.default,
headers: [String: String]? = nil)
-> Observable<(HTTPURLResponse, Any)>
{
return SessionManager.default.rx.responseJSON(
method,
url,
parameters: parameters,
encoding: encoding,
headers: headers
)
}
What I tried was tracing through the RxAlamofire.requestJSON function step-by-step but I don't see anywhere where the actual API call happens (to me it seems like it's just outlining the types it's expecting/returning). Additionally there's no parameters in the RxAlamofire.requestJSON(.get,strURL!,parameters:[:],headers:headers)
call.
The actual call is being made in this line (which has been spread out over multiple lines for clarity):
return SessionManager.default.rx.responseJSON(
method,
url,
parameters: parameters,
encoding: encoding,
headers: headers
)
The parameters are being set here:
var urlComponents = URLComponents(string: strURLTo)
urlComponents?.queryItems = [URLQueryItem(name: "offset", value: String(offset)),
URLQueryItem(name: "limit", value: String(limit))]
However, I also do not see user being set, so maybe it is happening in the API function at the top of this answer?

Alamofire: Send JSON with Array of Dictionaries

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

HTTP Request error using Alamofire

I am trying to access my MAMP database webservice using Alamofire. Following is my code:
Following is my router to construct my URL:
enum Router: URLRequestConvertible {
static let baseURLString = "http://pushchat.local:44447/"
case PostJoinRequest(String,String,String,String,String)
var URLRequest: NSURLRequest {
let (path: String, parameters: [String: AnyObject]) = {
switch self {
case .PostJoinRequest (let addPath, let userID, let token, let nickName, let secretCode):
let params = ["cmd": "join", "user_id": "\(userID)", "token": "\(token)", "name": "\(nickName)", "code": "\(secretCode)"]
return (addPath, params)
}
}()
let URL = NSURL(string: Router.baseURLString)
let URLRequest = NSURLRequest(URL: URL!.URLByAppendingPathComponent(path))
let encoding = Alamofire.ParameterEncoding.URL
return encoding.encode(URLRequest, parameters: parameters).0
}
}
Following is my viewdidload code:
Alamofire.request(.POST,Router.PostJoinRequest("api.php","12345678901234","12345678901234","ABCDEF","TopSecret")).responseJSON()
{(request, response, JSON, error) in
println(JSON)
}
Following is the compile error:
Cannot invoke 'responseJSON' with an argument list of type '((,,,)->_)'
Following is the declaration from Almofire for your reference.
:param: method The HTTP method.
:param: URLString The URL string.
:param: parameters The parameters. `nil` by default.
:param: encoding The parameter encoding. `.URL` by default.
:returns: The created request.
*/
// public func request(method: Method, _ URLString: URLStringConvertible, parameters: [String: AnyObject]? = nil, encoding: ParameterEncoding = .URL) -> Request {
// return request(encoding.encode(URLRequest(method, URLString), parameters: parameters).0)
// }
Please let me know why am I facing this issue while chaining and what is it that I am not doing right?
Thanks for your help.
Dev
The compiler error message is really misleading – there is no problem with responseJSON but with request method itself.
In fact compiler does not like your second parameter. You are passing URLRequestConvertible but URLStringConvertible is expected (see the signature you posted).
Maybe you wanted to call another version of request method:
//:param: URLRequest The URL request
//:returns: The created request.
public func request(URLRequest: URLRequestConvertible) -> Request
In that case you have to adjust your Router class and set HTTP method into NSURLRequest created inside. For example:
let URLRequest = NSMutableURLRequest(URL: URL!.URLByAppendingPathComponent(path))
URLRequest.HTTPMethod = "POST"
Note you will also probably need to use another parameter/data encoding.