How to Set Primary Key in Swift for Realm Model

How to set primary key in Swift for Realm model

As of Realm Swift v10.10.0, you declare a primary key with @Persisted(primaryKey: true):

class Foo: Object {
@Persisted(primaryKey: true) var id = 0
@Persisted var title = ""
}

Older versions:

primaryKey needs to be a class function which returns the name of the property which is the primary key, not an instance method which returns the value of the primary key.

@objcMembers class Foo: RLMObject {
dynamic var id = 0
dynamic var title = ""

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

how to add primary key to realm objects

A lot of this is covered in the Realm guide Read and Write Data but let me also provide some details. Note, this answer reflects Swift pre-10.10.0 and some things have changed in later versions.

The method you're trying to use

realm.add(category, update: .all)

is called an 'upsert' and requires a primary key in the object.

An upsert either inserts or updates an object depending on whether the
object already exists. Upserts require the data model to have a
primary key.

So let's re-tool the Category object to include a primary key _id

class Category: Object {
@objc dynamic var _id: ObjectId = ObjectId.generate()
@objc dynamic var name: String = ""
let items = List<Item>()

override static func primaryKey() -> String? {
return "_id"
}
}

Then, when an object is created

let newCategory = Category()
newCategory.name = textField.text!
let realm = try! Realm()
try! realm.write {
realm.add(newCategory)
}

it will have a primary key

From then forward you can either modify the object within a write or upsert it via it's primary key.

let someId = //the id of the newCategory created above
try! realm.write {
let anotherCat = Category(value: ["_id": someId, "name": "Frank"])
realm.add(anotherCat, update: .modified)
}

The above code will update the original newCategory object with a new name because the _id is the same

Changing primary key property name on Realm Swift object model

You can actually change the primary key within a migration block. Suppose you have a object that looks like this, with 'localID' being the primary key

class TestClass: Object {
@objc dynamic var localID = UUID().uuidString

override static func primaryKey() -> String? {
return "localID"
}
}

and we want to change the primary key to _id. The migration block would look something like this

let vers = UInt64(3)
let config = Realm.Configuration( schemaVersion: vers, migrationBlock: { migration, oldSchemaVersion in
print("oldSchemaVersion: \(oldSchemaVersion)")
if (oldSchemaVersion < vers) {
print(" performing migration")
migration.enumerateObjects(ofType: TestClass.className()) { oldItem, newItem in
let oldId = oldItem!["localID"]
newItem!["_id"] = oldId

}
}
})

I would suggest using _id instead of id so it will be compatible with MongoDB Realm in the future. See Define a Realm Schema

Update Realm primary key value swift

Once you insert an object with a primary key you cannot modify it:

From Realm Docs

primary key is added to a Realm, the primary key cannot be changed.

Which leave you with few options:

  • Remove and reinsert the object
  • Change the primary key to something that doesn't change, like id
  • Omit the Primary Key. If you don't define one, you don't have a primary key, which means you can have more than one object with the same value for this property and it won't be indexed by this property.

In IOS Realm Primary key, how to update data and if already exist it should not take twice?

Realm provides several ways to update an objects properties.

A lot of this answer depends on the use case - for this example, it appears you are trying to update the property of a known object than has an existing primary key.

Starting with a DiscountClass - one thought is that offerName will probably not be a good primary key; what if you have two offers with the same name '10% Off' is common. You're better to use a UUID and then have the offer as text within the object.

class DiscountClass: Object {
@objc dynamic var discount_id = UUID().uuidString //guaranteed unique
@objc dynamic var offerName : String = ""
@objc dynamic var percentage: Float = 0.00
override class func primaryKey() -> String? {
return "discount_id"
}
}

Based on your question it looks like those are presented in a list (tableView?) which would be backed by a dataSource, usually an array or for Realm, a Results object which works as well.

var discountResults = Results<DiscountClass>

When a user selects a row to edit, that row index will correspond to the index in the discountResults. I don't know how you are editing so lets just say that when the user selects a row, they can then edit the percentage. When they are done the update is super simple

try! realm.write {
thisDiscount.percentage = "0.1" //updates this objects percent to 10%
}

Since it's a known object you are specifically updating that objects percentage and it will not create another object.

If you're in a situation where you may be adding a new object instead of updating an existing one then you can leverage using the realm.create option and supply either .modified or .all to the update parameter. Noting that .all can have a LOT of overhead so generally .modified is preferred.

Keeping in mind that to use realm.create, you need to create the object and assign an existing primary key if you want to update, or a new unique primary key if you want to create.

See the documentation for more example and complete explanation: Updating objects with primary keys

Realm Primary key with OR operator

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 ""
}
}

Realm swift change primaryKey

Katsumi from Realm here. You don't need to attempt change primary key in the migration block.

We always automatically update the schema to the latest version, and the only thing that you have to handle in the migration block is adjusting your data to fit it (e.g. if you rename a property you have to copy the data from the old property to the new one in the migration block).

So newObject!["primaryKeyProperty"] = "plateID"; is not needed.

I think your migration block should be like the following:

migration.enumerate(RegistrationPlateDB.className()) { oldObject, newObject in
newObject!["user"] = oldObject!["user"]
newObject!["registrationPlate"] = oldObject!["registrationPlate"]
newObject!["plateID"] = Int(oldObject!["registrationPlate"] as! String)
}

If you'd like to assign sequence numbers to plateID, for example:

var plateID = 0
migration.enumerate(RegistrationPlateDB.className()) { oldObject, newObject in
newObject!["user"] = oldObject!["user"]
newObject!["plateID"] = plateID
plateID += 1
}

Swift Realm Migrations fails (duplicate values for primary key)

You're telling your new object to use the same primary key as the old object, which isn't allowed

Let the new object populate the primary key on its own, which will copy the properties but assign it a unique ID.

It also appears the only difference is the new model is Codable and if that's the case it can be removed without a migration block as it's unrelated to Realm

Note that you only need local migrations for destructive changes like changing a property name or deleting a property.



Related Topics



Leave a reply



Submit