I have next error: Attempting to create an object of type 'TypeId' with an existing primary key value '96292'.
And I got crash after this.
Using String type for the primary key instead of the Int type, and use UUID for each object, then you could avoid the duplicated keys.
class AModel: Object {
#objc dynamic var id = UUID().uuidString
override static func primaryKey() -> String? {
return "id"
}
}
Alternatively, if you want to use Int, and you are sure about that there is only one object will be created in a second, you could use timestamp value to avoid the situation too:
class AModel: Object {
#objc dynamic var id = Date().timeIntervalSince1970
override static func primaryKey() -> String? {
return "id"
}
}
Agree with #Tj3n and #EpicPandaForce's opinions, updating it if it's not a new object actually.
Related
Say I have a class and its Realm representation that looks like this:
class Dog {
var identifier: String
var age: Int
...
override static func primaryKey() -> String? {
return "identifier"
}
}
Now here is what my new Identifier class looks like:
class Identifier {
var functionalId: String
var version: String
...
}
I need to replace my Dog's identifier String property to be an Identifier like this:
class Dog {
var identifier: Identifier
var age: Int
...
override static func primaryKey() -> String? {
return "identifier" // I need to change this
}
}
but I'm having a hard time replacing the content of the primaryKey() method:
How do I tell Realm to look for an object's sub property for the primaryKey() ?
I tried something like:
override static func primaryKey() -> String? {
return "identifier.functionalId"
}
But it seems that I was too naive, it won't work
** EDIT ** Following comments, here is the output of the crash I'm getting:
Terminating app due to uncaught exception 'RLMException', reason: 'Primary key property 'identifier.functionalId' does not exist on object Dog
Sorry for bad English though, I couldn't find the words fir this simple problem, especially the title!
I've never tried this in Realm, but it might be possible using a dynamic variable for your primary key and a function that pulls the value from the sub-object:
var _identifier: Identifier
dynamic lazy var identifier: String = self.identifierValue()
override static func primaryKey() -> String? {
return "identifier"
}
func identifierValue() -> String {
return _identifier.functionalId
}
How do I tell Realm to look for an object's sub property for the
primaryKey()
You can't.
Looking at the errors you've mentioned:
If you try setting the primary key to:
override static func primaryKey() -> String? {
return "identifier"
}
Then you get an error from Realm saying: Property 'identifier' cannot be made the primary key of 'Dog' because it is not a 'string' or 'int' property.
If you try setting the primary key to:
override static func primaryKey() -> String? {
return "identifier.functionalId"
}
Then you get an error from Realm saying: Primary key property 'identifier.functionalId' does not exist on object Dog
This leads to the conclusion that the primary key must be of type String or Int, and it must be a property of Dog, not another class.
I thought this would be pretty straightforward after reading here and here but I'm a bit stuck.
I have a 'favouriteWorkout' object that looks like this :
class FavouriteObject: Object {
#objc dynamic var favouriteWorkoutName = ""
#objc dynamic var workoutReference = WorkoutSessionObject()
override class func primaryKey() -> String? {
return "favouriteWorkoutName"
}
}
What I'm trying to do here is reference a WorkoutSessionObject in Realm that links from a WorkoutName when a workout is saved as a favourite.
My WorkoutSessionObject has a primary key of workoutID which is a UUID string. It looks like this :
class WorkoutSessionObject: Object {
#objc dynamic var workoutID = UUID().uuidString
#objc dynamic var workoutType = ""
let exercises = List<WorkoutExercise>()
#objc dynamic var totalExerciseCount = 0
#objc dynamic var rounds = 0
#objc dynamic var favourite : Bool = false
override class func primaryKey() -> String? {
return "workoutID"
}
}
I've then tried to save using this :
let favouriteWorkout = FavouriteObject()
favouriteWorkout.favouriteWorkoutName = favouriteName
favouriteWorkout.workoutReference = (realm.object(ofType: WorkoutSessionObject.self, forPrimaryKey: self.workoutID))!
do {
try realm.write {
realm.add(favouriteWorkout)
}
} catch {
print ("Error adding favourite")
}
but i get a crash when I run of :
'RLMException', reason: 'The FavouriteObject.workoutReference property must be marked as being optional.
However, when I then try to make it optional (by adding ?) it says
"Cannot use optional chaining on non-optional value of type 'WorkoutSessionObject"!
Summary
I want to save a reference of the workoutID of a WorkoutSessionObject in my FavouriteObject which is an actual link to the WorkoutSessionObject (so the properties can be accessed from favourites)
Update
using the answers below I've now sorted the problem of the workout reference. This is now showing in Realm as the proper format () under "workoutReference". However, I'm now getting "nil" in "workoutReference" when trying to save. I know the workoutID is coming through correctly as I am printing it in the console.
You need to change the declaration of workoutReference. First of all, you need to make it Optional by writing ? after the type. Secondly, you shouldn't assign a default value to it, it needs to be Optional for a reason. The linked docs clearly state that
to-one relationships must be optional
, and workoutReference is clearly a to-one relationship.
class FavouriteObject: Object {
#objc dynamic var favouriteWorkoutName = ""
#objc dynamic var workoutReference:WorkoutSessionObject?
override class func primaryKey() -> String? {
return "favouriteWorkoutName"
}
}
In property-cheatsheet you can see that a non-optional Object-property is not allowed, so you have to change it like the following:
class FavouriteObject: Object {
#objc dynamic var favouriteWorkoutName = ""
// here you have to make the property optional
#objc dynamic var workoutReference: WorkoutSessionObject?
override class func primaryKey() -> String? {
return "favouriteWorkoutName"
}
}
How can I model one-to-one relationship between objects?
For example, I have models for user_infoA, user_infoB and user_profile.
user_profile has
user_id (PK)
name
age
user_infoA has
info_a_id (PK)
user_profile
user_infoB has
info_b_id (PK)
user_profile
user_profile (P) have relationship with both user_infoA (A) and user_infoB(B). When A is deleted, also will P be deleted or not? Will P be deleted only if when related A and B are deleted?
And how can I model this with realm swift?
Many-to-one relationship needs optional property, and it makes me use force unwrapping optional. :(
[EDITED]
class RealmMyProfile: Object {
#objc dynamic var id: Int64 = 0
#objc dynamic var profile = RealmUserProfile()
}
class RealmUserProfile: Object {
#objc dynamic var userId: Int64 = 0
#objc dynamic var name: String = ""
override static func primaryKey() -> String? {
return "userId"
}
}
Exception 'The RealmMyProfile.profile property must be marked as being optional.' occurred. It should be optional.
To-one relationships (links) in Realm cannot enforce that a link is always present. So they always have to be marked as optional because there's no way to prevent nil from being stored for a link in the file format.
Therefore, we require that Realm models defined in Swift explicitly mark to-one relationships as Optional.
class RealmMyProfile: Object {
#objc dynamic var id: Int64 = 0
#objc dynamic var profile:RealmUserProfile?
}
class RealmUserProfile: Object {
#objc dynamic var userId: Int64 = 0
#objc dynamic var name: String = ""
override static func primaryKey() -> String? {
return "userId"
}
}
You can do this solution which may save you from using the unwrapping value
Realm Issue 2814
dynamic var profile_:RealmUserProfile? = nil
var profile: RealmUserProfile {
get {
return profile_ ?? RealmUserProfile()
}
set {
profile_ = newValue
}
}
I am trying to create compound primary key from two keys. Using lazy for compoundKey will raise an exception - either remove lazy or add to ignore property list
So when I try to add ignore property list I am getting following exception - Terminating app due to uncaught exception 'RLMException', reason: 'Primary key property 'compoundKey' does not exist on object 'Collection'
Removing lazy and setting the empty string will add empty key and hence single row which will treat all primary key value as empty.
This is my code
class Collection : Object {
#objc dynamic var count: Int = 0
#objc dynamic var nextURL: String?
#objc dynamic var previousURL: String?
func setCompoundNextURL(nextURL: String) {
self.nextURL = nextURL
compoundKey = compoundKeyValue()
}
func setCompoundTourPreviousURL(previousURL: String) {
self.previousURL = previousURL
compoundKey = compoundKeyValue()
}
public dynamic lazy var compoundKey: String = self.compoundKeyValue()
override static func primaryKey() -> String? {
return "compoundKey"
}
override static func ignoredProperties() -> [String] {
return ["compoundKey"]
}
func compoundKeyValue() -> String {
return "\(nextURL ?? "")\(previousURL ?? "")"
}
}
Please help. I am not able to figure where I went wrong.
You cannot tell Realm to use an ignored property as a primary key. An ignored property isn't persisted to the Realm. The primary key property must be persisted to the Realm. Additionally, the primary key property's value cannot be changed after the object is created. For this reason I'd suggest computing the value inside a convenience initializer and assigning it to the property at that time.
I am trying to add a non-realm class object to realm object something like this.
class TrainTripItinerary: Object {
dynamic var departStationName: String?
dynamic var departStationCode: String?
var runningStatus: TrainRunningStatus?
override static func ignoredProperties() -> [String] {
return ["runningStatus"]
}
}
While TrainRunningStatus is not a realm class.
class TrainRunningStatus {
var trainDataFound: String?
var startDate: String?
var startDayDiff: String?
}
I am not able to update runningstatus property now. Anyone know how it works? I fetch separately runnningstatus and assign it to the realm object later but it stays nil even after the assignment.
eg.
let runningStatus = TrainRunningStatus()
trainTripItinerary.runningStatus = runningStatus
This line is not working, trainTripItinerary runningStatus property is not set properly its always nil.
As suggested in comments make sure you use the same instance of TrainTripItinerary because ignored properties won’t automatically update their value across different instances.
See an example code below that demonstrates how ignored properties work
let realm = try! Realm()
try! realm.write {
realm.deleteAll()
}
let runningStatus = TrainRunningStatus()
var trainTripItinerary = TrainTripItinerary()
trainTripItinerary.runningStatus = runningStatus
assert(trainTripItinerary.runningStatus != nil)
try! realm.write {
realm.add(trainTripItinerary);
}
assert(trainTripItinerary.runningStatus != nil)
trainTripItinerary = realm.objects(TrainTripItinerary.self).first!
assert(trainTripItinerary.runningStatus == nil)
Firstly, your code is not correct.
class TrainTripItinerary: Object {
dynamic var departStationName: String?
dynamic var departStationCode: String?
var runningStatus: TrainRunningStatus?
override static func ignoredProperties() -> [String] {
return ["runningStatus"]
}
}
func ignoredProperties() -> [String] is only used on Realm properties. Since your property var runningStatus: TrainRunningStatus? does not begin with dynamic, it is not a Realm property. You don't need to use func ignoredProperties() -> [String] here.
var runningStatus: TrainRunningStatus? here is called a "transient property" in Realm. Usually a transient property is something calculated basing on current date or on Realm properties, Realm won't do anything on transient properties and you should maintain them yourself.
So if you just want to use runningStatus as a transient property, you can simply remove the code override static func ignoredProperties() -> [String].