Realm Adding Object with Compound primaryKey Error - swift

I have a simple class
class FarmRecord: Object {
dynamic var year = ""
dynamic var month = ""
dynamic var day = ""
func setYearID(inYear: String) {
self.year = inYear
compoundKey = compoundKeyValue()
}
func setMonthID(inMonth: String) {
self.month = inMonth
compoundKey = compoundKeyValue()
}
func setDayID(inDay: String) {
self.day = inDay
compoundKey = compoundKeyValue()
}
dynamic lazy var compoundKey: String = self.compoundKeyValue()
private func compoundKeyValue() -> String {
return "\(year)\(month)\(day)"
}
override static func primaryKey() -> String? {
return "compoundKey"
}
}
I tried to add object as follow:
let storeRealm = try! Realm()
let farm = FarmRecord()
farm.setYearID("2016")
farm.setMonthID("3")
farm.setDayID("1")
do {
try storeRealm.write {
storeRealm.add(farm)
}
} catch {
}
And I see a crash with EXEC_BAD_ACCESS (code = 1). I even tried storeRealm.add(farm, update: true) with no difference.

Realm appears to be mishandling your compoundKey property due to it being marked as lazy. I've written up a bug report about the issue on GitHub. As a workaround I'd suggest removing the lazy modifier and initializing compoundKey to an empty string.

Related

I want to sort the List of RealmSwift

I am using RealmSwift.
Data is a one-to-many relationship.
I'm having trouble because I don't know how to sort the list in RealmSwift.
I want to sort the tasks linked to the TaskList.
Thank you.
class TaskList: Object, Identifiable {
#objc dynamic var id = NSUUID().uuidString
#objc dynamic var title = ""
#objc dynamic var createdAt = NSDate()
var tasks: List<Task> = List<Task>()
override static func primaryKey() -> String? {
return "id"
} }
class Task: Object, Identifiable {
#objc dynamic var id = NSUUID().uuidString
#objc dynamic var title = ""
#objc dynamic var createdAt = NSDate()
private let lists = LinkingObjects(fromType: TaskList.self, property: "tasks")
var list: TaskList { return list.first! }
override static func primaryKey() -> String? {
return "id"
} }
If you want your tasks stored in an ordered fashion you'll have to manually do an ordered insert.
extension List {
func insert<V: Comparable>(_ object: Element, orderedBy keyPath: KeyPath<Element, V>) {
var index = 0
for i in 0..<count {
if self[i][keyPath: keyPath] >= object[keyPath: keyPath] {
break
}
index = i + 1
}
insert(object, at: index)
}
}
let list = TaskList()
let tasks = [
Task(title: "J"),
Task(title: "Z"),
Task(title: "T"),
Task(title: "J"),
Task(title: "Z"),
]
tasks.forEach {
list.tasks.insert($0, orderedBy: \.title)
}
However, I find it much easier to keep Lists unsorted and retrieve sorted Results whenever I need to display the data. To sort by a single property just call sorted(byKeyPath:):
let sortedTasks = taskList.tasks.sorted(byKeyPath: "title")
To sort by multiple fields call sorted(by:):
let sortedTasks = taskList.tasks.sorted(by: [
SortDescriptor(keyPath: "title"),
SortDescriptor(keyPath: "createdAt")
])
Alternatively, you can add a sorted property to your Model:
class TaskList: Object, Identifiable {
#objc dynamic var id = UUID().uuidString
#objc dynamic var title = ""
#objc dynamic var createdAt = Date()
var tasks = List<Task>()
lazy var sortedTasks: Results<Task> = tasks.sorted(byKeyPath: "title")
override class func ignoredProperties() -> [String] {
return ["sortedTasks"]
}
override static func primaryKey() -> String? {
return "id"
}
}
You cannot get a LinkingObjects itself to be sorted, but you can call LinkingObjects.sorted(byKeyPath:), which returns a Results instance containing all elements of the LinkingObjects, just sorted.
class Task: Object, Identifiable {
#objc dynamic var id = NSUUID().uuidString
#objc dynamic var title = ""
#objc dynamic var createdAt = NSDate()
private let lists = LinkingObjects(fromType: TaskList.self, property: "tasks")
lazy var sortedLists = lists.sorted(byKeyPath: "createdAt")
var list: TaskList { return list.first! }
override static func primaryKey() -> String? {
return "id"
}
}
I think this is a one line answer. If you know which TaskList object you want to get the tasks for, and you want them ordered by say, creation date. This will do it
let taskResults = realm.objects(Task.self)
.filter("ANY lists == %#", taskList)
.sorted(byKeyPath: "createdAt")

Getting "Must have a uuid if no _objectID" exception when inserting object into dictionary

I'm writing a unit-test to a class that uses PHAsset type. I mocked it as below:
class PHAssetMock: PHAsset {
let date: Date
let uuid: UUID
init(dateStr: String) {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM-dd-yyyy HH:mm"
self.date = dateFormatter.date(from: dateStr)!
self.uuid = UUID()
}
override var creationDate: Date? {
return date
}
override var hash: Int {
let hash = Int(self.date.timeIntervalSinceNow)
return hash
}
static func ==(lhs: PHAsseMock, rhs: PHAsseMock) -> Bool {
return lhs.date.timeIntervalSinceNow == rhs.date.timeIntervalSinceNow
}
}
When a function that uses mocked objects tries to insert it in a dictionary I'm getting an exception:
func foo(assets: [PHAsset]) {
var label: [T: String]()
for asset in assets {
label[asset] = "undefined" // Exception: "NSInternalInconsistencyException", "Must have a uuid if no _objectID"
}
}
When debugging, the override hash var is being called.
I had the same issue with the PHAsset when unit testing Photos framework. Overriding isEqual function helped to get rid of the exception.
class Mock : PHAsset {
let _localIdentifier: String = UUID().uuidString
let _hash: Int = UUID().hashValue
override var localIdentifier: String {
return _localIdentifier
}
override var hash: Int {
return _hash
}
override func isEqual(_ object: Any?) -> Bool {
guard let object = object as? Mock else {
return false
}
return self.localIdentifier == object.localIdentifier
}
}

Swift: Bug when try to access value of object in array

I encounter a very strange bug, I have a PodEvent type array, and with a second function I get another array that I cast, when I make a print of the casted array with the result it shows me all the objects of the board...
PodEvent {
eventID = 2;
podID = 1;
drinkDate = 2018-09-25 10:00:00 +0000;
actualDrinkingTime = (null);
keyDate = 2018-09-25 13:00;
rawState = Forgotten;
}
But when I want to access the value of the object it returns to me as the default values! for example:
print(self.podEvents[1].eventID)
print(self.podEvents[1].podID)
outpout:
-1
-1
Here my class:
class PodEvent: Object {
#objc var eventID: Int = -1
#objc var podID: Int = -1
#objc var drinkDate: Date = Date()
#objc var actualDrinkingTime: Date? = Date()
var state: PodState = .future
#objc var keyDate: String = ""
#objc private var rawState: String!
convenience init(eventID: Int, podID: Int, drinkDate: Date, actualDrinkingTime: Date?, state: PodState){
self.init()
self.eventID = eventID
self.podID = podID
self.drinkDate = drinkDate
self.actualDrinkingTime = actualDrinkingTime
self.state = state
self.rawState = state.state
}
func setState(state: PodState){
self.state = state
rawState = state.state
}
override class func primaryKey() -> String? {
return "keyDate"
}
This bug is very strange
My code to fetch my array:
//Fetch pod history in internal database
self.databaseManager.fetch(object: PodEvent.self, predicate: nil, sortedBy: "keyDate", ascending: true) { success, results, error in
guard error == nil else {
Alerts.alertMessage(for: self, title: "ERROR".localized,
message: error!.localizedDescription,
closeHandler: nil)
return
}
self.podEvents = (results as! [PodEvent])
self.pods = pods
print(self.podEvents[1].eventID)
print(self.podEvents[1].podID)
}
and:
func fetch(object: Object.Type, predicate: NSPredicate?, sortedBy: String, ascending: Bool, completion: DatabaseCompletion?) {
do {
let realm = try Realm()
let objects: Results<Object>!
if let predicate = predicate {
objects = realm.objects(object).filter(predicate).sorted(byKeyPath: sortedBy, ascending: ascending)
} else {
objects = realm.objects(object).sorted(byKeyPath: sortedBy, ascending: ascending)
}
let objectsArray = Array(objects)
completion?(true, objectsArray, nil)
} catch let error {
print("Could not write object (type \(object)) to Realm:", error.localizedDescription)
completion?(false, nil, error)
}
}
im using realm
Your object definition is flawed, you need to add the dynamic keyword to all persisted properties.
class PodEvent: Object {
#objc dynamic var eventID = -1
#objc dynamic var podID = -1
#objc dynamic var drinkDate = Date()
#objc dynamic var actualDrinkingTime: Date? = Date()
var state: PodState = .future
#objc dynamic var keyDate = ""
#objc dynamic private var rawState: String!
...
}

query Object from Realm List

enter image description herei'm trying to query object from realm
class MessageRealm: Object {
dynamic var fromId = String()
dynamic var messageID = String()
dynamic var textDownloadded = String()
override class func primaryKey() -> String? {
return "messageID"
}
}
class UsersRealm: Object {
dynamic var sender = String()
let msgs = List<MessageRealm>()
override class func primaryKey() -> String? {
return "sender"
}
}
i have two class one for messages and the other for users, every users have a list of messages and i need to query thats message based on (UserRealm.sender)
This is the realm DB
I solve the issue by this way if anyone face the same
var messageIndex: Results<MessageRealm>!
let realm = try! Realm()
let mssagesRealm = realm.objects(UsersRealm.self).filter("sender = %#", userTitleName)
for sub in mssagesRealm {
messageIndex = sub.msgs.sorted(byKeyPath: "timeStamp")
}

Cannot assign to the result of this expression with generics

I have the following generic class where I want to manage a string hash:
class NamedProfile<T> {
private var set = [String:T]()
private var profiles = [String]()
private let userDefaults = NSUserDefaults.standardUserDefaults()
private let profileName:String
var currentSet = ""
init(name:String, set:[String:T]) {
profileName = name
self.set = set
if let existingProfiles = userDefaults.objectForKey(name) as? [String] {
profiles = existingProfiles
}
for key in profiles {
if let existingProfile = userDefaults.objectForKey(profileNamed(name)) as? T {
set[key] = existingProfile // <-- error
}
}
}
private func profileNamed(name:String) -> String { return "\(profileName) \(name)" }
}
Why does the compiler croak in the above assignment?
In
init(name:String, set:[String:T]) {
// ...
set[key] = existingProfile // <-- error
// ...
}
set refers to the (immutable) method parameter.
Use self.set instead to refer to the property.