25

I've setup a REST API to realm object in iOS. However I've found an issue with creating a favorite flag in my object. I've created a favorite bool, however everytime the object is updated from the API it sets the favorite to default false again. Here I want this flag to not be updated, since the favorite only is stored locally. How can I achieve this?

class Pet: Object{
    dynamic var id: Int = 1
    dynamic var title: String = ""
    dynamic var type: String = ""
    dynamic var favorite: Bool = false


    override class func primaryKey() -> String {
        return "id"
    }
}

CreateOrUpdate

let pet = Pet()
pet.id = 2
pet.name = "Dog"
pet.type = "German Shephard"


try! realm.write {
    realm.add(pet, update: true)
}
Carl Veazey
  • 18,392
  • 8
  • 66
  • 81
Peter Pik
  • 11,023
  • 19
  • 84
  • 142
  • Are you saying that you do not want to save `favorite` in Realm? If so then remove `dynamic` as dynamic properties gets saved in Realm. – Abhinav Oct 01 '15 at 15:37
  • i'm saying that the `favorite` is not stored in the remote database, so everytime open the app and it add new objects and update existing based on primary key `id` it will set the favorite value to the default (`false`). Can i avoid it from updating to default? – Peter Pik Oct 01 '15 at 15:48
  • This is odd. I would have assumed that the `createOrUpdate` methods in Realm wouldn't touch the properties that aren't specified in the dictionary. Can you please include the sample code where you're actually saving/updating the API data to Realm? – TiM Oct 01 '15 at 16:24
  • i've added it now. lets say that i for instance change favorite when a user add a `pet` to favorite. then when i update the object from values in my rest api it will change favorite to default – Peter Pik Oct 01 '15 at 16:31

3 Answers3

22

There are two ways to solve this:

1. Use an Ignored Property:

You can tell Realm that a certain property should not be persisted. To prevent that your favorite property will be persisted by Realm you have to do this:

class Pet: Object{
    dynamic var id: Int = 1
    dynamic var title: String = ""
    dynamic var type: String = ""
    dynamic var favorite: Bool = false

    override class func primaryKey() -> String {
        return "id"
    }

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

or you could

2. Do a partial update

Or you could tell Realm explicitly which properties should be updated when you update your Pet object:

try! realm.write {
  realm.create(Pet.self, value: ["id": 2, "name": "Dog", "type": "German Shepard"], update: true)
}

This way the favorite property will not be changed.

Conclusion

There is one big difference between the two approaches:

Ignored Property: Realm won't store the favorite property at all. It is your responsibility to keep track of them.

Partial Update: Realm will store the 'favorite' property, but it won't be updated.

I suppose that the partial updates are what you need for your purpose.

joern
  • 27,354
  • 7
  • 90
  • 105
  • In my case isFavorite should be saved, but not updated after getting response from server, second solution is also not possible since the response is coming as an array and I am converting the whole array to objects using codable – Johnykutty Jul 03 '19 at 12:06
22

If you want to be more explicit, there is third option:

3. Retrieve the current value for the update

// Using the add/update method
let pet = Pet()
pet.id = 2
pet.name = "Dog"
pet.type = "German Shephard"

if let currentObject = realm.object(ofType: Pet.self, forPrimaryKey: 2) {
    pet.favorite = currentObject.favorite
}

try! realm.write {
    realm.add(pet, update: true)
}

// Using the create/update method
var favorite = false
if let currentObject = realm.object(ofType: Pet.self, forPrimaryKey: 2) {
    favorite = currentObject.favorite
}

// Other properties on the pet, such as a list will remain unchanged
try! realm.write {
    realm.create(Pet.self, value: ["id": 2, "name": "Dog", "type": "German Shephard", "favorite": favorite], update: true)
}
Adam Fish
  • 1,543
  • 9
  • 11
  • 2
    I find this option much more viable than the ones in the accepted answer. – Tiago Veloso Oct 07 '15 at 15:42
  • 1
    found this to be the best working solution. Solution 1 doesn't add the object, and solution 2 seems like a messy work around. Thanks for this third option! – SoundShock Jul 13 '16 at 08:59
  • 2
    How is this possible with inner objects? For example, if I save an object with a list and have to intercept saving for every object in the list? – Anton Shkurenko Jun 11 '17 at 20:03
  • You can also partially update objects with primary keys by passing just a subset of the values you wish to update, along with the primary key. I updated the example to show this. – Adam Fish Jun 13 '17 at 05:25
  • Want to second @AntonShkurenko's question, how can we intercept and update each nested object? – claramelon Mar 01 '18 at 00:31
  • Like @claramelon, I would also like to know how to intercept and update nested objects – jfredsilva Feb 22 '19 at 17:24
1

4. NSUserDefaults (or any other data store, really)

I ran into the same issue and I opted for the other, more traditional option of saving things to another data store (NSUserDefaults). In my case, I was storing the last time a user viewed an item and storing this data in NSUserDefaults felt appropriate. I did something like the following:

First, define a unique key for the object you are storing (self here is the model object being viewed and rendered):

- (NSString *)lastViewedDateKey {
    // Note each item gets a unique key with <className>_<itemId> guaranteeing us uniqueness
    return [NSString stringWithFormat:@"%@_%ld", self.class.className, (long)self.itemId];
}

Then, when a user views the item, set the key to:

- (void)setLastViewedToNow {
    NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
    [userDefaults setObject:[NSDate date] forKey:self.lastViewedDateKey];
    [userDefaults synchronize];
}

Later, I use it like this:

NSUserDefaults *userDefaults = [NSUserDefaults standardUserDefaults];
NSDate *createdOnDate = <passed in from elsewhere>;
NSDate *lastViewedDate = [userDefaults objectForKey:self.lastViewedDateKey];
if (!lastViewedDate || [createdOnDate compare:lastViewedDate] == NSOrderedDescending) {
    ...
}

As opposed to the above solutions: 1. There is no data persistence. 2. This creates yet another place where one has to define the properties of an object and will likely lead to errors if one forgets to update this list every time a new property gets added. 3. If you are doing any large batch updates, going back through every object is not really practical and will certainly cause pain down the road.

I hope this gives someone another option if they need it.

salil
  • 407
  • 3
  • 14