Here is my operate order:
1: fetch data from the servers
2: update UI
3: save data to realm
So I have an issue: When I fetch the data again, if the results contain the same data like before, So I don't want to save it to realm again. How can I solve it?
You should create a primary key for your class like
class Foo: Object {
dynamic var yourPrimaryKey = 0
dynamic var otherProperty1 = ""
// and so on
override class func primaryKey() -> String? {
return "yourPrimaryKey"
}
}
Then when you save data
let foo = Foo()
//set properties for foo
realm.add(foo, update: true)
The documentation says:
parameter update: If true, the Realm will try to find an existing copy of the object (with the same primary key), and update it.
Otherwise, the object will be added.
Related
I have a data coming from webservice. I have same models made in my project. To demonstrate the model let me show a little idea of my model
Model1:
class Standard {
var Id = 0
var standardName = ""
var students : [StudentModel] = nil
}
Model2:
class StudentModel {
var Id = 0
var stdName = ""
var Teacher: [TeacherModel] = nil
}
Model3:
class TeacherModel {
var Id = 0
var Name = ""
}
Now what I am facing is as follow :
I have TeacherModel in DB already, but I dont have student model and standard model instance int he Realm, so it is supposed to save coming student and standard data in Realm. and skip or update TeacherModel in Realm. But right now it is crashing on TeachModel data as one Teacher with same things are already saved in Realm.SO i am looking forward to some sort of method in which it update or just skip saving Item if already exist in the Realm.
Note: These models are just to demonstrate my case, where as I know there are many typos and other thing. Also I did not showed any implementation of Realm over my Models. Its just to show you the things to make you understand.
Well you need to have class func primaryKey() -> String? overriden.
override class func primaryKey() -> String? {
return "Id"
}
And then use realm.write(...) or realm.create(...) functions with update parameter set to true.
I am new to realm and iOS development so I apologize in advance if something isn’t explained properly or is just incorrect.
I have 2 Realm Object classes:
class Category: Object {
#objc dynamic var name: String = ""
#objc dynamic var color: String = ""
let trackers = List<Tracker>()
}
and
class Tracker: Object {
#objc dynamic var timeSegment: Int = 0
var parentCategory = LinkingObjects(fromType: Category.self, property:
"trackers")
}
I’m able to store new timeSegment properties consistently; however, the issue is that I cannot retrieve & display a collection of timeSegment values relating to their parentCategory. setting
var entries : Results<Tracker>?
produces all results for every category, which is the only result i'm able to pull so far after testing.
Any help is appreciated, and can follow up with any additional details. Thanks
You need to call objects on your Realm object with a filter for fetching only results that match a predicate. The realm object in this code is an instance of the Realm class.
func getTrackersWithName(_ name: String) -> Results<Tracker> {
return realm.objects(Tracker.self).filter("name = \"\(name)\"")
}
This tells Realm to fetch all objects that match the filter predicate. In this case, the filter predicate matches any object where the value of the "name" property matches the string that is passed into the method.
I'm experiencing trouble understanding how updating objects works in Realm. I'd appreciate help in helping me to understand how updating nested objects work and why it doesn't work the way I expect it.
I started using Realm just recently, and here's what I want to use it for: I have a set of key value pairs stored on my server, that serve as localized values for strings used in my iOS app. On app launch every now and then I want to update my strings, so I pull them from the server and store them locally in realm on my iOS device. I want to have only ONE instance of those strings on my device.
Here are the classes:
import RealmSwift
public class LocalizedStrings: Object {
dynamic var id = 1
dynamic var version: String = ""
let assets = List<LocalizedString>()
override public static func primaryKey() -> String? {
return "id"
}
}
public class LocalizedString: Object {
dynamic var key: String = ""
dynamic var value: String = ""
}
Here's how I update the LocalizedStrings object:
realm.add(localizedStrings, update: true)
Here's how I access my strings:
func getLocalizedString(forKey key: String) -> String {
var result = key
try! realm.write {
let queryResult = realm.objects(LocalizedString.self).filter("key == %#", key)
// print(queryResult)
if queryResult.count == 1 {
result = queryResult[0].value(forKey: "value") as! String
}
}
return result
}
Now, I would expect, that whenever I update my LocalizedStrings, that the localizedStrings.assets list would get updated with new values. But instead, the assets are not updated, the list reference gets updated and I end up having multiple instances of the same string, which is not what I would expect from an update function. When I try to access a particular LocalizedString, it turns out there's multiple instances:
(...)
[19] LocalizedString {
key = update;
value = Update;
},
[20] LocalizedString {
key = update;
value = Update;
}
Perhaps I'm missing something obvious and I would really appreciate if someone could point me in the right direction, so I'd be able to achieve the behavior I'm looking for (which would be having the nested object actually updated, rather than having unnecessary duplicates of my objects).
Thanks!
Ok, so this answer helped me figure out, what was wrong with my setup. I was missing primaryKey in LocalizedString class.
From the answer above on how realm.add(object, update: true) works:
Documentation :
parameter object: The object to be added to this Realm.
parameter update: If true, the Realm will try to find an existing copy of the object (with the same primary
key), and update it. Otherwise, the object will be added.
So the same thing happens with nested objects. They can not be updated unless they have primaryKey.
want some method like which do update the existing followers, and if not exists do add it to DB and link them to user something like
GETS CRASH OVER append in write block, due to duplicate primary key,
also, it works perfectly if no followers has been added in HKUser Table, once it comes to update it crashes
import UIKit
import RealmSwift
class HKUser: Object{
dynamic var full_name = ""
dynamic var email: String?
dynamic var user_id: String?
let followers = List<HKUser>()
override static func primaryKey() -> String? {
return "user_id"
}
}
I want to update the connection of a user in DB also, so I want to do some thing like
//1. updated the userFollower array with required data
let userFollowers:[HKUser] = []
//2. now need to link it with my user object and update it in db
if let user = realmWrapper.sharedInstance.getUser(forID: id) {
try! realm.write {
//want some method like which do update the existing followers,
//and if not exists do add it to db and link them to user something like
//realm.add(user, update: true)
user.followers.append(contentsOf: followers)
/**********
GETS CRASH OVER HERE,
due to duplicate primary key,
it works perfect if no followers has been added in HKUser Table,
once it comes to update it crashes
**********/
}
}
List<T>.append() method save to the Realm implicitly if the objects are unmanaged. That is why the duplicated primary key exception happens.
To avoid this, you can add or update the unmanaged objects before appending to the List. Then you append the objects to the List.
try! realm.write {
...
realm.add(followers, update: true) // Add or update the objects first
user.followers.append(contentsOf: followers)
...
}
Maybe it's not the case (not enough code in your question to tell) but it looks like you prepare the userFollowers array in step 1 to contain ALL the current followers for that specific user.
If this is the case then you'll end up re-adding all the existing followers not only the new ones, hence the duplicate keys.
I'll try to explain my scenario as short as possible, I have read some comments on Realm GitHub Repo about this issue:
Terminating app due to uncaught exception 'RLMException', reason:
'Can't set primary key property 'id' to existing value 'xxxxxxx'.
Here's my issue:
I got two classes.
Appointment Model Class
import Foundation
import RealmSwift
class Appointment: Object {
dynamic var id = 0
dynamic var user_id: String?
dynamic var profile_id: String?
let mainMeeting = List<Meeting>()
let meetingsWithOtherInfo = List<Meeting>()
override static func primaryKey() -> String? {
return "id"
}
}
Meeting Model Class
import Foundation
import RealmSwift
class Meeting: Object {
dynamic var id = 0
dynamic var name: String?
dynamic var created_at: String?
// other info
dynamic var restaurant_venue: String?
override static func primaryKey() -> String? {
return "id"
}
}
I am fetching the Appointments from my server API like this
for fetchedAppointment in allAppointmentsFromAlamofire {
let existingAppointment: Results<(Appointment)>! = realm.objects(Appointment).filter("id = \(fetchedAppointment["id"]!)")
let newAppointment: Appointment = Appointment()
newAppointment.id = fetchedAppointment["id"]! as! Int
....
// add data to Meeting connected to Appointment
let newMeeting = Meeting()
newMeeting.id = fetchedAppointment["meetings"]["id"]! as! Int
...
// update or add new entry
try! realm.write {
print("NEW APPOINTMENT: \(newAppointment)")
realm.add(newAppointment, update: existingAppointment.count == 0 ? false : true)
}
}
The error comes out whenever the program is trying to update existing entry in realm - whenever the existingAppointment is 1. the workaround here, from what I've read from Github Realm is to delete the override static func primaryKey() in Meeting Class.
There is no issue if I am just adding new entries to Appointment, but again, the issue comes out if I will be updating, and the issue goes away if I remove the primaryKey() in Meeting Class ---- BUT, in other screens of my app, I really need to have that primaryKey() in Meeting Class.
My wild guess here is that every time that I need to update entries in Appointment, I should update too the Meeting.
So, the question is: why is this happening? Is my wild guess correct? Any other way to solve this?
It looks like you are trying to update the newAppointment object with a value that is not its primary key.
realm.add(newAppointment, update: existingAppointment.count == 0 ? false : true)
Instead, Realm is expecting you to provide the key for that object so that it can update the specified object.
It looks like you are setting the key value here, which is what you should use for your update.
let newAppointment: Appointment = Appointment()
newAppointment.id = fetchedAppointment["id"]! as! Int
Realm documentation on updating with keys.
You don't need to set the update argument of add(:_, update: _) to false if you provide a new object. If your model has a primary key and you want to create or update, you can pass true and Realm will automatically figure out whether an object with the same primary key is already managed in the database or not.