Swift 5: getting several values out of JSON with array root object - ios

I (a complete rookie) am currently trying to create my first iOS app - a currency table/converter for the currency of my country, Ukrainian Hryvna. I have created a TableView that I am going to fill with data from JSON file from the following link:
Tap here
The file itself has Array root. Like that:
[
{
"r030":36,"txt":"Австралійський долар","rate":21.334,"cc":"AUD","exchangedate":"23.12.2020"
}
,{
"r030":124,"txt":"Канадський долар","rate":21.9334,"cc":"CAD","exchangedate":"23.12.2020"
}
,{
"r030":156,"txt":"Юань Женьміньбі","rate":4.3192,"cc":"CNY","exchangedate":"23.12.2020"
}
,{
"r030":191,"txt":"Куна","rate":4.5833,"cc":"HRK","exchangedate":"23.12.2020"
}]
I want to get a create a Dictionary out of this file using just two values: [cc: rate] and then fill my TableView with this data. I don't care for other values.
Something like that:
["AUD": 21.334, "CAD": 21.9334]
Should I use some other data type to store this data? A Struct representing a currency and then make an Array of currency Structs, perhaps?
How do I get this file from that URL and make such a Dictionary/Struct array/...?
Thank you so much in advance :)

Make a struct
struct Currency : Decodable {
let name : String
let rate : Double
private enum CodingKeys : String, CodingKey { case name = "cc", rate }
}
Then load the data with URLSession and decode the JSON array to [Currency] with JSONDecoder (there are zillions of examples of both).

Related

Parsing JSON file using Swift

Here is the general structure of a team from our JSON file:
{"Team11":{
"results":{
"leg1":[
{"g":"m","n":"Name1"}
],"leg2":[
{"g":"m","n":"Name2"}
],"leg3":[
{"g":"m","n":"Name3"}
],"leg4":[
{"g":"m","n":"Name4"}
],"leg5":[
{"g":"m","n":"Name5"}
],"leg6":[
{"g":"m","n":"Name6"}
],"leg7":[
{"g":"m","n":"Name7"},{"g":"m","n":"Name8"}
]
},"tn":"TeamName",
"division":"co"
}
}
So far we are able to parse up into results categories leg1, leg2, etc. Accessing the info contained in the bracket arrays has not worked so far.
My current idea on why it is failing is because we are storing the JSON teams incorrectly via String:Any.
My other theory is I just haven't been able to find the correct documentation. Any pointers on where to look or tips would be huge!
Make sure to add What hasn't worked for you and what you have tried? As a New contributor you need to learn how to post questions. Give it a try with my below answer.
Use Codable to parse the JSON like below,
let welcome = try? newJSONDecoder().decode(Welcome.self, from: jsonData)
// Welcome
struct Welcome: Codable {
let team11: Team11
enum CodingKeys: String, CodingKey {
case team11 = "Team11"
}
}
// Team11
struct Team11: Codable {
let results: [String: [Result]]
let tn, division: String
}
// Result
struct Result: Codable {
let g, n: String
}
Note: Your JSON is missing open and ending curly brackets, and I've updated that in your question.
Are you trying to parse the JSON manually? I'm not sure where you're at with your code, but the standard way to parse a JSON string into an object is this:
let jsonData = myJSONString.data(using: .utf8)
There is an issue with your JSON though. You can validate a JSON file on this link.

Can we create variables with '-' like JS in swift?

I have a service that returns this JSON structure "actual-price": {,
I want to know if it is possible to create a variable like JS with Codable on swift.
PS: I can't change the JSON since the service is not mine
You will need to use a CodingKeys enumeration to map the jSON properties to valid Swift properties. Note that once you introduce a CodingKeys enumeration it must contain all of the properties you wish to map, not just the properties where you want to change the name.
Something like
struct MyStruct: Codable {
var actualPrice: Double
var quantity: Int
enum CodingKeys: String, CodingKey {
case actualPrice = "actual-price"
case quantity = "quantity"
}
}

How can one use Swift's Decodable to parse an arbitrary JSON string where you only know or care about a few fields? [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 4 years ago.
Improve this question
The new Swift "Decoder" class sounds like a great way to parse JSON data, but all of the examples I've found use a well-known, well-defined 'struct' to do so.
In my case I'm querying an arbitrary website that returns a HUGE JSON string and I only care about a few of the (deeply nested) fields, so I don't want to take all that time to define a 'struct' to get at them.
Is it even possible to do this with "Decoder"? And if so, how does one go about it?
The question seems to be based on a misapprehension about how Decodable works. As a convenience, Decodable is willing to do some automatic code generation behind the scenes so that you can define a struct or nest of structs and just decode the entirety of the JSON. But you are not required to take advantage of that in order to decode JSON.
There is no need to define struct properties for "fields" you don't care about. If a JSON dictionary contains 100 keys and your corresponding struct contains just one property, no problem; that key will be fetched, and no others.
With regard to the "deeply nested" part, it should not take you much time to write simple nested structs that perform the dive to reach the dictionary you really care about. But if you don't want to do even that, you could write an implementation of init(from:) that dives down and fetches out the desired values.
In other words, if you think of Decodable as consisting primarily of your implementation of init(from:), and learn to write the code that it needs, you will see that this JSON can be parsed in a few quick simple lines of code.
As an example, here's a JSON sketch of a deeply nested piece of information with a bunch of extra information at every level that we want to ignore:
{
"ignore": true,
"outer1": {
"ignore": true,
"outer2": {
"ignore": true,
"outer3": {
"name": "matt",
"ignore": true
}
}
}
}
What I'd like to do is define a very simple struct Person that consists solely of the deeply nested name:
struct Person : Decodable {
let name : String
}
I can do that! To do so, I implement Decodable myself, supplying a "hoover" CodingKey adopter struct and an implementation of init(from:), like this (this may look like a lot of work, but it isn't, because the AnyCodingKey implementation is boilerplate, copied and pasted from here, and the init(coder:) implementation is just a few lines of code that were easy to write):
struct Person : Decodable {
let name : String
struct AnyCodingKey : CodingKey {
var stringValue: String
var intValue: Int?
init(_ codingKey: CodingKey) {
self.stringValue = codingKey.stringValue
self.intValue = codingKey.intValue
}
init(stringValue: String) {
self.stringValue = stringValue
self.intValue = nil
}
init(intValue: Int) {
self.stringValue = String(intValue)
self.intValue = intValue
}
}
init(from decoder: Decoder) throws {
var con = try! decoder.container(keyedBy: AnyCodingKey.self)
con = try! con.nestedContainer(keyedBy: AnyCodingKey.self, forKey: AnyCodingKey(stringValue:"outer1"))
con = try! con.nestedContainer(keyedBy: AnyCodingKey.self, forKey: AnyCodingKey(stringValue:"outer2"))
con = try! con.nestedContainer(keyedBy: AnyCodingKey.self, forKey: AnyCodingKey(stringValue:"outer3"))
let name = try! con.decode(String.self, forKey: AnyCodingKey(stringValue:"name"))
self.name = name
}
}
When I want to dive into the JSON and grab the name information, it's trivial:
let person = try! JSONDecoder().decode(Person.self, from: json)
The result is a Person object with name value "matt". Note that I didn't have to add any of the ignore keys and I didn't need to make a nest of structs.
Sure you can achieve this but with both JSonSerialization & Decodable , you have to serialize the json until reach the desired content then decode it ,but instead I recommend to create structs for root keys only then decode , think of it as it's a path from top to bottom don't decode a key that isn't in the path of your desired content

How to save array of tuples in UserDefaults

I am using a tuple of arrays in one of my files and want to start saving it in UserDefaults. Here is how I am storing my current data:
typealias savemytuple = (Name: String, NameID: String, BookID: String, Picture: NSData)
var savefun = [savemytuple]()
I have researched and found I may need to do something with the following for saving:
NSKeyedArchiver.archivedDataWithRootObject
And this for retrieving:
NSKeyedUnarchiver.unarchiveObjectWithData
Not sure if this way will work as my tuple has multiple data types. Any idea on how to do this?
The values in UserDefaults must be property lists, so you need to convert each tuple to a property list. A Data is a property list, and there are several ways to convert things to Data.
One way is to stop using a tuple and use a struct instead. If all of the struct's stored properties conform to Codable, then you can ask Swift to generate everything needed to make the struct itself conform to Codable just by declaring the conformance. Both String and Data conform to Codable.
Once the struct is Codable, you can convert one of them, or even an array of them, into a property list via JSONEncoder or PropertyListEncoder:
import Foundation
struct Bookie: Codable {
var name: String
var nameId: String
var bookId: String
var picture: Data
}
let bookies = [
Bookie(name: "mbro12", nameId: "id1", bookId: "b1", picture: Data()),
Bookie(name: "mayoff", nameId: "id2", bookId: "b2", picture: Data())
]
let bookiesData = try! PropertyListEncoder().encode(bookies)
UserDefaults.standard.set(bookiesData, forKey: "bookies")
let fetchedData = UserDefaults.standard.data(forKey: "bookies")!
let fetchedBookies = try! PropertyListDecoder().decode([Bookie].self, from: fetchedData)
print(fetchedBookies)
You have to convert the tuples into Dictionary to save it, not really a proper way, rather make your tuple into a custom object, then conform NSCoding protocol for a proper way to convert between data and object.
You don't really need NSKeyedArchiver.archivedDataWithRootObject, just simply convert the tuple into a Dictionary and have another function to convert back to the tuple type.

JSON Dictionary to CoreData

Ok so I'm able to parse my JSON to my Model struct which looks like this:
JSON:
{
"base":"CHF",
"date":"2017-02-09",
"rates":{
"AUD":1.3086,
"BGN":1.8326,
"BRL":3.123,
"CAD":1.3133,
"CNY":6.879,
"CZK":25.32,
"DKK":6.9665,
"GBP":0.79732,
"HKD":7.7729,
"HRK":6.9992,
"HUF":289.31,
"IDR":13280.0,
"ILS":3.7553,
"INR":66.867,
"JPY":112.48,
"KRW":1146.2,
"MXN":20.482,
"MYR":4.4473,
"NOK":8.3265,
"NZD":1.3871,
"PHP":50.008,
"PLN":4.0382,
"RON":4.2115,
"RUB":58.914,
"SEK":8.8863,
"SGD":1.4173,
"THB":35.076,
"TRY":3.7,
"USD":1.0019,
"ZAR":13.435,
"EUR":0.93703
}
}
MODEL:
struct TestStruct {
var base: String
var date: String
var rates: [String: Double]
init(base: String, date: String, rates: [String:Double]) {
self.base = base
self.date = date
self.rates = rates
}
}
But now I have no Idea how I'm going to store my Model in CoreData I know how store the base and date because those are just strings but how can I store a Dictionary or maybe convert it to something because I will need the "rates" Dictionary back from CoreData since I need to know which currency has which exchange rate...
Core Data is so complex that I suggest you search on Google for tutorials. However, I built the proper data model. May it help you.

Resources