Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Storing an array of strings using Realm's RLMArray

Does anyone know how you can use Realm to store an array of strings? I'm trying to map the following response into Realm correctly:

"zoneInfo": {
    "tariffInfoLines": [
        "In this city you pay per minute."
    ]
}

We have a zoneInfo object that contains a tariffInfoLines array. This tariffInfoLines array contains strings. In Realm there are two different variable types for storing data. The first is RLMObject which allows your standard NSString, int, long etc.

The second type is RLMArray, which is used for arrays (as NSArray is not supported). You have to give the array a type, which must be a class that subclasses RLMObject. We have so far got around this by using a ABCRealmString object, as shown below:

@property RLMArray<ABCRealmString> *tariffInfoLines;

ABCRealmString contains an NSString property (it is basically a wrapper):

@property NSString *value;

However what this means is that when Realm tries to map the response to persist the data, it is looking for a value for the key "value" (the name of the property). It appears that it expects a response similar to the following:

"zoneInfo": {
    "tariffInfoLines": [
        {
            "value": "In this city you pay per minute."
        },
    ]
}

In the project, we have it working for the following structure:

"userOptions": [
    {
        "wantsEmailNotifications": true,
        "wantsPushNotifications": false
    },
]

This has an array, with objects inside that have clear key value pairs that Realm can map to. The zoneInfo structure appears to be the only place that we have an array with sets of values inside without them being inside an object or having any keys.

If anyone could shed some light on this, regarding if this is possible using Realm, or whether an API change is required to match a structure that Realm can map.

like image 453
Jamie Avatar asked Jul 30 '15 13:07

Jamie


3 Answers

UPDATE (most of the previous answers are no longer correct):

You can now store primitive types or their nullable counterparts (more specifically: booleans, integer and floating-point number types, strings, dates, and data) directly within RLMArrays or Lists. If you want to define a list of such primitive values you no longer need to define cumbersome single-field wrapper objects. Instead, you can just store the primitive values themselves.

Lists of primitive values work much the same way as lists containing objects, as the example below demonstrates for Swift:

class Student : Object {
    @objc dynamic var name: String = ""
    let testScores = List<Int>()
}

// Retrieve a student.
let realm = try! Realm()
let bob = realm.objects(Student.self).filter("name = 'Bob'").first!

// Give him a few test scores, and then print his average score.
try! realm.write {
    bob.testScores.removeAll()
    bob.testScores.append(94)
    bob.testScores.append(89)
    bob.testScores.append(96)
}
print("\(bob.testScores.average()!)")   // 93.0

All other languages supported by Realm also supports lists of primitive types.

like image 177
bmunk Avatar answered Oct 18 '22 19:10

bmunk


Cross posting from the github issue response: Although this example demonstrates how to store flat arrays of strings on a Realm model, you can extend this pattern to store anything from arrays of integers to native Swift enum's. Basically anything that you can map to a representable type in Realm.

class RealmString: Object {
    dynamic var stringValue = ""
}

class Person: Object {
    var nicknames: [String] {
        get {
            return _backingNickNames.map { $0.stringValue }
        }
        set {
            _backingNickNames.removeAll()
            _backingNickNames.appendContentsOf(newValue.map({ RealmString(value: [$0]) }))
        }
    }
    let _backingNickNames = List<RealmString>()

    override static func ignoredProperties() -> [String] {
        return ["nicknames"]
    }
}

// Usage...

let realm = try! Realm()
try! realm.write {
    let person = Person()
    person.nicknames = ["John", "Johnny"]
    realm.add(person)
}

for person in realm.objects(Person) {
    print("Person's nicknames: \(person.nicknames)")
}

// Prints:
// Person's nicknames: ["John", "Johnny"]
like image 43
yoshyosh Avatar answered Oct 18 '22 19:10

yoshyosh


For the Swift 3.0 here is the change (in my case the Xcode 8 compiler didn't offer auto fix when i switched to swift 3.0 so I had some pain to resolve it).

_backingNickNames.append(objectsIn: newValue.map { RealmString(value: [$0]) })
like image 3
Pavle Mijatovic Avatar answered Oct 18 '22 18:10

Pavle Mijatovic