Reputation: 251
I have read the other answers but couldn't find a suitable solution.
I have a product which is uploaded to server only if all IMAGES belonging to that product have finished uploading. The product's details (along with the images) are filled on view controller 1 and then he is taken to the next screen (view controller 2), regardless of whether all images have finished uploading or not. MY VC1 completes the product upload process like this.
let areAllImagesUploaded = RetailProductsService.sharedInstance.checkProductImageDependency(realm!, uuid: retailProduct.getClientId())
if areAllImagesUploaded {
uploadProductToServer(retailProduct)
} else {
do {
try realm = Realm()
RetailProductsService.sharedInstance.updateSyncStatusForSellerProduct(realm!, clientId: retailProduct.getClientId(), syncStatus: ProductSyncStatus.SYNC_FAILED)
let groupT = dispatch_group_create()
for sellerSKU in retailProduct.sellersSKUs {
for productImage in sellerSKU.productImages {
dispatch_group_enter(groupT)
let imageUploadInfo = ImageUploadInfo(productId: retailProduct.getClientId(), imageId: sellerSKU.getId(),imageData: productImage.imageData, uploadURL: ServerConfig.RETAIL_SERVER_UPLOAD_FILE_URL)
ImageUploadManager.sharedInstance.queueImageForUpload(imageUploadInfo, completion: { (success, error) -> Void in
dispatch_group_leave(groupT)
})
dispatch_group_notify(groupT, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), { () -> Void in
//self.uploadProduct(retailProduct)
self.uploadProductToServer(retailProduct) // Fails here
})
}
}
} catch {
print("Error in saving product.")
}
}
I have marked the line on which I get this error. My app has moved to the next view controller while this function in view controller 1 continues uploading images and as soon as all images associated the product are uploaded to server, it tries to upload the product. However it fails with this exception.
Terminating app due to uncaught exception 'RLMException', reason: 'Realm accessed from incorrect thread'
Please help!
Upvotes: 0
Views: 2153
Reputation: 4726
We need to understand the fact Realm Objects cannot be accessed from different threads. What does this means and how to workout this issue.
First, realm objects cannot be access from different thread means, one instance of thread defined in one thread cannot be access from different thread. What we should do actually is we need to have different instance of realm instance for each thread.
For eg. let's look at following e.g. where we insert 50 records in database asynchronously in background thread upon button click and we add notification block in main thread to update the no of people in count label. Each thread (main and background ) have its own instance of realm object to access Realm Database. Because Realm Database is thread safe.
class Person: Object {
dynamic var name = ""
convenience init(_ name: String) {
self.init()
self.name = name
}
}
override func viewDidAppear(_ animated: Bool) {
let realmMain = try! Realm ()
self.people = realmMain.objects(Person.self)
self.notification = self.people?.addNotificationBlock{ [weak self] changes in
print("UI update needed")
guard let countLabel = self?.countLabel else {
return
}
countLabel.text = "Total People: \(String(describing: self?.people?.count))"
}
}
@IBAction func addHandler(_ sender: Any) {
print(#function)
let backgroundQueue = DispatchQueue(label: "com.app.queue",
qos: .background,
target: nil)
backgroundQueue.async {
print("Dispatched to background queue")
let realm = try! Realm()
try! realm.write {
for i in 1..<50 {
let name = String(format: "rajan-%d", i)
//print(#function, name)
realm.add(Person(name))
}
}
}
}
Upvotes: 1
Reputation: 11
Realm Objects cannot be accessed from different threads. Your retailProduct
is created or fetched from Realm storage by threadX by than you switch to some other thread (threadY) by invoking dispatch_group_notify
. To fix the exception you might want to do something like this:
I assume that your retailProduct
object is of type RetailProduct and has an id property used as primary key in Realm storage. Of course you can fetch you retailProduct
by using another query than that.
let retailProductId = retailProduct.id
dispatch_group_notify(groupT, dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), { () -> Void in
// threadY executing this lines
if let retailProduct = realm.objectForPrimaryKey(RetailProduct.self, key: retailProductId){
self.uploadProductToServer(retailProduct)
}
})
Upvotes: 1