How to set attribute of member object of a realm model? - swift

I have a realm object called DiscoverUserInfo:
class DiscoverUserInfo: Object , Mappable{
dynamic var UserObject:User?
dynamic var ConnectionStatus:Int = -1
var PreviousMeetings = List<Meeting>()
required convenience init?(map: Map) {
self.init()
}
override class func primaryKey() -> String? { return "UserObject.UserId" }
}
Now for this, I want to set a primary key which is UserId of UserObject.
But when I run this code, I get this error:
Terminating app due to uncaught exception 'RLMException', reason:
'Primary key property 'UserObject.UserId' does not exist on object
'DiscoverUserInfo''

You cannot set a primary key using a property of a dynamic variable. You'll have to do something like this:
class DiscoverUserInfo: Object , Mappable{
dynamic var UserObject: User?
dynamic var id = ""
dynamic var ConnectionStatus:Int = -1
var PreviousMeetings = List<Meeting>()
required convenience init?(map: Map) {
self.init()
}
override class func primaryKey() -> String? {
return "id"
}
}
and then set the id to the associated UserObject's UserId each time you create a new DiscoverUserInfo object.
This is related to the issue of having no native support for compound primary keys in Realm. However, we expect to see this feature down the road.

Related

How I can get new unique id for each new object in Realm?

I try create objects in Realm with unique id. I use this code:
class Persons: Object {
#objc dynamic var id = 0
#objc dynamic var name = ""
override static func primaryKey() -> String? {
return "id"
}
}
and in my StorageManager I use this code:
import RealmSwift
let realm = try! Realm()
class StorageManager {
static func saveObject(_ person: Persons) {
try! realm.write {
realm.add(person)
}
}
static func deleteObject(_ person: Persons) {
try! realm.write {
realm.delete(person)
}
}
}
But when I add second new object, I get error:
Terminating app due to uncaught exception 'RLMException', reason:
'Attempting to create an object of type 'Persons' with an existing
primary key value '0'.'
How I can get new unique id for each new object?
Your best bet is to let your code define that for you
class PersonClass: Object {
#objc dynamic var id = UUID().uuidString
#objc dynamic var name = ""
override static func primaryKey() -> String? {
return "id"
}
}
UUID().uuidString returns a unique string of characters which are perfect for primary keys.
HOWEVER
That won't work for Realm Sync in the future. MongoDB Realm is the new product and objects need to have a specific primary key property called _id which should be populated with the Realm ObjectID Property. So this is better if you want future compatibility
class PersonClass: Object {
#objc dynamic var _id = ObjectId()
#objc dynamic var name = ""
override static func primaryKey() -> String? {
return "_id"
}
}
You can read more about it in the MongoDB Realm Getting Started Guide
Note that after RealmSwift 10.10.0, the following can be used to auto-generate the objectId
#Persisted(primaryKey: true) var _id: ObjectId
Keeping in mind that if #Persisted is used on an object then all of the Realm managed properties need to be defined with #Persisted
Note if using #Persisted then the override static func primaryKey() function is not needed
The better answer since Realm v10 came out is to use the dedicated new objectID class
A 12-byte (probably) unique object identifier.
ObjectIds are similar to a GUID or a UUID, and can be used to uniquely identify objects without a centralized ID generator. An ObjectID consists of:
A 4 byte timestamp measuring the creation time of the ObjectId in seconds since the Unix epoch.
A 5 byte random value
A 3 byte counter, initialized to a random value.
ObjectIds are intended to be fast to generate. Sorting by an ObjectId field will typically result in the objects being sorted in creation order.

Case insensitive primary key realm swift

I have an object with the following structure:
class REObject:Object {
dynamic var id = ""
dynamic var status = ""
override static func primaryKey() -> String? {
return "id"
}
}
The flow is: I get an array of items from the BE, the user can enter the object's id and change the status.
And the question is: how could get the item with case-insensitive id?
if let item = realm.object(ofType: REObject.self, forPrimaryKey: id) {
return item
}
Override isSameObjectAs in your class and compare lowercase (or uppercase) versions of id there

Modifying Realm List tries to recreate linked Objects

I have the following (simplified) Realm Models:
final class Item: Object {
#objc dynamic var identifier: String = ""
#objc dynamic var programSet: ProgramSet?
override static func primaryKey() -> String? {
return "identifier"
}
}
final class ProgramSet: Object {
#objc dynamic var identifier: String = ""
#objc dynamic var editorialCategory: EditorialCategory?
override static func primaryKey() -> String? {
return "identifier"
}
}
final public class EditorialCategory: Object {
#objc dynamic var identifier: String = ""
override public static func primaryKey() -> String? {
return "identifier"
}
}
final class Playlist: Object {
#objc dynamic var id = UUID().uuidString
#objc dynamic var name = ""
#objc dynamic var created = Date()
var items = List<Item>()
override static func primaryKey() -> String? {
return "id"
}
}
When the user adds an item to a playlist, I try to append the ItemModel to the Playlist.items List:
try realm.write {
playlistToModify.items.append(item)
}
My problem is that it's possible for Items to share the same EditorialCategory, so it may already be in the database. When I add other items via the Realm.add(_:update:) method, Realm won't try to recreate the linked objects if one with the same primary key exists.
The List.append(_:) method seems to force recreating the linked objects of an ItemModel when called. The app crashes in the following line from object_accessor.hpp:
throw std::logic_error(util::format("
Attempting to create an object of type '%1' with an existing primary key value '%2'.",
object_schema.name, ctx.print(*primary_value)));
Stacktrace:
The object_schema.name is "EditorialCategoryModel", which is linked in ProgramSetModel, which itself is linked from ItemModel.
Is there a way to avoid this error and append the item to the list without Realm attempting to re-create every linked object? Thanks!

Realm Primary key with OR operator

I am using RealmSwift for my new app. My Realm class has two primary keys.
Just an example I have a Realm Model(Product) like this:-
class Product: Object, Mappable {
dynamic var id: String? = nil
dynamic var tempId: String? = nil
dynamic var name: String? = nil
dynamic var price: Float = 0.0
dynamic var purchaseDate: Date? = nil
required convenience init?(map: Map) {
self.init()
}
//I want to do something like this
override static func primaryKey() -> String? {
return "id" or "tempId"
}
func mapping(map: Map) {
id <- map["_id"]
tempId <- map["tempId"]
name <- map["name"]
price <- map["price"]
purchaseDate <- (map["purchaseDate"], DateFormatTransform())
}
So I am creating an realm object in my device and storing into realm db with primary key tempId, as the actual primary key is the id, which is a server generated primary key is coming only after the report sync. So when I am sending multiple reports to the server with those tempId server response me back with actual id mapped with each tempId. As the report is not only created from my side so I can't keep tempId as the primary key. I thought of Compound primary key but it won't solve the problem.
So I want to create a primary key such as If id is there then that is the primary key else tempId is the primary key.
How to do this?
What you need essentially is a computed property as a primary key. However, this isn't supported at the moment, only stored and managed realm properties can be used as primary keys. A workaround could be to define both id and tempId to have explicit setter functions and inside the setter function you also need to set another stored property, which will be your primary key.
If you want to change id or tempId don't do it in the usual way, but do it through their setter function.
Idea taken from this GitHub issue.
class Product: Object {
dynamic var id:String? = nil
dynamic var tempId: String? = nil
func setId(id: String?) {
self.id = id
compoundKey = compoundKeyValue()
}
func setTempId(tempId: String?) {
self.tempId = tempId
compoundKey = compoundKeyValue()
}
dynamic var compoundKey: String = ""
override static func primaryKey() -> String? {
return "compoundKey"
}
func compoundKeyValue() -> String {
if let id = id {
return id
} else if let tempId = tempId {
return tempId
}
return ""
}
}
dynamic private var compoundKey: String = ""
required convenience init?(map: Map) {
self.init()
if let firstValue = map.JSON["firstValue"] as? String,
let secondValue = map.JSON["secondValue"] as? Int {
compoundKey = firstValue + "|someStringToDistinguish|" + "\(secondValue)"
}
}

Compound key in Realm with lazy property

I found this great solution for using Realm with compound primary key in Swift: https://github.com/realm/realm-cocoa/issues/1192
public final class Card: Object {
public dynamic var id = 0 {
didSet {
compoundKey = compoundKeyValue()
}
}
public dynamic var type = "" {
didSet {
compoundKey = compoundKeyValue()
}
}
public dynamic lazy var compoundKey: String = self.compoundKeyValue()
public override static func primaryKey() -> String? {
return "compoundKey"
}
private func compoundKeyValue() -> String {
return "\(id)-\(type)"
}
}
But I discovered that Realm does not support lazy properties, in my case I receive this error:
exception NSException * name: "RLMException" - reason: "Lazy managed property 'compoundKey' is not allowed on a Realm Swift object class. Either add the property to the ignored properties list or make it non-lazy." 0x00007f8a05108060
Is it still possible to have compound key without lazy property?
The solution you found is outdated. I'll leave a note about that there. I'd suggest removing the lazy modifier and initializing compoundKey to an empty string.