How to get json response using alamofire in iOS Swift? - ios

I have been trying to get json response from url using alamofire. Created model, apirouter and api client class.
its shows error
failure(Alamofire.AFError.responseSerializationFailed(reason:
Alamofire.AFError.ResponseSerializationFailureReason.decodingFailed(error:
Swift.DecodingError.keyNotFound(CodingKeys(stringValue: "variables", intValue: nil), Swift.DecodingError.Context(codingPath: [_JSONKey(stringValue: "Index 0", intValue: 0)], debugDescription: "No value associated with key CodingKeys(stringValue: \"variables\", intValue: nil) (\"variables\").", underlyingError: nil)))))
Here is my postman json response:
[
{
"id": "00602c70-fc8a-11e9-ad1d-2abe2670111d",
"resourceType": "Task",
"name": "My Tasks",
"owner": null,
"query": {
"assigneeExpression": "${currentUser()}",
"taskVariables": [],
"processVariables": [],
"caseInstanceVariables": [],
"orQueries": []
},
"properties": {
"variables": [
{
"name": "loanAmount",
"label": "Loan Amount"
},
{
"name": "firstName",
"label": "First Name"
}
],
"color": "#555555",
"showUndefinedVariable": false,
"description": "Tasks assigned to me",
"refresh": false,
"priority": -10
}
}
]
My trying get values for id, name and properties -> variables -> name and label from json response.
Here is model class:
import Foundation
public struct Filter: Codable {
let id: String
let name: String
let properties: [variables]
}
public struct variables: Codable {
let name: String
let label: String
}
Here is code for alamofire :
private static func performRequest<T:Decodable>(route:APIRouter, decoder: JSONDecoder = JSONDecoder(), completion:#escaping (AFResult<T>)->Void) -> DataRequest {
return AF.request(route)
.responseDecodable (decoder: decoder){ (response: AFDataResponse<T>) in
completion(response.result)
print("framework response::",response.result)
}
}
public static func getFilter(completion:#escaping (AFResult<[Filter]>)->Void) {
let jsonDecoder = JSONDecoder()
performRequest(route: APIRouter.getFilter, decoder: jsonDecoder, completion: completion)
}
Any help much appreciates pls...

your model class should be like the below.
import Foundation
public struct Filter: Codable {
let id: String
let name: String
let properties: Properties
}
public struct Properties: Codable {
let variables: [variables]
let color: String
let showUndefinedVariable: Bool
let description: String
let refresh: Bool
let priority: Int
}
public struct variables: Codable {
let name: String
let label: String
}

Did you try this one? This is what it should be
public struct Filter: Codable {
let id: String
let name: String
let properties: Property
}
public struct Property: Codable {
let variables: [Variable]
}
public struct Variable: Codable {
let name: String
let label: String
}

The error message you're getting is pretty clear: No value associated with key CodingKeys(stringValue: \"variables\"
You're attempting to decode the JSON into the Filter struct, but the JSON has no variables property. You could fix this by introducing a new Properties struct that will wrap the variables property, like this:
struct Filter: Codable {
let id: String
let name: String
let properties: Properties
}
struct Properties: Codable {
let variables: Variables
}
struct Variables: Codable {
let name: String
let label: String
}
Also, as you'll see in this snippet, it's convention to make the write the type names in CamelCase, so struct Variables instead of struct variables.

Related

The data couldn’t be read because it isn’t in the correct format (swift)

I want to take weather informations with JSON but there is an error : The data couldn’t be read because it isn’t in the correct format.
Error 'It looks like your post is mostly code; please add some more details.' in stackoverflow. Although I briefly describe my problem, it still expects an explanation from me :/
override func viewDidLoad() {
super.viewDidLoad()
let url = "https://api.openweathermap.org/data/2.5/weather?q=bursa,tr&appid=00f63a1cff271776651468c0204c422c"
getData(from: url)
}
private func getData (from url : String){
let task = URLSession.shared.dataTask(with: URL(string: url)!, completionHandler: { data , response , error in
guard let data = data , error == nil else {
print ("birşeyler ters gitti")
return
}
var main : Response?
do {
main = try JSONDecoder().decode(Response.self , from: data)
} catch{
print ("ERROR IS HERE!!! \(error.localizedDescription)")
}
guard let json = main else {
return
}
print (json.weather)
})
task.resume()
}}
struct Response : Codable {
let weather : myResult
let status : String
}
struct myResult : Codable {
let main : String
let description : String
let icon : String
}
API Response is like that :
{"coord": { "lon": 139,"lat": 35},
"weather": [
{
"id": 800,
"main": "Clear",
"description": "clear sky",
"icon": "01n"
}
],
"base": "stations",
"main": {
"temp": 281.52,
"feels_like": 278.99,
"temp_min": 280.15,
"temp_max": 283.71,
"pressure": 1016,
"humidity": 93
},
"wind": {
"speed": 0.47,
"deg": 107.538
},
"clouds": {
"all": 2
},
"dt": 1560350192,
"sys": {
"type": 3,
"id": 2019346,
"message": 0.0065,
"country": "JP",
"sunrise": 1560281377,
"sunset": 1560333478
},
"timezone": 32400,
"id": 1851632,
"name": "Shuzenji",
"cod": 200
}
First, error.localizedDescription is meant to display an information for the user. It's not useful for debugging. If you replace it with error:
} catch {
print ("ERROR IS HERE!!! \(error)") // <- remove .localizedDescription
}
you will get more details:
ERROR IS HERE!!! typeMismatch(Swift.Dictionary<Swift.String, Any>,
Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue:
"weather", intValue: nil)], debugDescription: "Expected to decode
Dictionary<String, Any> but found an array instead.", underlyingError:
nil))
To solve this you need to declare weather as an array:
let weather: [myResult]
I'd also recommend replacing myResult with Weather (or at least capitalised MyResult) as it will be more readable:
struct Weather: Codable {
let main: String
let description: String
let icon: String
}
Also, in the JSON response you provided there is no status field so you may need to remove it from the Response class (or make it optional).
If you'd like to add more fields to the response, declare them according to your JSON structure. Eg. if you want to add humidity and temperature you can do:
struct Response: Codable {
...
let main: Main
}
struct Main: Codable {
let temp: Double
let humidity: Double
}
To have a more readable code you can use CodingKeys - then your variable names can be independent from JSON variables.
struct Main: Codable {
enum CodingKeys: String, CodingKey {
case temperature = "temp"
case humidity
}
let temperature: Double
let humidity: Double
}
Summing up, your Response may look like this:
struct Response: Codable {
let weather: [Weather]
let main: Main
// alternatively declare `status` optional
// let status: String?
}
struct Weather: Codable {
let main: String
let description: String
let icon: String
}
struct Main: Codable {
enum CodingKeys: String, CodingKey {
case temperature = "temp"
case humidity
}
let temperature: Double
let humidity: Double
}

How to pass Codable object in a associatedtype in swift

I have below data from server
{
"success": true,
"terms": "https://currencylayer.com/terms",
"privacy": "https://currencylayer.com/privacy",
"timestamp": 1572365346,
"source": "USD",
"quotes": {
"USDBAM": 1.76456,
"USDTJS": 9.694558,
"USDYER": 250.349994,
"USDZAR": 14.592897,
.......
}
}
Now I have created following to handle above response using Codable
struct Resource<T: Decodable>: ItemList {
var quotes: Dictionary<String, Double>
var success: Bool
var terms: String
var privacy: String
var timestamp: Int
var source: String
}
protocol ItemList: Decodable {
associatedtype T:
var success: Bool {get}
var terms: String {get}
var privacy: String {get}
var timestamp: Int {get}
var source: String {get}
var quotes: T {get}
}
Everything works fine but I want 'quotes' as separate model so I created another struct as below
struct CurrencyModel: Decodable {
var quotes: Dictionary<String, Double>
}
and mofified 'Resource' as below
struct Resource<T: Decodable>: ItemList {
var quotes: T
var success: Bool
var terms: String
var privacy: String
var timestamp: Int
var source: String
}
and call
CurrencyLayerCommunicator().get(Resource<CurrencyModel>.self) { [weak self] item in
self?.handleResult(item)
}
but this time I am unable to get result & getting decoding error
Failed to convert into JSON with error: The data couldn’t be read
because it is missing.
Please suggest or guide how to fix this issue.
The biggest mistake is to print only error.localizedDescription and not error.
With this:
do {
let decoded = try JSONDecoder().decode(Resource<CurrencyModel>.self, from: jsonData)
print(decoded)
} catch {
print("Localized Description Error: \(error.localizedDescription)")
print("Full Error: \(error)")
}
You'll have:
Localized Description Error: The data couldn’t be read because it is missing.
Full Error: keyNotFound(CodingKeys(stringValue: "quotes", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "quotes", intValue: nil)], debugDescription: "No value associated with key CodingKeys(stringValue: \"quotes\", intValue: nil) (\"quotes\").", underlyingError: nil))
Much more useful.
The issue then, is that you have twice "quotes".
One as a key in ItemList, and another one in CurrencyModel.
Replace Resource<CurrencyModel>.self with Resource<[String: Double]>.self and it's working.
In case of JSON Decoding, I also recommend when it's not working in order to debug is to print the reverse version (I replaced Decodable by Codable in your structs):
let currencyModel = CurrencyModel(quotes: ["Currency1": 0.1,
"Currency2": 0.2,
"Currency3": 0.3])
let ressources: Resource<CurrencyModel> = Resource(quotes: currencyModel,
success: true,
terms: "terms",
privacy: "privacy",
timestamp: 30,
source: "source")
do {
let encoder = JSONEncoder()
encoder.outputFormatting = .prettyPrinted
let reversedJSONData = try encoder.encode(ressources)
if let reversedJSONString = String(data: reversedJSONData, encoding: .utf8) {
print(reversedJSONString)
}
} catch {
print("Error in reversing: \(error)")
}
This prints
{
"success" : true,
"quotes" : {
"quotes" : {
"Currency1" : 0.10000000000000001,
"Currency2" : 0.20000000000000001,
"Currency3" : 0.29999999999999999
}
},
"terms" : "terms",
"privacy" : "privacy",
"timestamp" : 30,
"source" : "source"
}
This way you'll see what's supposed to look like your JSON with your current code.
Note that if there is custom init, you'll need to have changes to reflect it.
In the JSON the quotes is a dictionary, you are trying to map that to CurrencyModel. It also expects a key 'quotes' and value as dictionary. In simple way to put what I said, your current implementation expects a JSON like:
{
"success":true,
"terms":"https://currencylayer.com/terms",
"privacy":"https://currencylayer.com/privacy",
"timestamp":1572365346,
"source":"USD",
"quotes":{
"quotes":{
"USDBAM":1.76456,
"USDTJS":9.694558,
"USDYER":250.349994,
"USDZAR":14.592897
}
}
}
Instead of creating a struct for CurrencyModel, just use typealias.
typealias CurrencyModel = [String: Double]
To solve this I think you need one more custom model inside CurrencyModel that holds the key/value pairs of the dictionary. Also to make this work CurrencyModel needs a custom init(from:)
struct FxRate {
let fxPair: String
let rate: Double
}
struct CurrencyModel: Decodable {
var fxRates = [FxRate]()
init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
let dict = try container.decode([String: Double].self)
fxRates = dict.map { FxRate(fxPair: $0.key, rate: $0.value)}
}
}

Use Codable And Decodable in API Call

I'm trying to call API using Codable and i want to access all dictionary, arrays from API.
Is This Possible from codable?
API Response eg:
{
"status": true,
"logo": "https://abc.png",
"data": [
{
"crumb": {
"Menu": {
"navigate": "Home",
},
},
"path": "2",
"type": "type0",
"orientation": [
{
"name": "All",
}
],
},
]
}
The API response you've posted is invalid JSON (it has a bunch of trailing commas that make it illegal). This needs to be changed on the producer's side, and when you've done that, you can use this struct to access the data:
struct Entry: Codable {
let status: Bool
let logo: String
let data: [Datum]
}
struct Datum: Codable {
let crumb: Crumb
let path, type: String
let orientation: [Orientation]
}
struct Crumb: Codable {
let menu: Menu
enum CodingKeys: String, CodingKey {
case menu = "Menu"
}
}
struct Menu: Codable {
let navigate: String
}
struct Orientation: Codable {
let name: String
}

Parsing the JSON iOS-Swift

I have a JSON response that I am parsing with the help of codable models.
Even though my models look good, I am getting the following error:
[Result]: FAILURE: keyNotFound(CodingKeys(stringValue: “user”, intValue: nil), Swift.DecodingError.Context(codingPath: [], debugDescription: “No value associated with key CodingKeys(stringValue: \“user\“, intValue: nil) (\“user\“).“, underlyingError: nil))
The JSON response is:
{
“status”: “success”,
“message”: “successfully.“,
“user”: {
“username”: “admin”,
“profileImage”: “/storage/default.png”
},
“data”: {
“cash”: {
“withdrawableCash”: “$999540”,
“outstandingOrders”: “$0”
},
“offering”: [
{
“company”: “ABCD”,
“location”: “San Francisco, CA”,
“amount”: 10
}
],
“history”: [
{
“amount”: 330,
“order_id”: 3,
“order_type”: “preBid”,
“status”: 2,
“message”: “Placed bid with 33 notes, on Auction,
“transaction_time”: “31-07-2018 05:31"
}
]
}
}
The models are:
public struct WalletResponseModel: Codable {
public let status: String
public let message: String
public let user: UserData
public let data: WalletData
}
public struct UserData: Codable {
public let username: String
public let profileImage: URL
enum CodingKeys: String, CodingKey {
case username
case profileImage = "profileImage"
}
}
public struct WalletData: Codable {
public let cash: Cash
public let history: [HistoryItem]
public let offerings: [Offering]
enum CodingKeys: String, CodingKey {
case cash
case history
case offerings = "offering"
}
}
public struct Cash: Codable {
public let withdrawableCash: String
public let outstandingOrders: String
}
public struct HistoryItem: Codable {
public let amount: Int
public let status: Int
public let orderId: Int
public let orderType: String
public let message: String
public let transactionTime: String
enum CodingKeys: String, CodingKey {
case amount, status, message
case transactionTime = "transaction_time"
case orderId = "order_id"
case orderType = "order_type"
}
}
public struct Offering: Codable {
public let company: String
public let amount: Int
public let location: String
}
Your json format is incorrect it contains “ ” instead of " " , the valid one is
{
"status": "success",
"message": "successfully.",
"user": {
"username": "admin",
"profileImage": "/storage/default.png"
},
"data": {
"cash": {
"withdrawableCash": "$999540",
"outstandingOrders": "$0"
},
"offering": [{
"company": "ABCD",
"location": "San Francisco, CA",
"amount": 10
}],
"history": [{
"amount": 330,
"order_id": 3,
"order_type": "preBid",
"status": 2,
"message": "Placed bid with 33 notes, on Auction",
"transaction_time": "31-07-2018 05:31"
}]
}
}

How to Decode Nested dictionary with nested arrays using Swift Decodable property?

The actual JSON,that I need to parse in swift4 is,
{
"class": {
"semester1": [
{
"name": "Kal"
},
{
"name": "Jack"
},
{
"name": "Igor"
}
],
"subjects": [
"English",
"Maths"
]
},
"location": {
"Dept": [
"EnglishDept",
],
"BlockNo": 1000
},
"statusTracker": {
"googleFormsURL": "beacon.datazoom.io",
"totalCount": 3000
}
}
The code that I'd tried but failed to execute is,
struct Class: Decodable {
let semester: [internalComponents]
let location: [location]
let statusTracker: [statusTracker]
enum CodingKeys: String, CodingKey {
case semester = "semester1"
case location = "location"
case statusTracker = "statusTracker"
}
}
struct location: Decodable {
let Dept: [typesSubIn]
let BlockNo: Int
}
struct statusTracker: Decodable {
let googleFormsURL: URL
let totalCount: Int
}
struct internalComponents: Decodable {
let semester1: [semsIn]
let subjects: [subjectsIn]
}
struct semsIn: Decodable {
let nameIn: String
}
struct subjectsIn: Decodable {
let subjects: String
}
struct Dept: Decodable {
let Depts: String
}
I know it's completely wrong can someone give the actual format? I'm actually confused with the format for "subjects".It's not compiling as a whole too.
There are many issues.
You are making a common mistake by ignoring the root object partially.
Please take a look at the JSON: On the top level there are 3 keys class, location and statusTracker. The values for all 3 keys are dictionaries, there are no arrays.
Since class (lowercase) is a reserved word, I'm using components. By the way please conform to the naming convention that struct names start with a capital letter.
struct Root : Decodable {
let components : Class
let location: Location
let statusTracker: StatusTracker
enum CodingKeys: String, CodingKey { case components = "class", location, statusTracker }
}
There are many other problems. Here a consolidated version of the other structs
struct Class: Decodable {
let semester1: [SemsIn]
let subjects : [String]
}
struct Location: Decodable {
let dept : [String]
let blockNo : Int
enum CodingKeys: String, CodingKey { case dept = "Dept", blockNo = "BlockNo" }
}
struct SemsIn: Decodable {
let name: String
}
struct StatusTracker: Decodable {
let googleFormsURL: String // URL is no benefit
let totalCount: Int
}
Now decode Root
do {
let result = try decoder.decode(Root.self, from: data)
} catch { print(error) }
It looks like you sterilize Class object in wrong way. It should looks like:
struct Class: Decodable {
let class: [internalComponents]
let location: [location]
let statusTracker: [statusTracker]
}
There are a few things here causing your issue.
You have no top level item, I added Response struct
Location, class and statusTracker are both at the same level, not under class.
In your class struct, your items are set as arrays but they aren't arrays
To debug these types of issues, wrap your decode in a do catch block and print out the error. it will tell you the reason it failed to parse
Try this code from my playground:
let jsonData = """
{
"class": {
"semester1": [{
"name": "Kal"
}, {
"name": "Jack"
}, {
"name": "Igor"
}],
"subjects": [
"English",
"Maths"
]
},
"location": {
"Dept": [
"EnglishDept"
],
"BlockNo": 1000
},
"statusTracker": {
"googleFormsURL": "beacon.datazoom.io",
"totalCount": 3000
}
}
""".data(using: .utf8)!
struct Response: Decodable {
let cls: Class
let location: Location
let statusTracker: statusTracker
enum CodingKeys: String, CodingKey {
case cls = "class"
case location
case statusTracker
}
}
struct Class: Decodable {
let semester: [SemesterStudents]
let subjects: [String]
enum CodingKeys: String, CodingKey {
case semester = "semester1"
case subjects
}
}
struct Location: Decodable {
let dept: [String]
let blockNo: Int
enum CodingKeys: String, CodingKey {
case dept = "Dept"
case blockNo = "BlockNo"
}
}
struct statusTracker: Decodable {
let googleFormsURL: URL
let totalCount: Int
}
struct SemesterStudents: Decodable {
let name: String
}
struct Dept: Decodable {
let Depts: String
}
do {
let result = try JSONDecoder().decode(Response.self, from: jsonData)
print(result)
} catch let error {
print(error)
}
Another approach is to create an intermediate model that closely matches the JSON, let Swift generate the methods to decode it, and then pick off the pieces that you want in your final data model:
// snake_case to match the JSON
fileprivate struct RawServerResponse: Decodable {
struct User: Decodable {
var user_name: String
var real_info: UserRealInfo
}
struct UserRealInfo: Decodable {
var full_name: String
}
struct Review: Decodable {
var count: Int
}
var id: Int
var user: User
var reviews_count: [Review]
}
struct ServerResponse: Decodable {
var id: String
var username: String
var fullName: String
var reviewCount: Int
init(from decoder: Decoder) throws {
let rawResponse = try RawServerResponse(from: decoder)
// Now you can pick items that are important to your data model,
// conveniently decoded into a Swift structure
id = String(rawResponse.id)
username = rawResponse.user.user_name
fullName = rawResponse.user.real_info.full_name
reviewCount = rawResponse.reviews_count.first!.count
}
}

Resources