How to properly handle create and update for realm.io relationships - ios

I've got two RLMObjects:
class Timeline: RLMObject {
dynamic var entries = RLMArray(objectClassName: Entry.className())
dynamic var id = 0
dynamic var title = ""
dynamic var coverPhoto = ""
dynamic var body = ""
override class func primaryKey() -> String {
return "id"
}
}
class Entry: RLMObject {
dynamic var id :Int = 0
dynamic var timelineID :Int = 0
dynamic var name :String = ""
dynamic var caption :String = ""
dynamic var body :String = ""
dynamic var imageURL :String = ""
override class func primaryKey() -> String {
return "id"
}
}
As you can see there is a To-Many relationship between a Timeline and an Entry. A timeline has many Entries.
the JSON fetch and assignment of my timelines works just fine, they are being fetched and set with:
realm.beginWriteTransaction()
Timeline.createOrUpdateInDefaultRealmWithObject(timelineObject)
realm.commitWriteTransaction()
My problem arises when I'm trying to fetch the entries (which is a separate JSON request/response) for a given timeline and set them.
Fetching and creating them like this works just fine:
realm.beginWriteTransaction()
Entry.createOrUpdateInDefaultRealmWithObject(entryObject)
realm.commitWriteTransaction()
But of course that doesn't associate them with my Timeline object. So then I tried to get my Timeline object and then adding the Entry object to my timeline by doing:
let timelineObject = Timeline(forPrimaryKey: id)
timelineObject.entries.addObject(entryObject)
(inside of a transaction ofc).
This works fine for the initial fetch. But when I try to refetch your data, you get the following RLMException:
'RLMException', reason: 'Can't set primary key property 'id' to existing value 59.'
Am I doing something wrong, or is this a bug? It seems like there should be an instance method on RLMObject that creates or updates a product, like it's class method does?
Solution
As segiddins comment suggests, you add the return from the class method: createOrUpdate... and then add that return to your object (in my case, the timelineObject.entries.addObject(entryReturn).
let realm = RLMRealm.defaultRealm()
realm.beginWriteTransaction()
for entry in json["entries"] {
...
//Process and map the JSON
...
let entryReturn = Entry.createOrUpdateInDefaultRealmWithObject(entryObject)
timelineObject.entries.addObject(entryReturn)
}
realm.commitWriteTransaction()

Try adding the Entry object that gets returned from the createOrUpdate call.

Related

Realm how to save item if not exist else it should update

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.

Retrieving object properties with relation to their parent categories

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.

Attempting to create an object of type 'Item' with an existing primary key value '1'

I have three classes, Folder, Object & Item.
For every Folder there are a list of objects, and for every Object there should be a list of items. However when creating an Object with a default list of items I am getting the following error;
*** Terminating app due to uncaught exception 'RLMException', reason: 'Attempting to create an object of type 'Item' with an existing
primary key value '1'.'
For each class I have a function that counts all the objects and increments the ID, this works fine but there must be something wrong with how I am adding Item to an Object.items list property.
Item
class Item: Object {
#objc dynamic var id: Int = 0
#objc dynamic var object: Object?
#objc dynamic var title: String = ""
override static func primaryKey() -> String? {
return "id"
}
func IncrementaID() -> Int{
let realm = try! Realm()
var num = 1
let allItems = realm.objects(Milestone.self)
if allItems.count >= 1 {
num = allItems.count + 1
}
return num
}
}
When creating an Object I am also trying creates Item's to append to the objects items property like so
func addObjectToFolder(_ title: String, folder: Folder) {
let object = Object()
object = object.IncrementaID()
object.title = title
let defaultList = ["1", "2", "3", "4", "5"]
for i in defaultList {
let item = Item()
item.id = item.IncrementaID()
item.title = i
object.items.append(item)
}
try! realm.write {
folder.objects.append(object)
}
}
Does an body know how I am going wrong when creating the default values for the Objects.items list ?
The value returned by your IncrementaID function only changes when the number of Milestone objects stored in your Realm changes. Your code contains a loop that creates multiple Item instances and initialized their id property to the value returned by IncrementaID. No Milestone objects are created or stored in the Realm between calls, so all of the Item objects end up with the same ID.
My suggestion: use the string representation of a UUID for your primary key, rather than trying to manage unique integers like this.

Realm Swift iOS - Can't Set Primary Key

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.

How to link two Realm objects

I'm new to iOS development and currently using Realm as database. My first tableview display Restaurant object and second table display customer objects. How can i link this two objects?. Means when i click each restaurant it will display different customer.
class Restaurant: Object {
dynamic var restname: String = ""
dynamic var date: String = ""
}
class Customer: Object {
dynamic var id = 0
dynamic var name: String = ""
dynamic var price: Float = 0.0
dynamic var drinks: Float = 0.0
override static func primaryKey() -> String? {
return "id"
}
}
You make references to your models like so
class Customer: Object {
dynamic var restaurant: Restaurant?
}
You also have the possibility to get reverse relationship with LinkingObjects(fromType:, property:)
You can write in your other model
class Restaurant: Object {
let customers = LinkingObjects(fromType: Customer.self, property: "restaurant")
}
That way you don't duplicate relationships.
If I understand, in Restaurant class put this:
dynamic var _customer = Optional(Customer())
or in Customer class put this line:
dynamic var _restaurant = Optional(Restaurant())
NOTE: Name of variable with lower dash, may be any name, my habit is to put lower dash

Resources