Alamofire responseArray String array for keyPath - swift

I have a rest call which returns array of string [String] for keyPath "data", for example...
{
"data": [
"1",
"3",
"5",
"2",
"4",
"9"
]
}
I am trying to get it via responseArray(keyPath: "data"), but get compile error for line *.responseArray(keyPath: "data") {(response: DataResponse<[String]>) in*
Cannot convert value of type '(DataResponse<[String]>) -> ()' to expected argument type '(DataResponse<[_]>) -> Void'
Part of request example
alamofireManager.request(url)
.responseArray(keyPath: "data") {(response: DataResponse<[String]>) in
if response.result.isSuccess {
if let data = response.result.value {
//process success
} else {
// handle error
}
}
...
Does anybody of you know how to do it ?

The problem is that String isn't Mappable. As per https://github.com/Hearst-DD/ObjectMapper/issues/487, these are the proposed solutions:
In this situation I would recommend accessing the data directly from the Alamofire response. You should be able to simply cast it to [String].
Alternatively, you may be able to subclass String and make the subclass Mappable, however I think that is more work than necessary for the your situation
Using Swift's 4 Codable (no external dependencies):
struct APIResponse: Decodable {
let data: [String]
}
let url = "https://api.myjson.com/bins/1cm14l"
Alamofire.request(url).responseData { (response) in
if response.result.isSuccess {
if let jsonData = response.result.value,
let values = try? JSONDecoder().decode(APIResponse.self, from: jsonData).data {
//process success
print(values)
} else {
// handle error
}
}
}

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

Manually Parsing a a JSON into an Object of a Struct

I'm a beginner in swift and I'm currently making an app that makes a web request. I've been trying to parse this JSON Data but the nested data is just really hard to wrap my head around:
"abilities": [
{
"ability": {
"name": "chlorophyll",
"url": "https://pokeapi.co/api/v2/ability/34/"
},
"is_hidden": true,
"slot": 3
},
{
"ability": {
"name": "overgrow",
"url": "https://pokeapi.co/api/v2/ability/65/"
},
"is_hidden": false,
"slot": 1
}
]
JSon Serialization Code
let jsonAny = try JSONSerialization.jsonObject(with: data, options: [])
guard let json = jsonAny as? [String: Any] else { return }
This is my attempt to manually parse the JSON Data
private func parsePokemonManual(json: [String: Any]) -> Pokemon {
let abilities = json["abilities"] as? [String: Any] ?? [String: Any]()
return Pokemon(abilities: abilities)
}
}
These are the structs that I made to hold the data.
struct Abilities {
let ability : Ability
struct Ability {
let name : String
}
}
How do I successfully parse the JSON Data into an object of Pokemon structure?
With this code so fat I am getting the error "Cannot convert the value of type '[String : Any]' to expected argument type '[Abilities]'. My problem is that I don't know what type to cast the abilities as and that my struct 'Abilities' is also incorrect.
There are 3 problems with your attempt although one might argue there is only 1, that you should use Codable instead but lets stay with JSONSerialization here. The problems are
You are reading the json wrong and should cast not to a dictionary but an array of dictionaries when accessing "abilities"
Your struct is to complicated, maybe because of the previous problem
Lastly, you can't cast into a custom type, you need to convert or map the data into your type by telling exactly what values to use and how because the compiler doesn't understand how to do it.
First the struct can be simplified to
struct Ability {
let name : String
}
And the rest is fixed in the function parsePokemonManual. First get "abilities" and cast to an array of dictionaries. Then map each item in the array by getting "ability" and casting it to a dictionary that is used to get the "name" value that is then used when creating an instance of Ability
private func parsePokemonManual(json: [String: Any]) -> [Ability] {
guard let abilities = json["abilities"] as? [[String: Any]] else {
return []
}
return abilities.compactMap { dict in
guard let ability = dict["ability"] as? [String: String], let name = ability["name"] else { return nil }
return Ability(name: name)
}
}

Swift 4 "Extra argument in call" Rxswift

I updated from Swift 3 to 4, and i am using RxSwift, When updated i came across an error "Extra argument in call" as it expects now of type element. I tried creating tuple of (response, data) but still gives me an error as shown.
public static func deleteCrush(receiver: String, receiver_type: ProviderType) -> Observable<(HTTPURLResponse, NSArray)> {
/*
Delete crush identified by `id`
*/
let parameters: Parameters = [
"receiver": receiver,
"receiver_type": receiver_type.rawValue
]
print("deleteCrush paramreters: \(parameters)")
return Observable.create({ (observer) -> Disposable in
Alamofire.request(Router.deleteCrush(parameters: parameters))
.rx
.responseJSON()
.debug()
.subscribe(onNext: { (response, json) in
print("deleteCrush response code: ", response.statusCode)
if let data = json as? NSArray {
observer.on(.next(response, data))
observer.on(.completed)
}
}, onError: { (error) in
print("deleteCrush error: ", error)
observer.on(.error(error))
}, onCompleted: nil, onDisposed: nil)
})
}
Error: Extra argument in call.
After trying to fix:
var tuple = (response, json)
if let data = json as? NSArray {
observer.on(.next(tuple))
observer.on(.completed)
}
Error:
Event<(HTTPURLResponse, NSDictionary)>' produces result of type 'Event', but context expects 'Event<(HTTPURLResponse, NSDictionary)>
It just seems like you are not passing correct data type to your events.
Either simply wrap your tuple properly,
observer.on(.next((response, data)))
Or then use proper tuple type for your Element type,
if let data = json as? NSArray {
var tuple = (response, data)
observer.on(.next(tuple))
observer.on(.completed)
}
Note that you are setting tuple as tuple = (response, json) which is not correct according to your method return type.
Swift is probably having a hard time figuring out the type of the of the data you are sending in .next. Sometimes this happens and the compiler provides a completely useless error message
Check your function definition. Specifically the return type. The observer you're returning may not be of the same type you are sending to your observers.

True becomes false using SwiftyJson in swift

I am getting the opposite value of a boolean when using .boolValue
I have this code.
let _ = channel.bind(eventName: "like-unlike-cake", callback:
{(data:Any?)->Void in
let likedCake = JSON(data!)["like"]
print("liked cake: \(likedCake)")
print("Boolean: \(likedCake["liked_by_current_user"].boolValue)")
liked cake: {
"liked_by_current_user" : true
}
}
Boolean: false
But when I use .bool it gives me nil. Does someone help me on this.
UPDATE This would be the json I want to fetch
{
"like": {
"id": "66642122-7737-4eac-94d2-09c9a35cbef8",
"liked_by_current_user": true
}
}
Try the following:
let _ = channel.bind(eventName: "like-unlike-cake", callback {
(data:Any?)->Void in
if let jsonData = data {
let json = JSON(jsonData)
let isLikedByCurrentUser = json["like"]["liked_by_current_user"].boolValue
print(isLikedByCurrentUser)
} else {
// your data is nil
debugPrint("data error")
}
}
also you can try to cast your data: Any into Data by replacing the line:
if let jsonData = data as? Data {
let json = JSON(data: jsonData)...
cause sometimes SwiftyJSON have problems with creating JSON objects from Any.

Getting JSON array with Alamofire + SwiftyJSON

I'm really new to Swift, sorry if this is a dumb question... there seem to be many questions about this but none of them use the latest version of Alamofire
Alamofire.request(.GET, url)
.responseJSON { response in
let json = JSON(response.data!)
debugPrint(json)
self.delegate?.didReceiveAPIResults(json)
}
And the delegate's didReceiveAPIResults method
func didReceiveAPIResults(results: JSON) {
dispatch_async(dispatch_get_main_queue(), {
self.tableData = results["items"].arrayObject!
self.appsTableView!.reloadData()
})
}
Here's the JSON response:
{
"items": [
{
"id": 1,
"name": "Sample 1"
},
{
"id": 2,
"name": "Sample 2"
}
]
}
I expect the debugPrint to print something similar to that JSON, but instead it just prints unknown
If I debugPrint response.data by itself, it appears to be encoded...
Optional(<7b226461 7461223a 5b7b2269 64223a36 2c226e61 6d6522......
Then my results["items"].arrayObject! line has this error:
fatal error: unexpectedly found nil while unwrapping an Optional value
Rather than grabbing response.data, I'd suggest grabbing response.result.value. When you do responseJSON, Alamofire does the JSON parsing for you, and you should feel free to just grab this parsed object.
Alamofire.request(.GET, url)
.responseJSON { response in
if let value = response.result.value {
let json = JSON(value)
self.delegate?.didReceiveAPIResults(json)
}
}