@TimOliver
We LOVE to help with any issues or bugs you have!
Questions: If you have questions about how to use Realm, ask on
StackOverflow.
We monitor the realm tag.
Feature Request: Just fill in the first two sections below.
Bugs: To help you as fast as possible with an issue please describe your issue
and the steps you have taken to reproduce it in as many details as possible.
Thanks for helping us help you! :-)
-->
I want to write to my realm file in a background thread while I'm updating my file as to not hold up my UI when a user logs in and by default from my understanding I will have to also read from the file in the background thread.
I have read the swift docs and searched SO but have been unsuccessful in applying the concepts to my app specifically.
I expect my file to get updated from the network in a background thread while the user navigates the app without interruption
The file is still being written to and read from the main thread and the UI is held up for a substantial amount of time while updating data from the network.
While trying to follow the Realm Swift Docs I was only able to get to the point of crashing with the following error message "xerminating app due to uncaught exception 'RLMException', reason: 'Realm accessed from incorrect thread'"
I was able to get rid of that error add get the app running while realm seems to be executing in the background however I'm not sure if its being done correctly or reading from the updated file.
Run project. Realm file should be querying the network upon app launch.
sent full Xcode project via email from [email protected] subject line Background Threading
Realm framework version: ?
2.5.1
Realm Object Server version: ?
Xcode version: ?
8.3.2
iOS/OSX version: ?
Dependency manager + version: ?
Thanks for getting in touch with us.
The same-thread Realm requirement is very simple to fulfill. It boils down to this:
If you create an instance of a Realm, or get a Realm object, list, results, or any other Realm type from a Realm, you can use it only on the same thread. If you want to use the same Realm, object, or whatever on another thread, you have to create a new Realm instance on that other thread and re-fetch the object.
Here are some examples of things you shouldn't do:
Realm in some function. Then you call Async to dispatch a block onto another thread, but in that other block you use the same Realm instance.Realm property. Then you have a method that uses that Realm property, but you call that method from multiple threads.Here are code samples:
// BAD
func doSomethingWrong() {
// Assume doSomething is running on the main queue
let config = // (get a Realm configuration)
let myRealm = try! Realm(configuration: config)
let objects = myRealm.objects(MyObject.self)
// (do more things with myRealm)
DispatchQueue.global(qos: .background).async {
let otherObjects = myRealm.objects(YourObject.self) // this queue is running on a different thread, so you can't use `myRealm` here!
}
}
// OKAY
func doSomethingRight() {
// Assume doSomething is running on the main queue
let config = // (get a Realm configuration)
let myRealm = try! Realm(configuration: config)
let objects = myRealm.objects(MyObject.self)
// (do more things with myRealm)
DispatchQueue.global(qos: .background).async {
// You *can* pass Realm configurations between threads, that's okay
let myBackgroundRealm = try! Realm(configuration: config)
let otherObjects = myBackgroundRealm.objects(YourObject.self) // okay, since we opened myBackgroundRealm on the same thread
}
}
// BAD
class MyRealmManagerWrong {
// Assume we can get the configuration from somewhere; where it comes from isn't important
let theRealm = try! Realm(config: getConfig())
// What if we call this method from any thread other than the one upon which
// MyRealmManager was instantiated? That'll cause a crash.
func getMyObjects() -> Results<MyObject> {
return theRealm.objects(MyObject.self)
}
}
// OKAY
class MyRealmManagerRight {
let theConfig: Realm.Configuration = getConfig()
// This is okay, since we always re-open the Realm and never allow it to escape to another thread.
func getMyObjects() -> Results<MyObject> {
// This is cheap, since the framework caches the expensive parts of a Realm once it's been opened at least once
let theRealm = try! Realm(config: theConfig)
return theRealm.objects(MyObject.self)
}
}
If you change your app's code to take these guidelines into account you should be free of any crashing issues due to accessing Realms or related instances from the wrong threads.
Thanks for getting back to me this is really helpful. Some follow up questions I have are:
In your doSomethingRight() function is the objects constant the same as the objects in myBackgroundRealm.objects?
How does the MyObject object differ from the YourObject object?
I don't need to incorporate the ThreadSafeReference?
Your code sample applies to both reading and writing the realm file, correct?
@austinzheng
To answer your questions:
objects and otherObjects are exactly the same. (There are some subtleties to this, but you won't run into them unless you explicitly go looking for them.)~ No, but if you replaced YourObject with MyObject in the example they would be the same.MyObject and YourObject types are irrelevant. The only point of those lines of code is to show doing _something_ with the Realm: it could be getting objects, starting a write transaction, removing objects from the Realm, etc. If it helps, substitute them mentally with comments saying "do something with this Realm".otherObjects in the async block I could have put the original objects in a thread safe reference and then unpacked the thread safe reference in the block. A thread safe reference is a way to move Realm lists, objects, results, etc between threads safely. I recommend you don't worry about them for now; if you get things working with the techniques I described above you can then decide whether or not it makes sense to use thread safe references.Hope this helps.
Thanks for the extremely comprehensive answers @austinzheng! :)
I've updated a majority of my code and got the realm to be executed in the background, however from my print statements from the console shown below it seems that the Realm is executing on different background threads.
The following print out is from the console is from the following code snippet: Thread 4
false
class SecondaryLaunchViewController: UIViewController {
var cached = CacheRealm()
var defaults = Date()
@IBOutlet weak var launchImage: UIImageView!
override func viewDidLoad() {
super.viewDidLoad()
launchImage.image = UIImage(named: "LaunchImage")
DispatchQueue.global(qos: .background).async {
print(Thread.isMainThread)
print(Thread.current)
self.cached.updateAll()
}
}
The following print out from the console is from the following code snippet: Thread 6
false
class RealmManager {
func Finder(for name:String)->Data {
print(Thread.isMainThread)
print(Thread.current)
let backgroundRealm = try! Realm(configuration: RealmConfig.main.configuration)
print(Thread.isMainThread)
print(Thread.current)
let realmObject: Data
if let found = Data.find(realm: backgroundRealm, searchName: name) {
college = found
} else {
realmObject = Data()
realmObject.name = name
backgroundRealm.add(realmObject)
}
return realmObject
}
}
Hope my follow up question makes sense
@austinzheng
Only the main queue is guaranteed to always execute on the same thread (the main thread). Queues and threads are distinct, and a single queue is usually served by multiple threads. (This means that, even if you open a Realm (or fetch an object, etc) on a given background queue in a given block, you shouldn't use that same instance in a different block even if that block is also run on the same queue.)
Hope that helps.
There has to be something I'm missing because Realm appears to be in the background thread (Thread 5) but my UI is still held up while Realm is being updated by the network. I have attached a file for you to take a look at if you could please and let me know where I'm going wrong.
Thank you
@austinzheng
@alexcowley unfortunately it's not possible to reproduce your case using just a source file you sent us and without the whole xCode project. It also contains a lot of unrelated code that makes the investigation very hard. However I've noticed some issues in your code that can potentially be related:
func collegeFinder(for name:String)->CollegeData {
...
let backgroundRealm = try! Realm(configuration: RealmConfig.main.configuration) // <-- You create a new Realm instance on a caller thread
...
let college: CollegeData
if let found = CollegeData.find(realm: backgroundRealm, searchName: name) {
college = found
} else {
college = CollegeData()
college.name = name
backgroundRealm.add(college) // <-- You modify realm outside write transaction
}
return college // <-- You return `CollegeData` instance from you local `backgroundRealm`
}
later in updateCollege:
func updateCollege(object:PFObject) {
DispatchQueue.global(qos: .background).async {
...
let backgroundRealm = try! Realm(configuration: RealmConfig.main.configuration) // <-- You create a local realm instance on background queue
...
backgroundRealm.beginWrite() <-- Transaction is started on your local Realm instance
// Find or create college
let college = self.collegeFinder(for: collegeName) <-- `college` is fetched or created in a different realm instance from `collegeFinder`
// Add Image
if let image = object["image"] as? String {
college.url = image // <-- You're modifying realm object in another realm's write transaction
print("Successfully retrieved \(object).")
}
...
}
}
Hope this info helps you to find your issue. Please review your code or try to create a simplified version that reproduces it.
Thank you for your response @stel I have emailed my zipped project to your help email when I first opened this ticket with a subject line of "Background Threading".
@austinzheng
@TimOliver
@alexcowley did you try to fix the issues I've mentioned in my previous comment?
Try to do this:
//This method will run on background, to maintain your Realm updated, you need to say what are the process.
DispatchQueue.global(qos: .background).sync {
DispatchQueue(label: "first").sync {
//Run first method, create first instance of Realm.
}
DispatchQueue(label: "second").sync {
//Run second method, create second instance of Realm.
}
}
@stel yes I have, I removed the local realm instances but left the realm write transactions as is. See attached files.

CacheRealm.swift.zip
SecondaryLaunchViewController.swift.zip
You're still getting this exception because you're starting a write transaction for realm created on a different thread. If you go down by the stack trace in debugger you will find the place in your code that causes this issue.
... you cannot have multiple threads sharing the same instances of Realm objects. If multiple threads need to access the same objects they will each need to get their own instances
please learn more in docs: https://realm.io/docs/swift/latest/#threading
@austinzheng
class databaseManager {
func fetchData() -> person {
Realm.Configuration.defaultConfiguration = config
let nill=person()
if let results = self.database.objects(person.self).first {
return results
}else {
return nill
}
}
func saveData(ps: person) ->Bool {
try! self.database.write {
print("fsdfsf",ps)
self.database.add(ps, update: true)
}
return true
}
}
could some one modify this class for singleton object ??
Most helpful comment
Thanks for getting in touch with us.
The same-thread Realm requirement is very simple to fulfill. It boils down to this:
If you create an instance of a
Realm, or get a Realm object, list, results, or any other Realm type from a Realm, you can use it only on the same thread. If you want to use the same Realm, object, or whatever on another thread, you have to create a newRealminstance on that other thread and re-fetch the object.Here are some examples of things you shouldn't do:
Realmin some function. Then you callAsyncto dispatch a block onto another thread, but in that other block you use the sameRealminstance.Realmproperty. Then you have a method that uses thatRealmproperty, but you call that method from multiple threads.Here are code samples:
If you change your app's code to take these guidelines into account you should be free of any crashing issues due to accessing Realms or related instances from the wrong threads.