How to return a modified result in a promise as a promise - swift

I'm using promiseKit 6 with the extensions for Alamofire. In the function bellow I'm looking to return Promise<JSON> (I use swiftyJson) but the response from the alamofire call is a Promise containing a tuple: Promise<(json: Any, response: PMKAlamofireDataResponse)>
when I get that from the first then, how can I continue to return just the json part? thanks :)
return firstly {
self.requestClient.request(url, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: nil).responseJSON()
}.then { arg in
let (json, rsp) = arg
return json
}
I also get this error: Cannot convert return expression of type 'Promise<_.T>' to return type 'Promise<JSON>'
on the line: }.then { arg in ...

You should cast Any to JSON, try this (not tested), but documentation said you could use map/compactMap https://github.com/mxcl/PromiseKit/blob/master/Documentation/CommonPatterns.md
return firstly {
self.requestClient.request(url, method: .get, parameters: nil,
encoding: JSONEncoding.default, headers: nil).responseJSON()
}.compactMap { data, rsp in
return data as? JSON
}

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

Can't make post request using params as dictionary with Swift 4 & Alamofire

I'm trying to learn to call API with/without library. But the problem here confuses me.
I have params like this:
let parameters: [String:String] =
["key":"MY_KEY" ,
"q":sourceText,
"source": sourceLanguage),
"target": target)]
let headers: HTTPHeaders = [ "Content-type": "application/json"]
I make a post call like this:
Alamofire.request(urlString, method: HTTPMethod.post, parameters: parameters, headers: headers)
.responseJSON{ response in
guard response.result.error == nil else {
print("error calling POST on /todos/1")
print(response.result.error!)
return
}
// make sure we got some JSON since that's what we expect
guard let json = response.result.value as? [String: Any] else {
print("didn't get todo object as JSON from API")
print("Error: \(response.result.error)")
return
}
By this I get an error 403, saying that I do not have a valid API key (I tested the key with postman, and it is okay).
After many efforts, I have to change the code like this
let stringparams = "key=MY_KEY&q=\(sourceText)&source=\(sourceLanguage)&target=\(target)"
request.httpBody = stringparams.data(using: String.Encoding.utf8)
and using this: Alamofire.request(request)
it works!
I'm using Google Cloud Translation api. And the web use a REST api as said here: https://cloud.google.com/translate/docs/reference/translate
So why can't I use params as dictionary, but using the string (like formdata) will work here?
My guess is Alamofire didn't make the right BODY for the request from the parameters because other arguments is okay. But I don't know why.
And I think Google should accept a json params as they mentioned, in stead of using form data? I did try the original method, but it didn't work with JSON.
From what actually works for you it looks like you need to encode the parameters in the same style as a query. Try this:
struct ParameterQueryEncoding: ParameterEncoding {
func encode(_ urlRequest: URLRequestConvertible, with parameters: Parameters?) throws -> URLRequest {
var request = try urlRequest.asURLRequest()
request.httpBody = parameters?
.map { "\($0)=\($1)" }
.joined(separator: "&")
.data(using: .utf8)
return request
}
}
You should then be able to perform the original call that you had before:
Alamofire.request(urlString,
method: HTTPMethod.post,
parameters: parameters,
encoding: ParameterQueryEncoding(),
headers: headers)
.responseJSON { response in
...
}
Try by using JSON encoding. Make sure you have removed ) from dictionary.
Alamofire.request(URL, method: method, parameters: parameters, encoding: JSONEncoding.default, headers: headers)

Extra argument in 'method' in call

let playlistUrl = NSURL(string: "https://www.googleapis.com/youtube/v3/playlistItems")!
let params = ["key":API_KEY,"part":"snippet","playlistId":PLAYLIST_ID]
Alamofire.request(playlistUrl, method: HTTPMethod.get, parameters: params, encoding: ParameterEncoding.URL, headers: nil)
Swift 3.0 and Alamofire 4
The thing which you are doing wrong is passing a NSURL as the first argument. Don't pass that as a URL, pass it as a string instead. Also you are doing the wrong encoding here.
So the modified code will be as follows:-
Alamofire.request("https://www.googleapis.com/youtube/v3/playlistItems", method: HTTPMethod.get, parameters: params, encoding: JSONEncoding.default, headers: nil)
The result here is unused. So take the result in a closure.
Alamofire.request("https://www.googleapis.com/youtube/v3/playlistItems", method: HTTPMethod.get, parameters: params, encoding: JSONEncoding.default, headers: nil).responseData { (response:DataResponse<Data>) in
switch(response.result) {
case .success(_):
if let data = response.result.value{
print(data)
}
break
case .failure(_):
print(response.result.error)
break
}
}
Also do check the encoding required. Check this link for more details.
https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%204.0%20Migration%20Guide.md#parameter-encoding-protocol
Also check this answer posted by me.
Alamofire Swift 3.0 Extra parameter in call
JSONEncoding.default or URLEncoding.default totally depends on the type of API architecture made at backend.
for alamofire 4,
Alamofire.request(playlistUrl, method: .get, parameters: params, encoding: JSONEncoding.default)
Hope this will help you.
For more about latest change in alamofire visit,
https://github.com/Alamofire/Alamofire/blob/master/Documentation/Alamofire%204.0%20Migration%20Guide.md#parameter-encoding-protocol
//Correct code is:
let playlistUrl = NSURL(string:
"https://www.googleapis.com/youtube/v3/playlistItems")!
let params:Parameters =
["key":API_KEY,"part":"snippet","playlistId":PLAYLIST_ID]
Alamofire.request(playlistUrl, method: HTTPMethod.get, parameters:
params, encoding: ParameterEncoding.URL, headers: nil)

Trying to migrate Alamofire 3 requests to version 4 with headers

I'm trying to migrate these type of calls:
let request = Alamofire.request(urlString, method: .put, parameters: ["password" : newPassword], encoding: .json, headers: ServiceManager.authorizationHeaders()).validate().responseJSON {
To the Alamofire 4.0.0 syntax but no matter what I try, I get 'extra argument in call'.
I checked the documentation and can't see where I'm going wrong, nor can I find an example of how to set the headers on the new version - apologies in advance if I missed that part.
Thanks for any advice.
Gareth.
This works for me
Swift 3.0
Alamofire.request("https://yourServiceURL.com", method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: nil).responseJSON { (response:DataResponse<Any>) in
switch(response.result) {
case .success(_):
if let data = response.result.value{
print(response.result.value)
}
break
case .failure(_):
print(response.result.error)
break
}
}
and make sure the parameters are of type
[String:Any]?

RX Alamofire Swift empty response JSON

I am using Alamofire with rx and i'm having one issue that if the call is a success (status 200) there is no JSON returned. This triggers my error code. If I get status 400 I get JSON so the call is fine.
How do I specify that the response is JSON, but not to error if empty and status code 200?
Thanks!
func createUser(httpBody: AccountDetails!) -> Observable<(NSHTTPURLResponse, AnyObject)> {
return Alamofire.Manager.rx_request(.POST,
APIService.REGISTER_ENDPOINT,
parameters: httpBody.getParameters(),
encoding: .JSON,
headers: nil)
.flatMap {
$0
.validate(statusCode: 200..<501)
.validate(contentType: ["application/json"])
.rx_responseJSON()
.map { (response:NSHTTPURLResponse, object:AnyObject) -> (NSHTTPURLResponse, AnyObject) in
return (response, object)
}
}
}
EDIT:
I fixed this by changing .rx_responseJSON() to .rx_responseData() and returning NSData instead of AnyObject as the second parameter. Now the call succeeds and I manually convert the data to JSON. Not sure if this is the correct solution but it will suffice for now. Happy to hear better solutions though.
Thanks
I fixed this by changing .rx_responseJSON() to .rx_responseData() and returning NSData instead of AnyObject as the second parameter. Now the call succeeds and I manually convert the data to JSON. Not sure if this is the correct solution but it will suffice for now. Happy to hear better solutions though. Thanks
func createUser(httpBody: AccountDetails!) -> Observable<(NSHTTPURLResponse, NSData)> {
return APIManager.sharedManager.rx_request(.POST,
APIService.REGISTER_ENDPOINT,
parameters: httpBody.getParameters(),
encoding: .JSON,
headers: nil)
.flatMap {
$0
.validate(statusCode: [200,400,500])
.validate(contentType: ["application/json"])
.rx_responseData()
.map { (response:NSHTTPURLResponse, object:NSData) -> (NSHTTPURLResponse, NSData) in
return (response, object)
}
}
}