String is not convertible to String:AnyObject - swift

I am trying out the Alamofire helpers for networking with my server. I am building up a router for handling my API endpoints. The construction itself seems clear to me, but I am struggling with some SWIFT syntax.
enum Router:URLRequestConvertible {
static let baseURLString = "url"
case AEDS
var URLRequest: NSURLRequest {
let (path: String, parameters: [String: AnyObject]) = {
switch self {
case .AEDS:
let params = [""]
return("/aeds", 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
}
}
I get the message that inside my case .AEDs the params are throwing an error:
[String] is not convertible to [String: AnyObject]
I am kind of new to Swift and could not figure out so far, where to start. I think I provided the array that I am defining. So what does this error mean?

In your switch case, you need to defines params as a dictionary and not as an array.
switch self {
case .AEDS:
let params = [""] <---- This is initialising an array containing a string
return("/aeds", params)
}
Try changing to:
switch self {
case .AEDS:
let params = ["" : ""] <---- This will create a dict
return("/aeds", params)
}
That should solve your problem.

Related

Swift: Get value from a JSON

I'm totally new with swift, it's my first iOs app
I would like to retrieve a value from an http POST response
struct represCode: Codable{
var CODE: String?
}
var table = [represCode]()
func httpPost(completion: #escaping (_ json: Any?)->()) {
let json: [String: Any] = ["login": usernameText.text!.uppercased(),
"pass": mdpText.text!]
let urlPath = url.chaine + "login.php"
let jsonData = try? JSONSerialization.data(withJSONObject: json)
let url = URL(string: urlPath)!
var request = URLRequest(url: url)
request.httpMethod = "POST"
// insert json data to the request
request.httpBody = jsonData
let task = URLSession.shared.dataTask(with: request) { data, response, error in
guard let data = data, error == nil else {
print(error?.localizedDescription ?? "No data")
return
}
do {
self.table = try JSONDecoder().decode([represCode].self, from: data)
print(self.table)
self.dl = true
}catch _ {
print ("JSON Error")
}
completion(json)
}
task.resume()
}
When I "print(self.table)" I get this
[Mobois.LoginViewController.represCode(CODE: Optional("AG"))]
And I would like to store the "AG" in a specific var (ex: var represCode: String?)
I tried many solutions that I found here but most of time I get errors like "Cannot assign value of type '[LoginViewController.represCode]' to type 'String'"
There are two serious mistakes.
The root object is an array (represented by the [] in [represCode].self)
The value AG is the value for key CODE
First of all to conform to the naming convention declare the struct this way
struct RepresCode: Decodable {
let code: String
private enum CodingKeys: String, CodingKey { case code = "CODE" }
}
and
var table = [RepresCode]()
..
JSONDecoder().decode([RepresCode].self ...
You can access the value by getting the value for property code of the first item in the array
let represCode = table.first?.code ?? "unknown code"

AF no member 'responseDecodable' with router

Please help and explain why the router doesn't have responseDecodable. I made a router for AF request call and one of the endpoint need to send up String: [String: Any]. I'm not sure what I did wrong. Thank you!
AFRouter
enum AFRouter: URLRequestConvertible {
case test([String: [String: Any]])
var base: URL {
return URL(string: "https://example.com")!
}
var method: HTTPMethod {
switch self {
case .test:
return .get
}
var path: String {
switch self {
case .test(_):
return "/v2/test"
}
}
func asURLRequest() throws -> URLRequest {
let urlString = baseURL.appendingPathComponent(path).absoluteString.removingPercentEncoding!
let removeSpace = urlString.replacingOccurrences(of: " ", with: "")
let url = URL(string: removeSpace)
var request = URLRequest(url: url!)
request.method = method
switch self {
case .test(_):
guard let token = defaults.string(forKey: "token") else {
return request
}
request.setValue("Bearer " + token , forHTTPHeaderField: "Authorization")
request = try JSONEncoding.default.encode(request)
return request
}
}
Codable
struct Test: Codable {
let success: String
let message: String
let data: [String]
}
Calling API
func getTest(testInfo: [String: Any]) {
AF.request(AFRouter.test(["Testing": testInfo]).responseDecodable(of: Test.self) { response in //got error here "Value of type 'AFRouter' has no member 'responseDecodable'"
//do something...
})
}
The error is saying that you want to use responseDecodable(of:) on a AFRouter instance.
But, in fact, you want to use it on a DataRequest instance.
But it "should work", so are you calling it on a mistaken instance? if we observe, there is a missing ):
AF.request(AFRouter.test(["Testing": testInfo]).responseDecodable(of:...
=>
AF.request(AFRouter.test(["Testing": testInfo])).responseDecodable(of:...

How to deal with responses from an API which may contain an Array or Dictionary?

I have an API call which checks for the UserLogin and Password and sends a response based on whether the username and password Pairs are correct or not.
The API returns a dictionary if the username-password pair is incorrect and an array of dictionaries if the username-password is correct.
The problem I face is that I am unable to downcast the response from Alamofire to a particular data type.
func afLoginDriver(firstName: String,password:String,completion: #escaping ([Dictionary<String, Any>])->Void){
let driverLoginURL = "URL here.."
let parameters = ["firstName" : firstName, "Password" : password]
AF.request(driverLoginURL, method: .post, parameters: parameters, encoding: JSONEncoding.default, headers: nil).responseJSON { response in
switch response.result {
case .success:
let driverLoginResponse = response.value as? [Dictionary<String, Any>]
completion(driverLoginResponse)
break
case .failure(let error):
print(error)
}
}
}
The variable driverLoginResponse throws up an error if the username-password pairs is incorrect as it is only a dictionary and not an array of dictionary.
I tried using the guard let statement but still was not able to get this to work.
I would use Codable and enum for that but consider this:
First, implement an enum with any possible response:
enum LoginDriverResponseValue {
case dictionary([String: Any])
case array([[String: Any]])
case string(String)
case unknown(Any)
}
then, change the function signature to adapt with that enum:
func afLoginDriver(firstName: String, password: String, completion: #escaping (LoginDriverResponseValue)->Void) {
,,,
}
And lastly, switch on the enum and call corresponding completion:
guard let responseValue = response.value else { return }
switch responseValue {
case let result as [String: Any]: completion(.dictionary(result))
case let result as [[String: Any]]: completion(.array(result))
case let result as String: completion(.string(result))
default: completion(.unknown(responseValue))
}
- More encapsulated way:
you can encapsulate responseValue type detection into the enum LoginDriverResponseValue:
extension LoginDriverResponseValue {
init(responseValue: Any) {
switch responseValue {
case let result as [String: Any]: self = .dictionary(result)
case let result as [[String: Any]]: self = .array(result)
case let result as String: self = .string(result)
default: self = .unknown(responseValue)
}
}
}
So then the only thing you need in the function will be:
guard let responseValue = response.value else { return }
completion(LoginDriverResponseValue(responseValue: responseValue))
Cast the response values to the potential Types, and handle each:
if let responseOne = response.value as? [String: Any] {
// ...
} else if let responseTwo = response.value as? [[String: Any]] {
// ...
}
You could use Codable:
struct Response: Codable {
var valueOne: [String: Any]?
var valueTwo: [[String: Any]]?
}

Access a dictionary (JSON format) in a function with a flexible variable

I can't find a solution for my programming issue. I want to create a function which will access a dictionary (data is coming from the internet) an I need the following code very often:
if let job_dict = json["data"] as? [String:Any] {
It would be great to be more flexible and to change the ["data"] part to a variable or something like that:
func get_JSON(Link: String, Value: String) -> [Double] {
let url = Link
let request = URLRequest(url: URL(string: url)!)
let myUrl = URL(string: basePath)!
var ValuestoReturn = [Double]()
let task = URLSession.shared.dataTask(with: myUrl) { (data, response, error) in
if let data = data {
do {
if let json = try JSONSerialization.jsonObject(with: data, options: []) as? [String:Any] {
print(Value.description)
if let job_dict = json[Value.description] as? [String:Any] {
print(job_dict)
}
}
} catch {
}
}
}
task.resume()
json[Value.description] is always wrong and the json["data"] thing is always true.
Don't use Value.Description use just Value
print(Value)
if let job_dict = json[Value] as? [String:Any] {...}
P.D: Don't use "Value" for a variable's name. The first letter in uppercase is for types. You can use value instead.

Alamofire post request sending data as array

I am using a php based web service which accepts post variables. When I run it in postman it works fine but when I run it from Alamofire in my code the web dev says he is receiving the request in an array. Following is the request that server receives:
{"{\"gameDate\":\"2018-03-31_12:43:37\",\"gameFee\":\"55\",\"gameInstruction\":\"\",\"gametitle\":\"ttt\",\"key\":\"AAAA\",\"latitude\":\"\",\"longitude\":\"\",\"numOfPlayers\":20,\"privacy\":0,\"status\":0,\"uid\":\"aaaaa\"}":"","0":""}
Following is how I am sending the request in Alamofire:
Alamofire.request(url, method: .post, parameters: param, encoding: JSONEncoding.default, headers: ["Content-Type":"application/x-www-form-urlencoded"]).responseJSON {
response in
switch response.result {
case .success:
completionHandler(Result.Success(response.result.value))
break
case .failure(let error):
print(error)
completionHandler(Result.Failure(.serverConnectionFailure))
}
}
param:
["key":appKey as AnyObject, "uid":event.uid as AnyObject,"gametitle":event.gameTitle as AnyObject,"gameDate":event.gameDate as AnyObject,"gameFee":event.gameFee as AnyObject,"gameInstruction":event.gameInstruction as AnyObject,"latitude":event.latitude as AnyObject,"longitude":event.longitude as AnyObject,"numOfPlayers":event.numOfPlayers as AnyObject,"privacy":event.privacy as AnyObject, "status":event.status as AnyObject]
How should I convert it to normal key-value request that php service accepts?
Edit:
my postman request that works:
You have to send the params in form of Dictionary(i.e [String: Any]).
To convert class to [String: Any] :-
//Protocol for converting Class to Dictionary
protocol JSONAble {}
extension JSONAble {
func toDict() -> [String : Any] {
var dict = [String : Any]()
let otherSelf = Mirror(reflecting: self)
for child in otherSelf.children {
if let key = child.label {
dict[key] = child.value
}
}
return dict
}
}
//Request Parameters Class
class request : JSONAble {
//MARK:- Properties
var gameDate = String()
var gameFee = String()
var gameInstruction = String()
.
.
//MARK:- Constructor
init(gameDate : String?, gameFee: String?, gameInstruction: String?) {
self.gameDate = gameDate
self. gameFee = gameFee
self. gameInstruction = gameInstruction
}
}
Use:-
var params : [String : Any]?
if let jsonableRequest = request as? JSONAble {
params = jsonableRequest.toDict()
}
=> Send this params as request.