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

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.

Related

How to map a Firestore DocumentID to a RealmObject attribute?

I'm trying to provide some data in the cloud with Firestore that can be downloaded and stored in a Realm database on an iOS device.
The structure of my object that I want to store is:
import Foundation
import RealmSwift
import FirebaseFirestore
import FirebaseFirestoreSwift
#objcMembers class Flashcard: Object, Codable{
#objc dynamic var id: String? = NSUUID().uuidString
#objc dynamic var character: String?
#objc dynamic var title: String?
#objc dynamic var translation: String?
#objc dynamic var created: Date = Date()
let deck = LinkingObjects<FlashcardDeck>(fromType: FlashcardDeck.self, property: "cards")
override class func primaryKey() -> String? {
return "id"
}
private enum CodingKeys: String, CodingKey {
case id
case character
case translation
case created
case title
}
If I try to map the documentID to my id attribute with
#DocumentID #objc dynamic var id: String? = NSUUID().uuidString
If get the following error:
'Primary key property 'id' does not exist on object 'Flashcard'
How can I solve this problem?
EDIT: To make it more understandable here is a screenshot of my Firestore database:
The collection "PredifinedDecks" will store many decks. For example the id = DF59B1B3-BD22-47CE-81A6-04E7A274B98F represents one deck. Each deck will store an array/List with cards in it.
Not sure I fully understand the question but let me address this at a high level.
It appears there is a PredefinedDecks (a collection) that contains documents. Each document has a field (an array) of cards and some other field data. If the goal is to read in all of the documents (the decks) and their child data and store them as Realm objects, here's one solution. Start with a Realm object to hold the data from Firestore
class DeckClass: Object {
#objc dynamic var deck_id = ""
#objc dynamic var created = ""
#objc dynamic var title = ""
let cardList = List<CardClass>()
convenience init(withDoc: QueryDocumentSnapshot) {
self.init()
self.deck_id = withDoc.documentID
self.title = withDoc.get("title") as? String ?? "no title"
self.created = withDoc.get("created") as? String ?? "no date"
let cardArray = withDoc.get("cards") as? [String]
for card in cardArray {
let card = CardClass(withCard: card) {
self.cardList.append(card)
}
}
}
}
With this, you simply pass the documentSnapshot from Firestore for each document and the class will populate its properties accordingly.
and the code to read Firestore
func readDecks() {
let decksCollection = self.db.collection("PredefinedDecks")
decksCollection.getDocuments(completion: { documentSnapshot, error in
if let err = error {
print(err.localizedDescription)
return
}
for doc in documentSnapshot!.documents {
let deck = DeckClass(withDoc: doc)
self.decksList.append(deck) //a Realm List class object? Something else?
}
})
}

Realm Model Conversion Failed

I have a realm db model like:
import RealmSwift
class User: Object {
let isVerified = RealmOptional<Bool>()
#objc dynamic var pk = 0
#objc dynamic var profilePicUrl: String? = nil
}
And I am getting data from service and it returns same name and type like realm model.
I want to save this data to db. But when I try to convert model to realm model it gives error.
let data = [Users(value: serviceUser)] -> serviceUser comes from service.
Before save when I try to convert I get this error:
'RLMException', reason: 'Invalid value 'ServiceUserModel(isVerified: Optional(false), pk: Optional(123456), profilePicUrl: Optional("") of type '__UserDataValue' for 'bool' property 'Users.isVerified'.'
serviceUser Model:
public struct ServiceUserModel: Codable {
public var isVerified: Bool?
public var pk: Int?
public var profilePicUrl: String?
}
I do not want to use for loop because of performance problem. I want to save this data in one time.
Save Method:
func save(users: [Users]){
try! database.write {
database.add(users)
}
}
How can I convert it?
I think the problem is located here:
when I try to convert
When dealing with RealmOptional you have to set it's .value property.
let isVerified = RealmOptional<Bool>()
now the isVerified.value is nil
isVerified.value = true
now it's true
But you can't use the Bool? to set the value.
The same applies to pk.
You should implement the convenience init() and provide some default values for your optionals

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!

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

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.

Do Realm writes cascade?

I'm currently using Realm Swift 1.0.1.
Say you have a Realm Object, that has another Realm Object as a property, like so:
import RealmSwift
class Car: Object {
dynamic var id = 0
override static func primaryKey() -> String? {
return "id"
}
dynamic var model = ""
}
class Garage: Object {
dynamic var id = 0
override static func primaryKey() -> String? {
return "id"
}
dynamic var carStored: Car?
}
If you then create new Car and Garage objects, with the Car being a property of the Garage... but only write the new Garage to the Realm, like so...
let teslaCar = Car()
teslaCar.id = 1
teslaCar.model = "Tesla"
let myGarage = Garage()
myGarage.id = 1
myGarage.carStored = teslaCar
let realm = try! Realm()
try! realm.write {
realm.add(myGarage, update: true)
}
... will the write cascade, and also save the teslaCar to the Realm as well as myGarage?
The Realm Swift write docs: https://realm.io/docs/swift/latest/#writes
I just tested it in one of the Realm sample apps to be absolutely sure. Yes, if you set an object as a linked object of another Realm object, both will be added to Realm in the next write transaction.