I have a dictionary of values
class Objects {
let values = [
"AAA": ["AAAAAAA", "111111111"],
"BBB": ["BBBBBBBB", "2222222"],
"CCC": ["CCCCCCCC", "3333333333"],
"DDD": ["DDDDDD", "44444444"],
]
}
Which I turn into custom objects and display in a tableview.
struct Object {
var heading : String!
var imageName: String!
}
Then the user can select two objects to store in UserDefaults
let defaults = UserDefaults.standard
func addObject(_ object1: String, object2: String) {
// Get objects for user
var userObjects = fetchObjectsFromUserDefaults()
// Add to user currencies
userObjects.append([object1,object2])
//Update user defaults value for key
// [ [Object1, Object2], [Object1, Object2] ]
defaults.set(userObject, forKey: "userCurrencies")
}
// Gets [[String]] values from user defaults for key
func fetchObjectsFromUserDefaults() -> [[String]] {
if let objects = UserDefaults.standard.value(forKey: "userObjects") {
return objects as! [[String]]
} else {
return []
}
}
// Uses [[String]] values and turns them into objects by using the dictionary to determine property values
func getObject() -> [[Object]] {
let userObject = fetchObjectsFromUserDefaults()
// [ [Object1, Object2], [Object1, Object2] ]
let object = Object()
var fetchedObject = [[Object]]()
if !userObjects.isEmpty {
for c in userObjects {
var set = [Object]()
if let val = object.available[c[0]] {
set.append(Currency(currencyTitle: c[0], imageName: val[0] ))
}
if let val2 = object.available[c[1]] {
set.append(Currency(currencyTitle: c[0], imageName: val2[0] ))
}
if !set.isEmpty {
fetchedObjects.append(set)
}
}
return fetchedObjects
}
return [[]]
}
View Controller
Here I get the objects to load into the TableView
let fetched = dataManager.getObjects
print(fetched)
self.objects = fetched()
However this prints out
(Function)
What am I doing wrong and is their a better method of storing and retrieving this data from user defaults ? I feel this is over kill and there is a swifter and safer approach.
Step 1.
Make your struct Codable
. The compiler will write all of the functions for you if all of the members of your struct
are Codable
and fortunately String
is Codable
so its just:
struct Object: Codable {
var heading : String!
var imageName: String!
}
Step 2.
The problem with Codable
is that it converts to and from Data
, but you want to convert to and from a Dictionary
. Fortunately JSONSerialization converts from Data
to Dictionary
so make a new protocol and give it a default implementation with a protocol extension:
protocol JSONRepresentable {
init?(json: [String: Any])
func json() -> [String: Any]
}
extension JSONRepresentable where Self: Codable {
init?(json: [String:Any]) {
guard let value = (try? JSONSerialization.data(withJSONObject: json, options: []))
.flatMap ({ try? JSONDecoder().decode(Self.self, from: $0) }) else {
return nil
}
self = value
}
func json() -> [String:Any] {
return (try? JSONEncoder().encode(self))
.flatMap { try? JSONSerialization.jsonObject(with: $0, options: []) } as? [String: Any] ?? [:]
}
}
Step 3.
Conform your struct to JSONRepresentable
struct Object: Codable, JSONRepresentable {
var heading : String!
var imageName: String!
}
Step 4.
Place your object into Userdefaults and get it out again:
let o = Object.init(heading: "s", imageName: "a").json()
UserDefaults.standard.set(o, forKey: "test")
print(Object.init(json: UserDefaults.standard.dictionary(forKey: "test") ?? [:]))
Here is the whole playground if you want to try:
import UIKit
struct Object: Codable, JSONRepresentable {
var heading : String!
var imageName: String!
}
protocol JSONRepresentable {
init?(json: [String: Any])
func json() -> [String: Any]
}
extension JSONRepresentable where Self: Codable {
init?(json: [String:Any]) {
guard let value = (try? JSONSerialization.data(withJSONObject: json, options: []))
.flatMap ({ try? JSONDecoder().decode(Self.self, from: $0) }) else {
return nil
}
self = value
}
func json() -> [String:Any] {
return (try? JSONEncoder().encode(self))
.flatMap { try? JSONSerialization.jsonObject(with: $0, options: []) } as? [String: Any] ?? [:]
}
}
let o = Object.init(heading: "s", imageName: "a").json()
UserDefaults.standard.set(o, forKey: "test")
print(Object.init(json: UserDefaults.standard.dictionary(forKey: "test") ?? [:]))