InMemory Realm Threading in Swift
For starters, I'd recommend absolutely checking to make sure your main thread Realm reference isn't being deallocated. I personally would use a singleton object to store the main Realm reference on the main thread. That would be the simplest reason why your Realm is staying empty.
Beyond that, when you do a write to a Realm in the background, those changes are only reflected in the main thread on the next iteration of the run loop. If you need the changes to be reflected before that, you can call realm.refresh()
to explicitly request it to pull the latest changes.
In memory realm loses data even though I am calling from same thread
After reading the documentation of Realm again and again and keep thinking of what @Jay have said in the comments, I paid more attention to this quote from the documentation
Notice: When all in-memory Realm instances with a particular identifier go out of scope with no references, all data in that Realm is deleted. We recommend holding onto a strong reference to any in-memory Realms during your app’s lifetime. (This is not necessary for on-disk Realms.)
Key sentence in the above quote is
When all in-memory Realm instances with a particular identifier go out of scope with no references, all data in that Realm is deleted.
In other words my Realm
object is going out of scope everytime I try to save or fetch or do any other function
for example:
self.queue.async {
guard let realm = try? Realm(configuration: self.configuration) else {
completion(.failure(RealmInMemoryCacheError.realmIsNil))
return
}
guard let object = object as? RealmSwift.Object else {
completion(.failure(RealmInMemoryCacheError.notRealmSpecificModel))
return
}
do {
try realm.write { () -> () in
realm.add(object, update: .all)
completion(.success(()))
return
}
} catch {
completion(.failure(RealmInMemoryCacheError.realmError))
return
}
}
In the above code Realm goes out of scope as soon as that queue is done with it. Then Realm will look if there is any other variable in memory with the same identifier if so then it does nothing otherwise it will delete the current realm to optimize.
So the solution to the problem is basically to create a strong reference to realm with this identifier and then in each function re-create realm with the same identifier as well to avoid Realm accessed from incorrect thread
This will however render in an extra variable that is not used but I think that is okay for now, at least it is my solution until something official comes from Realm. Keep in mind the process of re-initializing should not be an overhead since Realm takes care of that optimization.
Here is what I have done
final class RealmInMemoryCache {
private let configuration: Realm.Configuration
private let queue: DispatchQueue
private let strongRealm: Realm <-- Added variable
init(_ configuration: Realm.Configuration) {
self.queue = DispatchQueue(label: "inMemoryRealm", qos: .utility)
self.configuration = configuration
self.strongRealm = try! Realm(configuration: self.configuration) <-- Initialized here
}
}
and in other functions I do something like the following
...
self.queue.async {
guard let realm = try? Realm(configuration: self.configuration) else {
completion(.failure(RealmInMemoryCacheError.realmIsNil))
return
}
...
What threw me off thinking my Realm was going out of scope is when I setup breakpoints Realm started behaving perfectly fine. Although I still don't know 100% for sure why but my thinking is that xcode debugger might have created a strong reference to Realm when I write the command po realm.objects(...)
in lldb
.
I will accept this answer for now, unless someone has a better solution.
Reaching Realm Results object from different thread gives crash
Realm objects can only be used from the same thread that they were fetched.
So you have 2 main options, if you are going to use any of your data to populate UI, you'll have to fetch the objects in the main thread.
The other option is to create a new Realm instance and fetch the results again in the main thread, creating a Realm is supposed to be really lightweight.
You can also pass objects between threads but that is more complicated.
Realm instances with background process loses data
It sounds like it should work fine. You'll have to try it, and that's probably quicker than waiting for a definitive answer on here.
I'd write a singleton class, that as part of the constructor defines the Realm configuration and stores it, and also opens and stores an instance of that realm. Then I'd add an accessor to that class for your threads to use; it would return a new instance of the realm using the same configuration. When those go out of scope (and you should allow them to go out of scope) your realm will persist due to the singleton instance maintaining its reference.
I may be reading your first referenced question wrongly, but that isn't dealing with in-memory realms and therefore doesn't demand that you hold onto a reference somewhere.
Is it safe to perform Realm local migration in background thread?
Creating a new RLMRealmConfiguration and calling setDefaultConfiguration are trivial operations. Neither of them trigger migrations.
Migrations don't run until you open a realm.
There are a couple of ways to cause the migration to run in a background thread. I'll show you the easiest way in Swift. You'll have to translate this code to Objective-C:
enum Migrator {
static func migrate(completion: @escaping () -> Void) {
let queue = DispatchQueue(label: "migrator.awesome")
let configuration = Realm.Configuration(
schemaVersion: 13,
migrationBlock: { migration, oldSchemaVersion in
if oldSchemaVersion < 1 {
// ...
}
if oldSchemaVersion < 2 {
// ...
}
if oldSchemaVersion < 3 {
// ...
}
// ...
}
)
Realm.asyncOpen(configuration: configuration, callbackQueue: queue) { result in
Realm.Configuration.defaultConfiguration = configuration
switch result {
case .failure(let error):
print("error", error)
case .success(let realm):
print("realm", realm)
break
}
completion()
}
}
}
asyncOpen
will open a realm and run the migration on the specified queue. In your case, you want to pass in a background queue. Once the migration runs asyncOpen
will invoke the specified callback on the same queue you specified.
Once the migration is complete you can switch back to the main thread and do whatever you need to do. And yes, the data that was migrated in the background thread will be available to the main thread.
enum RealmInitializer {
static func initializeRealm() {
Migrator.migrate {
DispatchQueue.main.async {
readAndWriteRealmData()
}
}
}
}
Realm Swift: passing unpersisted objects between threads
You are free to pass unpersisted objects between threads as you wish -- only persisted objects cannot yet be handed over between different threads.
RLMException when save an object in a background thread
Realm Object
instances can't be moved between threads once they've been saved to Realm. Since school
would be an object that was instantiated on the main thread, you're creating a conflict by attaching it to an un-persisted object and moving the lot to a background thread.
To fix this, you'll need to make a background version of the school
object using Realm's thread reference feature.
Also, unless you have a specific reason for creating Person
on the main thread, I'd also recommend moving its creation to the background thread too.
let schoolRef = ThreadSafeReference(to: school)
DispatchQueue.global().async {
do {
let realm = try Realm!
let person = new Person()
person.name = "John"
person.school = realm.resolve(schoolRef)!
try realm.write {
realm.add(person, update: true)
}
DispatchQueue.main.async {
// Success!
}
} catch let error as NSError {
DispatchQueue.main.async {
// Error!
}
}
}
Thread-safe way of instantiating optional Realm objects
Your problem exists due to the fact that you are using ignored properties, which are not persisted by Realm
. According to this GitHub issue about ignored properties, what you experience is the expected behaviour.
From the linked issue:
"Ignored properties are only guaranteed to be consistent across
individual Swift instances, but retrieving an object from a collection
creates a new Swift instance each time, thus the behavior that you
see."
This means that the self.collection.players[i]
inside locationManager(didUpdateLocations:)
is actually a new Swift instance of the same Realm
object that you created in loadPlayers()
, which is not an issue with normal Realm
properties, since they are fetched from the Realm
database, but is causes an issue with ignored properties, since they are linked to a specific Swift
instance and only exist in memory. Hence, when you retrieve a specific object from a collection (Player
instance from `List'), all ignored properties are lost.
To sum it up, you won't be able to use ignored Realm
properties in collections.
Related Topics
Can't Dismiss View Controller That's Embedded in a Navigation Controller
Swiftui - How to Change The Button's Image on Click
Why Does a Simple Swift Arithmetic Operation Compile So Slow
How to Get a Full List of Running Processes
How to Draw an Image in an Nsopenglview with Swift
Swift Struct Adopting Protocol with Static Read-Write Property Doesn't Conform
Update Widget When Appearance Changes
Swift Combine: Turn a Publisher into a Read-Only Currentvaluesubject
How to Replace My .Xib File with Pure Swift 3
Rlmexception', Reason: 'Attempting to Modify Object Outside of a Write Transaction
Swiftui Map Overlays Without UIviewrepresentable
Nskeyedunarchiver Decodeobjectforkey: Cannot Decode Object of Class for Key (Ns.Objects)
The "Funk" Sound When Hitting Escape Key in App
How to Provide Default Implementation of an Objective-C Protocol in a Swift Protocol Extension