Realm swift change primaryKey - ios

So I have Realm object
class RegistrationPlateDB: RLMObject {
dynamic var registrationPlate : String = ""
dynamic var user : String = ""
override static func primaryKey() -> String? {
return "registrationPlate"
} ...
and would like to change it to
class RegistrationPlateDB: Object {
dynamic var plateID : Int = -1
dynamic var registrationPlate : String = ""
dynamic var name : String = ""
dynamic var user : String = ""
override static func primaryKey() -> String? {
return "plateID"
} ....
So i've written a migration
migration.enumerate(RegistrationPlateDB.className()) { oldObject, newObject in
newObject!["name"] = ""
newObject!["user"] = ""
newObject!["registrationPlate"] = ""
newObject!["plateID"] = -1
newObject!["primaryKeyProperty"] = "plateID";
}
but I get an error probably because of the primaryKey change, since if I leave that line out it works but the primary key doesn't change.
Can someone give me any idea how to change the primaryKey.
EDIT: The first object was written for Objective-c realm
EDIT2: Or if anybody would know how could I make plateID autoincrement

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
}

Related

MongoDB Realm cannot update user embedded object in Swift

I'm trying to add to an embedded "Conversation" object within my user collection (for a chat app) in Mongo Realm. I would like to create a "default" conversation when the user account is created, such that every user should be a member of at least one conversation that they can then add others to.
The app currently updates the user collection via a trigger and function in Realm at the back end using the email / Password authentication process.
My classes are defined in Swift as follows:
#objcMembers class User: Object, ObjectKeyIdentifiable {
dynamic var _id = UUID().uuidString
dynamic var partition = "" // "user=_id"
dynamic var userName = ""
dynamic var userPreferences: UserPreferences?
dynamic var lastSeenAt: Date?
var teams = List<Team>()
dynamic var presence = "Off-Line"
var isProfileSet: Bool { !(userPreferences?.isEmpty ?? true) }
var presenceState: Presence {
get { return Presence(rawValue: presence) ?? .hidden }
set { presence = newValue.asString }
}
override static func primaryKey() -> String? {
return "_id"
}
#objcMembers class Conversation: EmbeddedObject, ObjectKeyIdentifiable {
dynamic var id = UUID().uuidString
dynamic var displayName = ""
dynamic var unreadCount = 0
var members = List<Member>()
}
So my current thinking is that I should code it in Swift as follows which I believe should update the logged in user, but sadly can't get this quite right:
// Open the default realm
let realm = try! Realm()
try! realm.write {
let conversation = Conversation()
conversation.displayName = "My Conversation"
conversation.unreadCount = 0
var user = app.currentUser
let userID = user.id
let thisUser = User(_id: userID)
realm.add(user)
}
Can anyone please spot where I'm going wrong in my code?
Hours spent on Google and I'm missing something really obvious! I have a fair bit of .NET experience and SQL but struggling to convert to the new world!
I'm a noob when it comes to NoSQL databases and SwiftUI and trying to find my way looking at a lot of Google examples. My example us based on the tutorial by Andrew Morgan https://developer.mongodb.com/how-to/building-a-mobile-chat-app-using-realm-new-way/
I am a bit unclear on the exact use case here but I think what's being asked is how to initialize an object with default values - in this case, a default conversation, which is an embedded object.
If that's not the question, let me know so I can update.
Starting with User object
class UserClass: Object {
#objc dynamic var _id = ObjectId.generate()
#objc dynamic var name = ""
let conversationList = List<ConversationClass>()
convenience init(name: String) {
self.init()
self.name = name
let convo = ConversationClass()
self.conversationList.append(convo)
}
override static func primaryKey() -> String? {
return "_id"
}
}
and the EmbeddedObject ConversationClass
class ConversationClass: EmbeddedObject {
dynamic var displayName = "My Conversation"
dynamic var unreadCount = 0
var members = List<MemberClass>()
}
The objective is that when a new user is created, they have a default conversation class added to the conversationList. That's done in the convenience init
So the entire process is like this:
let realm = Realm()
let aUser = UserClass(name: "Leroy")
try! realm.write {
realm.add(aUser)
}
Will initialize a new user, set their name to Leroy. It will also initialize a ConversationClass Embedded object and add it to the conversationList.
The ConversationClass object has default values set for displayName and unread count per the question.

In realm When ever i am updating it is updating only in 0th index how to solve it?

in realm i given id = 0 and it is as primary key and it will be auto increment, but problem is while updating it is saving in the index path 0 as declare as id : Int = 0.
Where ever i update also it is only updating in 0th index only.
i want to update as per selected object.
What to do?
Program :-
class Discount: Object {
#objc dynamic var id : Int = 0
#objc dynamic var offerName : String = ""
#objc dynamic var percentage: Float = 0.00
#objc dynamic var segmentIndex : Int = 0
#objc dynamic var dateWise: Date?
override class func primaryKey() -> String? {
return "id"
}
//Incrementa ID
func IncrementaID() -> Int{
let realm = try! Realm()
if let retNext = realm.objects(Discount.self).sorted(byKeyPath: "id").last?.id {
return retNext + 1
}else{
return 1
}
}
}
Generally speaking, auto-incrementing primary keys are challenging to deal with and can cause headaches long term.
What's generally most important is ensuring primary keys are unique and using UUID strings is ideally suited for that.
class Discount: Object {
#objc dynamic var discount_id = UUID().uuidString
override static func primaryKey() -> String? {
return "discount_id"
}
}
There may be concern about ordering and often times that managed by either adding a class var to determine ordering; like a timestamp for example or if you want to preserve ordering, objects can be added to a List, which keeps the order, like an array.
To answer your specific question, the code in your question is not complete (it was partially pulled from another question). The reason is that for each object that's created, it must be written to realm first, then the next object's primary key is based on the prior object.
Here's an example.
#objcMembers class User: Object {
dynamic var uid: Int = 0
dynamic var username: String?
func getNextUid() -> Int {
let realm = try! Realm()
if let lastObject = realm.objects(User.self).sorted(byKeyPath: "uid").first {
let lastUid = lastObject.uid
let nextUid = lastUid + 1
return nextUid
}
return 1
}
override static func primaryKey() -> String? {
return "uid"
}
}
now the sequence to use this is as follows
let u0 = User()
u0.uid = u0.getNextUid()
u0.username = "User 0"
let realm = try! Realm()
try! realm.write {
realm.add(u0)
}
let u1 = User()
u1.uid = u1.getNextUid()
u1.username = "User 1"
try! realm.write {
realm.add(u1)
}
as you can see, each object needs to be written to realm in order to the next object to be queried to get the prior objects primary key.
It's a whole lot of potentially unnecessary work and code.
My advice: Stick with the UUID().uuidString for primary keys.

How to store String Enums with Realm

I can't save data as enum in Realm. When I track the state of a variable, and save, the old value remains, instead of the new one. What am I doing wrong?
I used a setter and a getter, but still the problem is not solved.
import RealmSwift
class Transaction: Object {
var accounting: Accounting = .income
#objc dynamic var amount = 0
#objc dynamic var date = ""
#objc dynamic var note = ""
private var privateCategory: String = Category.noCategories.rawValue
var category: Category {
get { return Category(rawValue: privateCategory)! }
set { privateCategory = newValue.rawValue }
}
}
enum Category: String {
case noCategories = "No сategories"
case food = "Food"
case cafesAndRestaurants = "Cafes And Restaurants"
case manufacturedGoods = "Manufactured Goods"
case forceMajeure = "Force Majeure"
case entertainment = "Entertainment"
}
When I try to save a property, the Сategory is saved by default("No categories")
let transactionOne = Transaction()
transactionOne.accounting = .consuption
transactionOne.amount = 250
transactionOne.category = .food
transaction privateCategory String "No categories"
As seen from all the examples in the Realm guide, string properties that you want to persist are marked with #objc dynamic. So you should mark privateCategory as #objc dynamic too:
#objc dynamic var privateCategory: String = Category.noCategories.rawValue

List Objects Migrations in Realm for Swift 3

I added a list to my realm object class, and i need to migrate but i keep getting this error:
"Migration is required due to the following errors:
- Property 'UserIdCard.idImages' has been added."
these are my classes:
class UserIdCard: Object {
dynamic var idAccountId: Int = 0
dynamic var idName: String = ""
dynamic var idType: String = ""
let idImages = List<UserImage>()
override static func primaryKey() -> String? {
return "idAccountId"
}
}
class UserImage: Object {
dynamic var image: Data? = nil
}
And this is my migration code:
if (oldSchemaVersion < 4) {
migration.enumerateObjects(ofType: UserImage.className()) { oldObject, newObject in
newObject!["image"] = nil
}
migration.enumerateObjects(ofType: UserIdCard.className()) { oldObject, newObject in
let image = migration.create(UserImage.className(), value: Data())
let images = newObject?["idImages"] as? List<MigrationObject>
images?.append(image)
}
}
I did exactly like the example Realm provided : Link
Also i tried this : Link
,but it's not working, i tried to pass different values in "value" field but nothing worked, what is the right way to migrate a list in realm?
Thanks,

Sort Realm objects with the count of List property

I have two Realm data models classes like this:
class TaskList: Object {
dynamic var name = ""
dynamic var createdAt = NSDate()
let tasks = List<Task>()
}
And:
class Task: Object {
dynamic var name = ""
dynamic var createdAt = NSDate()
dynamic var notes = ""
dynamic var isCompleted = false
}
Now I need to query TaskList and sort them with number of tasks in each of them. I tried to use something like this but it crashes the app because its not supported:
realm.objects(TaskList).sorted("tasks.count")
Another workaround is:
Introduce taskCount property in TaskList, and make always sync taskCount and tasks.count.
class TaskList: Object {
dynamic var name = ""
dynamic var createdAt = NSDate()
let tasks = List<Task>()
dynamic var taskCount = 0
}
Then, you can use
realm.objects(TaskList).sorted("taskCount")
Since Realm does not support sorting on key paths, currently.
If you'd like to sync taskCount and tasks.count automatically, you can do like the following:
(Don't use tasks.append() directly, use addTask() method instead.)
class TaskList: Object {
dynamic var name = ""
dynamic var createdAt = NSDate()
private let tasks = List<Task>()
dynamic var taskCount = 0
func addTask(task: Task) {
func add() {
tasks.append(task)
taskCount = tasks.count
}
if let realm = realm where !realm.inWriteTransaction {
try! realm.write{
add()
}
} else {
add()
}
}
}
Like this:
realm.objects(TaskList).sort { $0.tasks.count < $1.tasks.count }
EDIT: have no idea about Realm, this only works when objects returns a CollectionType and List has a count property.

Resources