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

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?

Related

AWS get response as data, not JSON (for using with Codable)

Not familiar enough with AWS, but I have some Codable models I need to initialize from AWS. I'm getting JSON result from AWSTask.result (which is AnyObject). I'm trying to avoid creating Data from Dictionaty and back to a struct (to be able to use Codable).
I tied to use AWSNetworkingHTTPResponseInterceptor, but it was never got called and I couldn't find any example of using it.
self.getGatewayClient { (apiGatewayClient: AWSAPIGatewayClient?) in
let queryParameters = ...
let headerParameters = ...
apiGatewayClient?.invokeHTTPRequest(
"GET",
urlString: "/path",
pathParameters: [:],
queryParameters: queryParameters,
headerParameters: headerParameters,
body: nil,
responseClass: nil
).continueWith { (task: AWSTask<AnyObject>) -> Any? in
if let data = task... { // Get response as Data type??
}
if let result = task.result as? [String: Any] {
// Thanks, but I have a Codable, so I'll just take the data thank you.
}
return task
}
}
AWS's AWSAPIGatewayClient has two functions, one is: invokeHTTPRequest (which was what was used). There is another one called invoke, which returns data. It takes a AWSAPIGatewayRequest request:
func someTask(completion: #escaping (String?) -> ()) {
self.getGatewayClient { (apiGatewayClient: AWSAPIGatewayClient?) in
let request = AWSAPIGatewayRequest(httpMethod: "GET",
urlString: "/path",
queryParameters: queryParameters,
headerParameters: headerParameters,
httpBody: nil)
apiGatewayClient?.invoke(request).continueOnSuccessWith { response in
if let data = response.result?.responseData {
// Init Codable using data
}
}
}
}

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

Encode parameters for URL Request

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

List expects 3 elements but I don't know from where third comes

So I was trying to follow a tutorial for oAuth 2.0 from here.
Under the section "OAuthSwift with Embedded Web View".
This is the whole function:
// 1 Create OAuth2Swift object
let oauthswift = OAuth2Swift(
consumerKey: "YOUR_GOOGLE_DRIVE_CLIENT_ID", // 2 Enter google app settings
consumerSecret: "YOUR_GOOGLE_DRIVE_CLIENT_SECRET",
authorizeUrl: "https://accounts.google.com/o/oauth2/auth",
accessTokenUrl: "https://accounts.google.com/o/oauth2/token",
responseType: "code"
)
// 3 Trigger OAuth2 dance
oauthswift.authorizeWithCallbackURL(
NSURL(string: "com.raywenderlich.Incognito:/oauth2Callback")!,
scope: "https://www.googleapis.com/auth/drive", // 4 Scope
state: "",
success: { credential, response in
var parameters = [String: AnyObject]()
// 5 Get the embedded http layer and upload
oauthswift.client.postImage(
"https://www.googleapis.com/upload/drive/v2/files",
parameters: parameters,
image: self.snapshot(),
success: { data, response in
let jsonDict: AnyObject! = NSJSONSerialization.JSONObjectWithData(data,
options: nil,
error: nil)
self.presentAlert("Success", message: "Successfully uploaded!")
}, failure: {(error:NSError!) -> Void in
self.presentAlert("Error", message: error!.localizedDescription)
})
}, failure: {(error:NSError!) -> Void in
self.presentAlert("Error", message: error!.localizedDescription)
})
I get an error on point 4 (scope) where the list is filled:
success: { credential, response in
var parameters = [String: AnyObject]()
It says it expects 3 arguments but specified are just two. Any help would be really appreciated.
It may be that the two arguments were valid back when the tutorial was made (or last updated), and now it requires three arguments. So, replace:
success: { credential, response in
var parameters = [String: AnyObject]()
...
with:
success: { credential, response, parameters in
...
The let jsonDict line also follows an old format, and doesn't handle errors, so change it to:
do {
let jsonDict = try NSJSONSerialization.JSONObjectWithData(data, options: []) as! [String: AnyObject]
// And replace [String: AnyObject] with your JSON format.
} catch {
// Error handling here
}

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.