In my iOS app I am using the Realm library for storing data. It works great, until for one reason or the other, object get invalidated. (reasons in the specific case could be that I sometimes implement background jobs on the same data that are getting visualized on view, or similar scenarios).
I know why it happens, and I understand is correct behavior but from there on: what is the correct behavior I should implement?
I would love to pull the data from Realm again and continue my job, but when an object gets invalidated, every field is inaccessible and crashes the environment (so, I don't know what is the unique+immutable id of the object).
How can I safely pull the current data from that object again, without storing the id in the ViewController?
Here is some code. I had to edit it heavily to since the structure of the app is different, but the issue is still exemplified. Assume that the table view delegate's is the view and all the technicalities are there.
// A class variable
var events: RLMResults<RLMMyEvent>
// A table view that shows all "MyEvents"
var tableview: UITableView
func initialiseView(_ predicate: NSPredicate) {
// Get the events with a predicate from Realm
events = RLMMyEvent.objects(with: predicate)
tableview.reloadData()
}
// All tableView delegates, and in particular
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: reuseIdentifier, for: indexPath) as! MyCell
let event = self.events[UInt(indexPath.row)]
if !event.isInvalidated {
} else {
/***** HERE is the problem. What to do here?
HOW to get those data again, since I cannot
get that event's unique ID? ****/
}
cell.initialize(event)
return cell
}
As I understand, you want to access object's properties when the object is invalidated. Correct me if I'm wrong :)
First of all, let take a look at isInvalidated property
Indicates if the object can no longer be accessed because it is now invalid.
An object can no longer be accessed if the object has been deleted from the Realm that manages it, or if invalidate() is called on that Realm.
It means that an object can be invalidated only when it's managed by a Realm.
Detach object from the Realm which managers it. If object isn't managed by any Realm, of course it will never be invalidated and you can access properties as you want.
Whenever you fetch an object from Realm, detach it (create a cloned object).
Add below code to your project. It's used to detach every object in Result
after fetching from Realm. I found here
protocol DetachableObject: AnyObject {
func detached() -> Self
}
extension Object: DetachableObject {
func detached() -> Self {
let detached = type(of: self).init()
for property in objectSchema.properties {
guard let value = value(forKey: property.name) else { continue }
if property.isArray == true {
//Realm List property support
let detachable = value as? DetachableObject
detached.setValue(detachable?.detached(), forKey: property.name)
} else if property.type == .object {
//Realm Object property support
let detachable = value as? DetachableObject
detached.setValue(detachable?.detached(), forKey: property.name)
} else {
detached.setValue(value, forKey: property.name)
}
}
return detached
}
}
extension List: DetachableObject {
func detached() -> List<Element> {
let result = List<Element>()
forEach {
if let detachable = $0 as? DetachableObject {
let detached = detachable.detached() as! Element
result.append(detached)
} else {
result.append($0) //Primtives are pass by value; don't need to recreate
}
}
return result
}
func toArray() -> [Element] {
return Array(self.detached())
}
}
extension Results {
func toArray() -> [Element] {
let result = List<Element>()
forEach {
result.append($0)
}
return Array(result.detached())
}
}
Instead of keep events
as RLMResults<RLMMyEvent>
, keep it as [RLMMyEvent]
.
After fetching result, detach objects and convert to an array
events = RLMMyEvent.objects(with: predicate).toArray()
Now you can access properties without being afraid of invalidated objects and crash.
Note that detached objects will not be updated if the original objects or their values inside Realm are changed.