How can I store a Dictionary with RealmSwift?
Solution 1
Dictionary
is not supported as property type in Realm.
You'd need to introduce a new class, whose objects describe each a key-value-pair and to-many relationship to that as seen below:
class Person: Object {
dynamic var name = ""
let hobbies = List<Hobby>()
}
class Hobby: Object {
dynamic var name = ""
dynamic var descriptionText = ""
}
For deserialization, you'd need to map your dictionary structure in your JSON to Hobby objects and assign the key and value to the appropriate property.
Solution 2
I am currently emulating this by exposing an ignored Dictionary property on my model, backed by a private, persisted NSData which encapsulates a JSON representation of the dictionary:
class Model: Object {
private dynamic var dictionaryData: NSData?
var dictionary: [String: String] {
get {
guard let dictionaryData = dictionaryData else {
return [String: String]()
}
do {
let dict = try NSJSONSerialization.JSONObjectWithData(dictionaryData, options: []) as? [String: String]
return dict!
} catch {
return [String: String]()
}
}
set {
do {
let data = try NSJSONSerialization.dataWithJSONObject(newValue, options: [])
dictionaryData = data
} catch {
dictionaryData = nil
}
}
}
override static func ignoredProperties() -> [String] {
return ["dictionary"]
}
}
It might not be the most efficient way but it allows me to keep using Unbox to quickly and easily map the incoming JSON data to my local Realm model.
Solution 3
I would save the dictionary as JSON string in Realm. Then retrive the JSON and convert to dictionary. Use below extensions.
extension String{
func dictionaryValue() -> [String: AnyObject]
{
if let data = self.data(using: String.Encoding.utf8) {
do {
let json = try JSONSerialization.jsonObject(with: data, options: JSONSerialization.ReadingOptions.allowFragments) as? [String: AnyObject]
return json!
} catch {
print("Error converting to JSON")
}
}
return NSDictionary() as! [String : AnyObject]
} }
and
extension NSDictionary{
func JsonString() -> String
{
do{
let jsonData: Data = try JSONSerialization.data(withJSONObject: self, options: .prettyPrinted)
return String.init(data: jsonData, encoding: .utf8)!
}
catch
{
return "error converting"
}
}
}
Solution 4
UPDATE 2021
Since Realm 10.8.0, it is possible to store a dictionary in a Realm object using the Map
type.
Example from the official documentation:
class Dog: Object {
@objc dynamic var name = ""
@objc dynamic var currentCity = ""
// Map of city name -> favorite park in that city
let favoriteParksByCity = Map<String, String>()
}
Solution 5
Perhaps a little inefficient, but works for me (example dictionary from Int->String, analogous for your example):
class DictObj: Object {
var dict : [Int:String] {
get {
if _keys.isEmpty {return [:]} // Empty dict = default; change to other if desired
else {
var ret : [Int:String] = [:];
Array(0..<(_keys.count)).map{ ret[_keys[$0].val] = _values[$0].val };
return ret;
}
}
set {
_keys.removeAll()
_values.removeAll()
_keys.appendContentsOf(newValue.keys.map({ IntObj(value: [$0]) }))
_values.appendContentsOf(newValue.values.map({ StringObj(value: [$0]) }))
}
}
var _keys = List<IntObj>();
var _values = List<StringObj>();
override static func ignoredProperties() -> [String] {
return ["dict"];
}
}
Realm can't store a List of Strings/Ints because these aren't objects, so make "fake objects":
class IntObj: Object {
dynamic var val : Int = 0;
}
class StringObj: Object {
dynamic var val : String = "";
}
Inspired by another answer here on stack overflow for storing arrays similarly (post is eluding me currently)...
Related videos on Youtube
Comments
-
gabuchan over 1 year
Considering the following model:
class Person: Object { dynamic var name = "" let hobbies = Dictionary<String, String>() }
I'm trying to stock in Realm an object of type
[String:String]
that I got from an Alamofire request but can't sincehobbies
has to to be defined throughlet
according to RealmSwift Documentation since it is aList<T>
/Dictionary<T,U>
kind of type.let hobbiesToStore: [String:String] // populate hobbiestoStore let person = Person() person.hobbies = hobbiesToStore
I also tried to redefine
init()
but always ended up with a fatal error or else.How can I simply copy or initialize a Dictionary in RealSwift? Am I missing something trivial here?
-
gabuchan over 8 yearsThanks! I've thought of this solution as well (since it's the cleanest one) but it's just really frustrating not to be able to use any Swift structures in RealmSwift... (not even tuples :( ). As my data is really static and simple, I ended up merging the two strings together with a delimiter and created a single
List<String>
. -
marius over 8 yearsThere are limitations which prevent us from being able to support any generic Swift structures especially tuples. Among them are that we must be able to figure out the type at runtime and be able to return the value by a dynamic accessor. That doesn't work with tuples.
-
marius almost 8 yearsPlease be aware of the performance impact by the additional JSON (de-)serialization and that you loose the capability to query on the dictionary in that way.
-
boliva almost 8 yearshi @marius, of course. This is a workaround and, as I said, not the most efficient way to do it, but it works for the cases where I need to have a dictionary reference on my Realm (which I don't really need to query). Hopefully we'll get to see native support for dictionaries at some point, in which case this won't be needed anymore.
-
jcpennypincher almost 6 yearsGreat solution.!
-
jcpennypincher over 5 yearsThis is the simplest solution and makes storing a dictionary in Realm easy.
-
Peter Lapisu almost 5 yearsis slow as hell too, would use keyarchiver
-
Andrew Kochulab almost 4 yearsWhat you do if you have a value with time Int or Double, etc? The best solution will be to use the Data object and JSONSerialization.
-
Coconuts almost 3 yearsUPDATE 2021: the
Map
type is now supported. Please see my answer below. -
Ufuk Köşker over 2 yearsHow Can I decode / encode Map ? I got error when I write Map<String , String>:
Type 'QuestionList' does not conform to protocol 'Decodable' || Type 'QuestionList' does not conform to protocol 'Encodable'