SwiftUI Query Realm Database - swift

In my Realm DB for ChatMessage I have the following data object:
When i follow the tutorial on retrieving data/query : https://www.mongodb.com/docs/realm/sdk/swift/crud/read/
let realm = try! Realm()
let specificPerson = realm.object(ofType: ChatMessage.self,
forPrimaryKey: ObjectId("6369ee9db15ac444f96eb5d6"))
print(specificPerson!.author as String)
I receive fatal error Nil, it cannot find anything. from the line
print(specificPerson!.author as String)
LiveChat/ChatView.swift:59: Fatal error: Unexpectedly found nil while unwrapping an Optional value
When I do a broader query for all,
let chatM = realm.objects(ChatMessage.self)
print(chatM)
print(type(of: chatM))
I receive empty object
Results<ChatMessage> <0x7fe4d163efa0> (
)
Results<ChatMessage>
I am adding the Chat messages Via
#ObservedResults(ChatMessage.self,
sortDescriptor: SortDescriptor(keyPath: "timeStamp", ascending: true)) var messages
private func addMessage(){
let message = ChatMessage(room: room, author: userName, text: messageText)
$messages.append(message)
messageText = ""
}
Similar to https://github.com/mongodb-developer/LiveTutorialChat/blob/main/iOS/LiveChat/LiveChat/Views/ChatsView.swift

Regarding your updated code:
So yes, you are indeed creating the objects (using the .append(_:) method on an #ObservedResults collection) in a correct way. This means that you are likely opening the wrong realm database when you're querying for the objects. Please have a look at Realm's documentation regarding realm configuration, specifically on how to set the default configuration. Calling try! Realm() without any parameters will use this default configuration to open the database.
Original reply
If let chatM = realm.objects(ChatMessage.self) returns an empty Results object, the realm you're querying does not contain any objects of the ChatMessage type. It's as simple as that. Logically, let specificPerson = realm.object(ofType: ChatMessage.self, forPrimaryKey: ObjectId("6369ee9db15ac444f96eb5d6")) would then also return nil.
Without seeing how and where you're creating the missing ChatMessage objects, it's hard to say what's going wrong. Some common missteps:
You are querying the wrong realm database: If you are only every accessing realm through let realm = try! Realm() this shouldn't be a problem.
You haven't actually added any ChatMessage object to the realm database: Simply initializing an object is not enough, You need to explicitly add objects to the Realm database using either Realm.create() or Realm.add():
let realm = try! Realm()
let exampleObject = ChatMessage()
print(realm.objects(ChatMessage.self).count) // Prints "0"
// Add to the realm using either create() or add()
realm.add(exampleObject)
print(realm.objects(ChatMessage.self).count) // Prints "1"

Related

Update multiple properties in Realm without touching the other (saved) values

When reading the docs, it says I should do the following to update a single (or multiple) field through a dictionary:
var person = ["personID": "My-Primary-Key", "name": "Tom Anglade"]
// Update `person` if it already exists, add it if not.
let realm = try! Realm()
try! realm.write {
realm.add(person, update: true)
}
I have no idea how they can get to compile, because I get this error:
Cannot convert value of type '[String : String]' to expected argument type 'Object'
I want to dynamically update multiple field throughout a dictionary, is this possible?
I don't think that tutorial is correct, even though it's from the official website written by Realm engineers. If you look at the official documentation of RealmSwift, you can see that both versions of the add function accept an argument subclassing Object.
The function you are looking for and that should be mentioned in the tutorial is public func create<T: Object>(_ type: T.Type, value: Any = [:], update: Bool = false) -> T, which you can use to update an existing object from a Dictionary.
Your code should look like:
var person = ["personID": "My-Primary-Key", "name": "Tom Anglade"]
// Update `person` if it already exists, add it if not.
let realm = try! Realm()
try! realm.write {
realm.create(Person.self, value: person, update: true)
}

Realm write fails on second time being called

I have a service for retrieving data from an api, it returns json that I map and then populate to Realm and finally display this to the view.
I have way for a user to force refresh the retrieval of data, which means I need to update my Realm data aswell. This works fine on the first time I call the method. But if I try to do it again it crashes every single time with this exception.
*** Terminating app due to uncaught exception 'RLMException', reason: 'Can only add, remove, or create objects in a Realm in a write transaction - call beginWriteTransaction on an RLMRealm instance first.'
This is how my function works:
private func writeCollection(someKey: String?) {
let realm = try! Realm()
try! realm.write {
realm.add(someObject[someKey!]!, update: true)
}
}
This error occurred inside a different function then initially thought. I was holding a dictionary like this [someKey:someObject] and using this dictionary as a reference to write from.
Problem was this was outside the try! realm.write
// removed this reference
// var dictionary = [String:SomeObject]()
realm.write {
let someObject = SomeObject()
someObject.id = someKey
realm.add(someObject, update: true)
}

How to fetch core data objects into Dictionary in Swift?

I've saved objects in core data, and I am looking how to fetch those objects as a Dictionary
Here is an example of my code where sections is keys for the dictionary and Company as an array of core data objects.
private var companies = Dictionary<String, Array<Company>>()
private var sections: [String] = ["Pending", "Active", "Pending"]
override func viewWillAppear(_ animated: Bool) {
let fetchRequest : NSFetchRequest<Company> = Company.fetchRequest()
let moc = DatabaseController.getContext()
do {
let request = try moc.fetch(fetchRequest)
for case let (index, object) in request.enumerated() {
companies[sections[index]]!.append(object)
}
} catch let error as NSError {
print("Could not fetch. \(error), \(error.userInfo)")
}}
When I am trying to execute my code, I have an error:
fatal error: unexpectedly found nil while unwrapping an Optional value
Could anyone help me with that issue?
That error message means that you're force unwrapping an optional that doesn't have a value. In other words you're using ! where you shouldn't. (You should basically never use the force unwrap operator (!).)
Let's look at the line where you do that:
companies[sections[index]]!.append(object)
If we break this down and add the inferred types we have:
let section: String? = sections[index]
let companyArray: Array<Company> = companies[section]!
You're crashing because companies starts off empty, so asking for any of the arrays will return nil. (Actually, I'm not sure how your code is compiling, since you can't subscript into the dictionary with an optional.)
However, if you fix that, we still have a problem because you're using the index of the fetched array to look up the section. If you have more than three companies, that will start to fail.
I suspect you want something like:
for let company in result {
if var companyArray = companies[company.status] {
companyArray.append(company)
} else {
companies[company.status] = [company]
}
}
Where status is an invented property on Company that returns a String like "Pending" or "Active".
I've found the solution, just need to use NSFetchResultController in order to display data in TableView by different sections.

Object has been deleted or invalidated. (Realm)

Please refer Error: Object has been deleted or invalidated. (Realm)
I encounter this error with both 2 cases also.
I try to find the DBProduct before delete, but it also got Error: Object has been deleted or invalidated. Is this wrong? Please help me. I call this method in block of Alert view as Case 2.
let realm = try! Realm()
try! realm.write {
let dbProduct = realm.objectForPrimaryKey(DBProduct.self, key: product.id)
if dbProduct != nil {
realm.delete(dbProduct!)
}
}
Update: This issue happens on iOS8 only, and it is OK on iOS 9.
Normally, that error should only be thrown if you try and access a property of a Realm object that has been deleted, or if you explicitly told its parent Realm object to invalidate.
Like James said, it's quite likely that your product variable there has already been invalidated, in which case trying to call product.id would likely cause that crash.
If that's the case, then the easiest thing to do to fix this would be to avoid using the product variable and instead, simply making a copy of the value of id directly. This way, if the object is deleted/invalidated, you still have its primary key in which you can test to see if it still exists.
On a side note, this code could certainly be made a bit more efficient as well. It's not necessary to perform queries inside write transactions and you should only open a write transaction if there actually was an object to delete (Write transactions are pretty heavy things, so they should be avoided as much as possible).
let productID = product.id //save a copy of the ID in case 'product' gets deleted.
let realm = try! Realm()
let dbProduct = realm.objectForPrimaryKey(DBProduct.self, key: productID)
if dbProduct != nil {
try! realm.write {
realm.delete(dbProduct!)
}
}
I hope that helped!

Delete all data from specific Realm Object Swift

Before i get too far into my question. My goal, which may influence your answers, is to remove Object data if it is no longer in the cloud.
So if I have an array ["one", "two", "three"]
Then in my server I remove "two"
I want my realm to update the change.
I figure the best way to do this is to delete all data in the specific Object, then call my REST API to download the new data. If there is a better way, please let me know.
Okay so here is my problem.
I have an Object Notifications()
every time my REST API is called, before it downloads anything I am running this:
let realm = Realm()
let notifications = Notifications()
realm.beginWrite()
realm.delete(notifications)
realm.commitWrite()
I get this error after running: Can only delete an object from the Realm it belongs to.
so i tried something like this:
for notification in notifications {
realm.delete(notification)
}
realm.commitWrite()
The error I get within xcode is this: "Type Notifications does not conform to protocol 'SequenceType'
Not really sure where to go from here.
Just trying to figure out realm. Completely new to it
Note: realm.deleteAll() works, but I don't want all of my realm deleted, just certain Objects
You're looking for this:
let realm = Realm()
let deletedValue = "two"
realm.write {
let deletedNotifications = realm.objects(Notifications).filter("value == %#", deletedValue)
realm.delete(deletedNotifications)
}
or perhaps this:
let realm = Realm()
let serverValues = ["one", "three"]
realm.write {
realm.delete(realm.objects(Notifications)) // deletes all 'Notifications' objects from the realm
for value in serverValues {
let notification = Notifications()
notification.value = value
realm.add(notification)
}
}
Although ideally, you'd be setting a primary key on Notifications so that you can simply update those existing objects rather than taking the extreme approach of nuking all your local objects simply to recreate them all (or almost).