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
How to Compare Two Nsdate Objects in Objective C
Launchscreen.Xib Not Displaying My Custom Font
Programmatically Switching Between Tabs Within Swift
Cannot Set Searchbar as Firstresponder
Is a Date in Same Week, Month, Year of Another Date in Swift
iOS Enterprise Ota Distribution Unable to Download Application
iPhone Read Uiimage (Frames) from Video with Avfoundation
Swiftui - How to Initialize an Observedobject Using an Environmentobject as a Parameter
How to Make Post Nsurlrequest with 2 Parameters
Prevent Uialertcontroller to Dismiss
What Does the Text Inside Parentheses in @Interface and @Implementation Directives Mean
How to Set Device (Ui) Orientation Programmatically
Easiest Way to Force a Crash in Swift
Xcode 6.3 Adding Margins to Tableviewcell
Dismiss View Controller with Custom Animation
Capture Redirect Url in Wkwebview in iOS