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.
Related
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).
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.
I'm trying to access the data downloaded from a website which looks like this:
{"quizzes":[
{"id":1, "question": "Can't solve this :("},
{"id":2, "question": "Someone help pls"}]}
The data downloaded looks way more complex, with more values, more keys, and some keys being associated to another dictionary of Key:String, but since I can't even access the most simple fields I thought I would start with this.
I'm using JSONSerialization, so my question is:
If I want to create a variable where i can save the downloaded data... Which would be it's type? I would say [String:[String:Any]], but I'm not sure if "quizzes" represents a key on this specific key, since the data starts with '{' and not with '['.
Instead of using JSONSerialization one could use JSONDecoder.
Example
struct Question: Decodable {
let id: Int
let question: String
}
struct Quiz: Decodable {
let quizzes: [Question]
}
Assuming jsonStr is a string with the JSON in your question:
if let jsonData = jsonStr.data(using: .utf8) {
if let quiz = try? JSONDecoder().decode(Quiz.self, from: jsonData) {
for question in quiz.quizzes {
print("\(question.id) \"\(question.question)\"")
}
}
}
Output
1 "Can't solve this :("
2 "Someone help pls"
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.
I have a custom class
public class Balance: NSObject {
var details: String
var date: Date
var amount: Double
}
I have tried as a struct and as a class both fail
I have an array of balances list: [Balance]
Now I'm need to convert this array into a JSON String
something like
[ {details = "text"; date = "2016-11-20"; amount = 0;} ,
{details = "text2"; date = "2016-11-25"; amount= 10;} ]
I also need to be able to convert the String back into the array.
But I can't even get the array to JSON string to work
var resStr = ""
var list: [Balance]
do {
let data = try JSONSerialization.data(withJSONObject: list, options: .prettyPrinted)
resStr = String.init(data: data, encoding: .utf8) ?? ""
} catch { fatalError("Error BALANCE HISTORY ") }
Fails with
'Invalid type in JSON write (Balance)'
Please advise
Thanks in advance
What about defining your class like this:
public class Balance: NSObject {
var details: String
var date: Date
var amount: Double
func toJSONString() -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
return "{details = \(self.details); date = \(dateFormatter.string(from: self.date)); amount = \(self.amount);}"
}
}
Then you can create the full JSON string like this:
var fullJSONArray = [String]()
for balance in list {
fullJSONArray.append(balance.toJSONString)
}
let fullJSONString = fullJSONArray.description
I hope this helps you out! Good luck and let me know if you have any further questions or issues!
You can only store a quite small list of data types to JSON.
#CoolPenguin offers one solution - a custom method that will convert your object to a JSON string.
I would advise against building JSON strings yourself.
Instead, I would suggest creating a toDictionary() method for your class, and an init(dictionary:) method to create a Balance object from a dictionary.
You can convert the dates to TimeIntervals since 1970 (the UNIX "epoch date".) That seems easier than converting them to date strings.
You can then map your array of Balance objects to an array of dictionaries, and convert that to JSON using normal JSONSerialization.
let mappedArray = balanceArray.map{$0.toDictionary()}
And then easily convert your array of dictionaries to JSON.
Since others have pointed out that you can achieve the desired effect by first converting your object to a Dictionary, I will provide a method to achieve what is required by using JSONEncoder instead.
If you maintain the values you need to represent in the JSON encoded string (for example the date as a formatted String), the only required bits are,
Make your type conform to Codable
Create a CodingKeys enum that represent the JSON keys for your type's properties.
Please see below for an example that applies to your object.
public class Balance: NSObject, Codable {
var details: String
// Used to represent formatted date.
var dateString: String
var date: Date = Date(){
didSet{
updateDateString()
}
}
var amount: Double
enum CodingKeys: String, CodingKey{
case details, dateString, amount
}
init(_ d: String, amt: Double){
details = d
dateString = ""
date = Date()
amount = amt
super.init()
updateDateString()
}
private func updateDateString(){
let df = DateFormatter()
df.locale = Locale(identifier: "en_US_POSIX")
df.dateFormat = "yyyy-MM-dd"
dateString = df.string(from: date)
}
}
var arr: [Balance] = [
Balance("Shared Balance", amt: 100),
Balance("Coupon Balance", amt: 120),
Balance("Account Balance", amt: 150)
]
do{
let data = try JSONEncoder().encode(arr)
// Convert to JSON Encoded String
let str = String(data: data, encoding: .utf8)!
print(str)
// Convert back from JSON Encoded String
let raw = try JSONDecoder().decode([Balance].self, from: str.data(using: .utf8)!)
print(raw)
}
catch(let err){
print(err)
}
The additional updateDateString() boiler plate code is to produce the String with the date format, "yyyy-MM-dd".
The above code can product a JSON encoded version of your Balance array as follows.
[{"details":"Shared Balance","amount":100,"dateString":"2021-01-14"},
{"details":"Coupon Balance","amount":120,"dateString":"2021-01-14"},
{"details":"Account Balance","amount":150,"dateString":"2021-01-14"}]
The key takeaway is to use JSONEncoder instead of JSONSerialization.
Hope I helped!
Well, I believe first you need to convert your object to some form of dictionary.
Let me show you an example:
class Balance: NSObject {
var details: String
var date: Date
var amount: Double
func asDictionary() -> [String: AnyObject] {
return ["details": details as AnyObject, "date": "\(date)" as AnyObject, "amount": amount as AnyObject]
}
}
You use the method asDictionary to convert your objects to a dictionary so that you can serialize it into JSON.
Suppose you have a list of Balance objects.
You need to first convert each of those objects to dictionary using the method above, and then try to serialize the objects to JSON. Note that the list is now a list of [String: AnyObject] dictionaries, and not a list of Balance objects.
var resStr = ""
var list: [[String: AnyObject]] = [balance1.asDictionary(), balance2.asDictionary()]
do {
let data = try JSONSerialization.data(withJSONObject: list, options: .prettyPrinted)
resStr = String.init(data: data, encoding: .utf8) ?? ""
} catch { fatalError("Error BALANCE HISTORY ") }
For certain types, like the date field, you need to find some way to convert it to String, as the JSONSerializer is very picky. In this case I just used String interpolation, but the format may not be what you want it to be like.
If you want to convert back to a Balance object from JSON, first you need to get JSON into a dictionary, and then check for each field if it exists, and if so, construct your Balance object.
Supposing you have converted your JSON data into a dictionary in the variable named dict, you could do something like the following:
// supposing you have a single object in dict
var balance: Balance
balance.details = dict["details"]
balance.amount = dict["amount"]
balance.date = parseDate(dict["date"])
Supposing you have a function parseDate to parse the date from String into a Date object.
You can take a look here for converting String date into an object: Use SwiftyJSON to deserialize NSDate