Reputation: 846
I have an array of objects received via rest response, which I want to insert into realm db in a background thread and use in a uicollectionview in main thread. As soon as I receive response from rest, I call callback function and insert array into db in a background thread. The problem when I am trying to access in main thread property of object being inserted in background I'm getting exception (see below) which I assume because of object not yet inserted
Terminating app due to uncaught exception 'RLMException', reason: 'Realm accessed from incorrect thread.
The model
class User : Object, Mappable {
dynamic var firstName: String?
dynamic var lastName: String?
required convenience init?(map: Map){
self.init()
}
func mapping(map: Map) {
firstName <- map["firstName"]
lastName <- map["lastName"]
}
}
Inserting in a background thread...
DispatchQueue.global().async {
let realm = try! Realm()
try! realm.write {
realm.add(users)
}
}
Rendering in UI...
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = self.collectionView.dequeueReusableCell(withReuseIdentifier: "MyCell", for: indexPath) as! UserViewCell
let user = users[indexPath.row]
cell.firstName.text = user.firstName
cell.lastName.text = user.lastName
}
Please note that exception occur either on accessing firstName or lastName.
Please let me know what I'm doing wrong here
Upvotes: 1
Views: 2848
Reputation: 54706
The easiest solution is to create a new reference to your Realm instance on the main thread and fetch all users from realm using the newly created reference, so you will be accessing realm from the same thread.
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = self.collectionView.dequeueReusableCell(withReuseIdentifier: "MyCell", for: indexPath) as! UserViewCell
let users = try! Realm().objects(User.self)
let user = users[indexPath.row]
cell.firstName.text = user.firstName
cell.lastName.text = user.lastName
}
Another solution is to use a ThreadSafeReference
object to pass the users
array from the background thread to the main thread. However, you can only create a single ThreadSafeReference
to your collection of users
if the type of users
is either Results
of List
. See below code assuming users
if of type Results<User>
.
var usersRef: ThreadSafeReference<Results<User>>?
DispatchQueue.global().async {
autoreleasepool{
let realm = try! Realm()
try! realm.write {
realm.add(users)
}
usersRef = ThreadSafeReference(to: users)
}
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = self.collectionView.dequeueReusableCell(withReuseIdentifier: "MyCell", for: indexPath) as! UserViewCell
let realm = try! Realm()
guard let usersRef = usersRef, let users = realm.resolve(usersRef) else {return}
let user = users[indexPath.row]
cell.firstName.text = user.firstName
cell.lastName.text = user.lastName
}
Upvotes: 3