Swift 4.1 JSON to Struct then to array of distinct dates - json

I am calling an API that returns a JSON result of appointments. Example:
[{"DiarySummary":"Test appointment","DiaryDescription":"Test","DiaryId":"62","EventNo":"","EventTypeId":"","StartDateTime":"07/06/2018 09:00:51","StopDateTime":"07/06/2018 21:00:51","DayHalfDay":"0","AgentGroupName":"DEV","AgentInitials":"AC","AgentId":"5","OwnerShortName":"","OwnerLongName":"","AbsenceTypeDescription":"Working From Home","AbsenceTypeId":"15"}...
I have this mapping to a struct in my code using the decode function:
struct DiaryAppointment: Decodable {
let DiarySummary: String?
let DiaryDescription: String?
let DiaryId: String?
let EventNo: String?
let EventTypeId: String?
let StartDateTime: String?
let StopDateTime: String?
let DayHalfDay: String?
let AgentGroupName: String?
let AgentInitials: String?
let AgentId: String?
let OwnerShortName: String?
let OwnerLongName: String?
let AbsenceTypeDescription: String?
let AbsenceTypeId: String?
}
self.appointments = try JSONDecoder().decode([DiaryAppointment].self, from: data!)
and then the self.appointments array is being used in a UICollectionView. All of this works absolutely fine. However, I would like to split the collectionView into sections based on distinct StartDates with a header fro each.
I have tried creating a secondary array, but the value of StartDateTime in my JSON and therefore in my appointments array is a string as opposed to a date. I can loop through the appointments array and convert the value of appointment.StartDateTime to a date object and add to a new array which should allow me to pick out distinct dates, but I won't be able to split the original appointments array because the value is still a string.
Is it possible convert this to a date object at the point off calling decode? If not, how can I achieve the required functionality, or, is there a better way to do this?

First of all use CodingKeys to get lowercased variable names and declare the properties as much non-optional as possible:
struct DiaryAppointment: Decodable {
private enum CodingKeys: String, CodingKey {
case diarySummary = "DiarySummary", diaryDescription = "DiaryDescription", diaryId = "DiaryId"
case eventNo = "EventNo", eventTypeId = "EventTypeId", startDateTime = "StartDateTime"
case stopDateTime = "StopDateTime", dayHalfDay = "DayHalfDay", agentGroupName = "AgentGroupName"
case agentInitials = "AgentInitials", agentId = "AgentId", ownerShortName = "OwnerShortName"
case ownerLongName = "OwnerLongName", absenceTypeDescription = "AbsenceTypeDescription", absenceTypeId = "AbsenceTypeId"
}
let diarySummary, diaryDescription, diaryId, eventNo, eventTypeId: String
let startDateTime, stopDateTime: Date
let dayHalfDay, agentGroupName, agentInitials, agentId: String
let ownerShortName, ownerLongName, absenceTypeDescription, absenceTypeId: String
}
Declare startDateTime and stopDateTime as Date and pass a DateFormatter as dateDecodingStrategy
let decoder = JSONDecoder()
let dateFormatter = DateFormatter()
dateFormatter.locale = Locale(identifier: "en_US_POSIX")
dateFormatter.dateFormat = "dd/MM/yyyy HH:mm:ss"
decoder.dateDecodingStrategy = .formatted(dateFormatter)

Related

Swift Large Data File Into Struct (JSON or CSV)

I've got a huge amount of data (80 columns over 60k rows). It's created in Excel and exported to CSV. The CSV file is 9.4mb. I then converted the CSV to JSON and it became 94.5mb. Obviously I can't have a 100mb data file included with the app so I tried thinking of different solutions.
I used the CSV itself and parsed it out into a struct. However, because the CSV has commas within some of the data (surrounded by double quotes of course), it isn't parsing correctly.
I could reformat the JSON to have the headers in one array and the data in another which would solve my comma issue, but I haven't found an up-to-date real-world solution to doing this.
This is medical drug data so some of the names can be quite long/complex, and a lot of rows have blank values for various fields simply because there is no corresponding data for that entry.
Here's a small example of the CSV formatted data:
medID,type,nameLong,isCurrent,nameShort,isCompound,strength,mUnit,strengthPer,mUnitPer,strengthIn,mUnitIn,ing1,ing1Strength,ing1MUnit,ing2,ing2Strength,ing2MUnit,ing3,ing3Strength,ing3MUnit,ing4,ing4Strength,ing4MUnit,ing5,ing5Strength,ing5MUnit,ing6,ing6Strength,ing6MUnit,ing7,ing7Strength,ing7MUnit,ing8,ing8Strength,ing8MUnit,ing9,ing9Strength,ing9MUnit,ing10,ing10Strength,ing10MUnit,ing11,ing11Strength,ing11MUnit,ing12,ing12Strength,ing12MUnit,ing13,ing13Strength,ing13MUnit,ing14,ing14Strength,ing14MUnit,ing15,ing15Strength,ing15MUnit,ing16,ing16Strength,ing16MUnit,ing17,ing17Strength,ing17MUnit,ing18,ing18Strength,ing18MUnit,ing19,ing19Strength,ing19MUnit,ing20,ing20Strength,ing20MUnit,ing21,ing21Strength,ing21MUnit,ing22,ing22Strength,ing22MUnit,ing23,ing23Strength,ing23MUnit
123456,Inhalant,"Fluticasone Propionate/Salmeterol 500/50 mcg/INHAL Dry Powder Inhaler, 14 Blisters, Inhalant",N,,TRUE,,,,,,,Fluticasone Propionate,500,mcg/INHAL,Salmeterol,50,mcg/INHAL,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
234567,Inhalant,"Advair Diskus 500/50 mcg/INHAL Dry Powder Inhaler, 14 Blisters, Inhalant",N,Advair Diskus,TRUE,,,,,,,Fluticasone Propionate,500,mcg/INHAL,Salmeterol,50,mcg/INHAL,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
345678,Powder,"Fluticasone Propionate 500 mcg / Salmeterol 50 mcg (Salmeterol Xinafoate 72.5 mcg) Per Actuat Dry Powder Inhaler, 28 Actuat",O,,TRUE,,,,,,,Fluticasone Propionate,500,mcg,Salmeterol,50,mcg,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
456789,Powder,"Advair Diskus 500/50 (Fluticasone Propionate 0.5 mg/ACTUAT / Salmeterol 0.05 mg/ACTUAT) Dry Powder Inhaler, 28 Actuat",O,,TRUE,,,,,,,Fluticasone Propionate,0.5,mg/ACTUAT,Salmeterol,0.05,mg/ACTUAT,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
567890,Inhalant,"Fluticasone Propionate/Salmeterol 500/50 mcg/INHAL Dry Powder Inhaler, 60 Blisters, Inhalant",N,,TRUE,,,,,,,Fluticasone Propionate,500,mcg/INHAL,Salmeterol,50,mcg/INHAL,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
678901,Inhalant,"Advair Diskus 500/50 mcg/INHAL Dry Powder Inhaler, 60 Blisters, Inhalant",N,Advair Diskus,TRUE,,,,,,,Fluticasone Propionate,500,mcg/INHAL,Salmeterol,50,mcg/INHAL,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,,
What I need is a solution to getting all of this data into the app so it can be accessed in the fastest manner. As it will only be used in one section of the app, I wasn't sure if putting it all in CoreData was a good idea (plus, it will be constantly updated), and instead thought it best to load it where it's needed and then unload it once the user leaves that section of the app, thus freeing up memory. However, I'm open to the best solution.
This is where I'm at right now with parsing the CSV file directly (and it works, sort of).
struct DrugsList: Codable, Hashable {
let medID: String
let type: String
let nameLong: String
let isCurrent: String
let nameShort: String?
let isCompound: String
let strength: String?
let mUnit: String?
let strengthPer: String?
let mUnitPer: String?
let strengthIn: String?
let mUnitIn: String?
let ing1: String?
let ing1Strength: String?
let ing1MUnit: String?
let ing2: String?
let ing2Strength: String?
let ing2MUnit: String?
let ing3: String?
let ing3Strength: String?
let ing3MUnit: String?
let ing4: String?
let ing4Strength: String?
let ing4MUnit: String?
let ing5: String?
let ing5Strength: String?
let ing5MUnit: String?
let ing6: String?
let ing6Strength: String?
let ing6MUnit: String?
let ing7: String?
let ing7Strength: String?
let ing7MUnit: String?
let ing8: String?
let ing8Strength: String?
let ing8MUnit: String?
let ing9: String?
let ing9Strength: String?
let ing9MUnit: String?
let ing10: String?
let ing10Strength: String?
let ing10MUnit: String?
let ing11: String?
let ing11Strength: String?
let ing11MUnit: String?
let ing12: String?
let ing12Strength: String?
let ing12MUnit: String?
let ing13: String?
let ing13Strength: String?
let ing13MUnit: String?
let ing14: String?
let ing14Strength: String?
let ing14MUnit: String?
let ing15: String?
let ing15Strength: String?
let ing15MUnit: String?
let ing16: String?
let ing16Strength: String?
let ing16MUnit: String?
let ing17: String?
let ing17Strength: String?
let ing17MUnit: String?
let ing18: String?
let ing18Strength: String?
let ing18MUnit: String?
let ing19: String?
let ing19Strength: String?
let ing19MUnit: String?
let ing20: String?
let ing20Strength: String?
let ing20MUnit: String?
let ing21: String?
let ing21Strength: String?
let ing21MUnit: String?
let ing22: String?
let ing22Strength: String?
let ing22MUnit: String?
let ing23: String?
let ing23Strength: String?
let ing23MUnit: String?
init(raw: [String]) {
medID = raw[0]
type = raw[1]
nameLong = raw[2]
isCurrent = raw[3]
nameShort = raw[4]
isCompound = raw[5]
strength = raw[6]
mUnit = raw[7]
strengthPer = raw[8]
mUnitPer = raw[9]
strengthIn = raw[10]
mUnitIn = raw[11]
ing1 = raw[12]
ing1Strength = raw[13]
ing1MUnit = raw[14]
ing2 = raw[15]
ing2Strength = raw[16]
ing2MUnit = raw[17]
ing3 = raw[18]
ing3Strength = raw[19]
ing3MUnit = raw[20]
ing4 = raw[21]
ing4Strength = raw[22]
ing4MUnit = raw[23]
ing5 = raw[24]
ing5Strength = raw[25]
ing5MUnit = raw[26]
ing6 = raw[27]
ing6Strength = raw[28]
ing6MUnit = raw[29]
ing7 = raw[30]
ing7Strength = raw[31]
ing7MUnit = raw[32]
ing8 = raw[33]
ing8Strength = raw[34]
ing8MUnit = raw[35]
ing9 = raw[36]
ing9Strength = raw[37]
ing9MUnit = raw[38]
ing10 = raw[39]
ing10Strength = raw[40]
ing10MUnit = raw[41]
ing11 = raw[42]
ing11Strength = raw[43]
ing11MUnit = raw[44]
ing12 = raw[45]
ing12Strength = raw[46]
ing12MUnit = raw[47]
ing13 = raw[48]
ing13Strength = raw[49]
ing13MUnit = raw[50]
ing14 = raw[51]
ing14Strength = raw[52]
ing14MUnit = raw[53]
ing15 = raw[54]
ing15Strength = raw[55]
ing15MUnit = raw[56]
ing16 = raw[57]
ing16Strength = raw[58]
ing16MUnit = raw[59]
ing17 = raw[60]
ing17Strength = raw[61]
ing17MUnit = raw[62]
ing18 = raw[63]
ing18Strength = raw[64]
ing18MUnit = raw[65]
ing19 = raw[66]
ing19Strength = raw[67]
ing19MUnit = raw[68]
ing20 = raw[69]
ing20Strength = raw[70]
ing20MUnit = raw[71]
ing21 = raw[72]
ing21Strength = raw[73]
ing21MUnit = raw[74]
ing22 = raw[75]
ing22Strength = raw[76]
ing22MUnit = raw[77]
ing23 = raw[78]
ing23Strength = raw[79]
ing23MUnit = raw[80]
}
}
func loadDrugsCSV(from fileName: String) -> [DrugsList] {
var csvToStruct = [DrugsList]()
// Locate the CSV File
guard let filePath = Bundle.main.path(forResource: fileName, ofType: "csv") else {
return []
}
// Convert the contents of the CSV file into one long string
var data = ""
do {
data = try String(contentsOfFile: filePath)
} catch {
print(error)
return []
}
// Split the long string into an array of rows
// Detect carriage returns "\n", then split
var rows = data.components(separatedBy: "\n")
// Remove header rows
rows.removeFirst()
// Loop through each row and split
for row in rows {
let csvColumns = row.components(separatedBy: ",")
let drugStruct = DrugsList.init(raw: csvColumns)
csvToStruct.append(drugStruct)
}
return csvToStruct
}

I can't pull Json data while doing MVVM design project with swift

I am making a project in Swift with MVVM design. I want to get coin name, current price, Rank and Symbol from a Crypto site. I can't show the json data I get on the console. The model is in another folder because I did it with MVVM. How can I create a struct to get the data here? You can find screenshots of my project below. I would be glad if you help.
Below are the codes I wrote in my web service file
import Foundation
class WebService {
func downloadCurrencies(url: URL, completion: #escaping ([DataInfo]?) -> ()) {
URLSession.shared.dataTask(with: url) { (data, response, error) in
if let error = error {
print(error.localizedDescription)
completion(nil)
} else if let data = data {
let cryptoList = try? JSONDecoder().decode([DataInfo].self, from: data)
print(cryptoList)
if let cryptoList = cryptoList {
completion(cryptoList)
}
}
}
.resume()
}
}
Below are the codes I wrote in my model file
import Foundation
struct DataInfo : Decodable {
var name: String
var symbol: String
var cmc_rank: String
var usd: Double
}
Finally, here is the code I wrote to print the data in the viewController to my console. But unfortunately I can't pull the data.
override func viewDidLoad() {
super.viewDidLoad()
let url = URL(string: "https://pro-api.coinmarketcap.com/v1/cryptocurrency/listings/latest?start=1&limit=10&convert=USD&CMC_PRO_API_KEY=5ac24b80-27a1-4d01-81bd-f19620533480")!
WebService().downloadCurrencies(url: url) { cryptos in
if let cryptos = cryptos {
print(cryptos)
}
}
}
I've seen your URL and tested it ON Postman and also i've got your code and tried to put it in a shape, the code is good, but it can't MAP JSON Data against your struct because this is the json Data from Postman
Postman Data
While Looking at your Struct, It Doesnt Match the format of JSON Data you're receiving,
Struct
Well, To Make your structure match the JSON String you need to create Nested String: Any Dictionary. But there's another issue with the logic, you need to decode data outside of the webservice call because it can contain errors which wont be mapped in the struct and can handle if you get other statusCode.
If you try to implement all these things manually, the code will become complex and hard to understand. I would rather recommend you to use Alamofire with SwiftyJSON and it can make your work a lot shorter and easier and understandable.
Sorry for the bad english.
Your api-key is not valid for me.
Your data must be inside of object or invalid keys and you are surely missing it thats why it is not parsing correctly.
My suggestion is to put your json response in this website
"https://app.quicktype.io/"
and replace your struct with new one, you will be good to go hopefully.
Your models does not have 1 to 1 correspondence with the response object. The root object is not a [DataInfo], but another structure that contains an array of DataInfos. Here are the correct models
struct Response: Codable {
let status: Status
let data: [CurrencyData]
}
struct Status: Codable {
let creditCount: Int
let elapsed: Int
let timestamp: String
let totalCount: Int
let errorCode: Int?
let errorMessage: String?
let notice: String?
enum CodingKeys: String, CodingKey {
case notice
case timestamp
case elapsed
case creditCount = "credit_count"
case errorCode = "error_code"
case errorMessage = "error_message"
case totalCount = "total_count"
}
}
enum Currency: String, Codable, Hashable {
case usd = "USD"
}
struct CurrencyData: Codable {
let circulatingSupply: Double?
let cmcRank: Int
let dateAdded: String?
let id: Int
let lastUpdated: String?
let maxSupply: Int?
let name: String
let numMarketPairs: Int
let platform: Platform?
let quote: [String: Price]
let selfReportedCirculatingSupply: String?
let selfReportedMarketCap: String?
let slug: String
let symbol: String
let tags: [String]?
let totalSupply: Double
func price(for currency: Currency) -> Double? {
return quote[currency.rawValue]?.price
}
enum CodingKeys: String, CodingKey {
case id
case name
case platform
case quote
case slug
case symbol
case tags
case circulatingSupply = "circulating_supply"
case cmcRank = "cmc_rank"
case dateAdded = "date_added"
case lastUpdated = "last_updated"
case maxSupply = "max_supply"
case selfReportedCirculatingSupply = "self_reported_circulating_supply"
case selfReportedMarketCap = "self_reported_market_cap"
case totalSupply = "total_supply"
case numMarketPairs = "num_market_pairs"
}
}
struct Price: Codable {
let fullyDilutedMarketCap: Double?
let lastUpdated: String?
let marketCap: Double?
let marketCapDominance: Double?
let percentChange1h: Double?
let percentChange24h: Double?
let percentChange30d: Double?
let percentChange60d: Double?
let percentChange7d: Double?
let percentChange90d: Double?
let price: Double?
let volume24h: Double?
let volumeChange24h: Double?
enum CodingKeys: String, CodingKey {
case price
case fullyDilutedMarketCap = "fully_diluted_market_cap"
case lastUpdated = "last_updated"
case marketCap = "market_cap"
case marketCapDominance = "market_cap_dominance"
case percentChange1h = "percent_change_1h"
case percentChange24h = "percent_change_24h"
case percentChange30d = "percent_change_30d"
case percentChange60d = "percent_change_60d"
case percentChange7d = "percent_change_7d"
case percentChange90d = "percent_change_90d"
case volume24h = "volume_24h"
case volumeChange24h = "volume_change_24h"
}
}
struct Platform: Codable {
let id: Int
let name: String
let symbol: String
let slug: String
let tokenAddress: String?
enum CodingKeys: String, CodingKey {
case id
case name
case symbol
case slug
case tokenAddress = "token_address"
}
}
and you can retrieve the cryptoList in your completion handler like this:
let cryptoList = (try? JSONDecoder().decode([Response].self, from: data))?.data
Also it's not safe to expose your personal data to the internet (API_KEY, etc.)

How to JSON encode multiple date formats within the same struct

Need to encode into JSON, a struct that has 2 Date instance variables (day and time), however, I need to encode each date instance variable with a different format, ie. for "day":"yyyy-M-d" and "time":"H:m:s".
Have written a custom decoder which works no problems. But not sure how to write the required custom encoder to solve this.
For example I can decode the following JSON string:
{ "biometrics" : [
{"biometricId":1,"amount":2.1,"source":"Alderaan","day":"2019-1-3","time":"11-3-3","unitId":2},
{"biometricId":10,"amount":3.1,"source":"Endoor","day":"2019-2-4","time":"11-4-4","unitId":20}]
}
However, when I encode it, I can only encode it in a single date format :(
Help, would be greatly appreciated.
Thank you.
import UIKit
let biometricsJson = """
{ "biometrics" : [
{"biometricId":1,"amount":2.1,"source":"Alderaan","day":"2019-1-3","time":"11-3-3","unitId":2},
{"biometricId":10,"amount":3.1,"source":"Endoor","day":"2019-2-4","time":"11-4-4","unitId":20}]
}
"""
struct Biometrics: Codable {
var biometrics: [Biometric]
}
struct Biometric: Codable {
var biometricId: Int
var unitId: Int
var source: String?
var amount: Double
var day: Date
var time: Date
init(biometricId: Int, unitId: Int, source: String, amount: Double, day: Date, time: Date){
self.biometricId = biometricId
self.unitId = unitId
self.source = source
self.amount = amount
self.day = day
self.time = time
}
}
extension Biometric {
static let decoder: JSONDecoder = {
let decoder = JSONDecoder()
decoder.dateDecodingStrategy = .custom { decoder in
let container = try decoder.singleValueContainer()
let dateString = try container.decode(String.self)
let formatter = DateFormatter()
formatter.timeZone = TimeZone.current
formatter.dateFormat = "H:m:s"
if let date = formatter.date(from: dateString) {
return date
}
formatter.dateFormat = "yyyy-M-d"
if let date = formatter.date(from: dateString) {
return date
}
throw DecodingError.dataCorruptedError(in: container,
debugDescription: "Cannot decode date string \(dateString)")
}
return decoder
}()
}
let biometrics = try Biometric.decoder.decode(Biometrics.self, from:biometricsJson.data(using: .utf8)!)
let jsonEncoder = JSONEncoder()
let encodedJson = try jsonEncoder.encode(biometrics)
let jsonString = String(data: encodedJson, encoding: .utf8)
if biometricsJson != jsonString {
print("error: decoding, then encoding does not give the same string")
print("biometricsJson: \(biometricsJson)")
print("jsonString: \(jsonString!)")
}
I expect the encoded JSON, to be decodable by the decoder.
i.e. biometricsJson == jsonString
In a custom encode(to:), just encode each one as a string using the desired formatter. There's no "date" type in JSON; it's just a string. Something along these lines:
enum CodingKeys: CodingKey {
case biometricId, amount, source, day, time, unitId
}
func encode(to encoder: Encoder) throws {
var container = encoder.container(keyedBy: CodingKeys.self)
try container.encode(biometricId, forKey: .biometricId)
try container.encode(unitId, forKey: .unitId)
try container.encode(source, forKey: .source)
try container.encode(amount, forKey: .amount)
let formatter = DateFormatter()
formatter.timeZone = TimeZone.current
formatter.dateFormat = "H:m:s"
let timeString = formatter.string(from: time)
try container.encode(timeString, forKey: .time)
formatter.dateFormat = "yyyy-M-d"
let dayString = formatter.string(from: day)
try container.encode(dayString, forKey: .day)
}
But note that you can't test for equivalent strings. JSON dictionaries aren't order-preserving, so there's no way to guarantee a character-by-character match.
Note that if you really want to have days and times, you should consider DateComponents rather than a Date. A date is a specific instance in time; it's not in any time zone, and it can't be just an hour, minute, and second.
Also, your use of Double is going to cause rounding differences. So 2.1 will be encoded as 2.1000000000000001. If that's a problem, you should use Decimal for amount rather than Double.

How to decode this using JSONDecoder and Swift 4 Decodabale class

I have decoded this using JSONSerialization. But for my own knowledge and the maintenance of my code. I would like to know how to decode this.
This is what I have so far:
let urlString = "site deleted" + "/DataSource/Mobile/?Action=MyProfile&uid=" + uid + "&uuid=" + uuid
guard let url = URL(string: urlString) else {return}
URLSession.shared.dataTask(with: url) { (data, _, error) in
if let err = error {
print("Handle MyProfileJSON error: ", err)
}
guard let data = data else {return}
do {
// swift 4.2 but we cant use it right now
let profile = try JSONDecoder().decode(RequestResult.self, from: data)
print(profile)
completion(profile)
} catch let err {
print("Handle Decoder Error: ", err)
}
}.resume()
I'm not too worried about the cases but this is what I have so far. I know the case I use is not the convention, that's why I did this with JSONSerialization so I can use camelCase. If you can help me convert it to camelCase too that would be amazing but my focus is to Decode this using Decodable class. Thanks a lot, guys.
And this are my structs:
struct RequestResult: Decodable {
var Action: String?
var Params: [String: String]?
var DATA: [String: String]?
}
struct Params: Decodable {
var Action_get: String?
var uid_get: String?
}
struct DATA: Decodable {
var Id: String?
var UserCode: String?
var HomePhone: String?
var Mobile: String?
var WorkPhone: String?
var Email: String?
var AltEmail: String?
var UnitNo: String?
var StreetNo: String?
var StreetName: String?
var City: String?
var StateProvince: String?
var Country: String?
var ZipPostalCode: String?
}
The structure of the JSON is very clear
The root object RequestResult contains a string and two dictionaries.
The dictionaries are replaced by structs.
The CodingKeys are useful to rename the keys to more meaningful and naming convention conforming names. The left side of an enum case is the struct member name, the right side the original JSON key.
A struct member name must match the dictionary key (or the mapped CodingKey).
The struct names are arbitrary.
All struct members can be declared as constants (let) and as non-optional if the JSON contain always the keys.
struct RequestResult: Decodable {
private enum CodingKeys : String, CodingKey {
case action = "Action", params = "Params", data = "DATA"
}
let action: String
let params: Parameter
let data: UserData
}
The dictionary for key Params will be renamed to Parameter and DATA to UserData
struct Parameter: Decodable {
private enum CodingKeys : String, CodingKey {
case action = "Action_get", uid = "uid_get"
}
let action: String
let get: String
}
struct UserData: Decodable {
private enum CodingKeys : String, CodingKey {
case id = "Id", userCode = "UserCode", homePhone = "HomePhone"
case mobile = "Mobile", workPhone = "WorkPhone", email = "Email"
case altEmail = "AltEmail", unitNo = "UnitNo", streetNo = "StreetNo"
case streetName = "StreetName", city = "City", stateProvince = "StateProvince"
case country = "Country", zipPostalCode = "ZipPostalCode"
}
let id: String, userCode, homePhone, mobile: String
let workPhone, email, altEmail, unitNo: String
let streetNo, streetName, city, stateProvince: String
let country, zipPostalCode: String
}

SWIFT 4, Xcode 9, JSON DECODER

I'm pretty stuck now. I'm attempting to PARS a JSON RETURN for just the year make make and model. It's buried in an array of dictionaries, and the decoder is having a hard time pulling them out. What am I doing wrong?
public struct Page: Decodable {
let Count: Int
let Message: String
let SearchCriteria: String
let Results: [car]}
public struct car: Decodable {
let ModelYear: String
let Make: String
let Model: String
let VIN: String}
let session = URLSession.shared
let components = NSURLComponents()
components.scheme = "https"
components.host = "vpic.nhtsa.dot.gov"
components.path = "/api/vehicles/decodevinvaluesextended/\(VIN)"
components.queryItems = [NSURLQueryItem]() as [URLQueryItem]
let queryItem1 = NSURLQueryItem(name: "Format", value: "json")
components.queryItems!.append(queryItem1 as URLQueryItem)
print(components.url!)
let task = session.dataTask(with: components.url!, completionHandler: {(data, response, error) in
guard let data = data else { return }
do
{
//let Result = try JSONDecoder().decode(Page.self, from: data)
// let PageResult = try JSONDecoder().decode(Page.self, from: data)
let json = try JSONDecoder().decode(Page.self, from: data)
let Results = json.Results;
print(Results)
First of all it's highly recommended to conform to the Swift naming convention that variable names start with a lowercase and structs start with a capital letter
public struct Page: Decodable {
private enum CodingKeys : String, CodingKey {
case count = "Count", message = "Message", searchCriteria = "SearchCriteria", cars = "Results"
}
let count: Int
let message: String
let searchCriteria: String
let cars: [Car]
}
public struct Car: Decodable {
private enum CodingKeys : String, CodingKey {
case modelYear = "ModelYear", make = "Make", model = "Model", VIN
}
let modelYear: String
let make: String
let model: String
let VIN: String
}
The cars array is in the variable result. This code prints all values
let result = try JSONDecoder().decode(Page.self, from: data)
for car in result.cars {
print("Make: \(car.make), model: \(car.model), year: \(car.modelYear), VIN: \(car.VIN)")
}