Reputation: 1795
I've created a model named "File", and it looks OK with Realm Browser:
but when I use the model, it will return error:
libc++abi.dylib: terminating with uncaught exception of type realm::IncorrectThreadException: Realm accessed from incorrect thread.
In my code, I create the Realm object every where I needs add/update:
private var allFiles : Results<File>!
private var downloadingFiles : Results<File>! {
return self.allFiles.filter("completed = false")
}
private var downloadedFiles : Results<File>! {
return self.allFiles.filter("completed = true")
}
private var downloading = false
private var request: Alamofire.Request?
func download() {
let fileRealm = try! Realm()
allFiles = fileRealm.objects(File).sorted("updatedAt")
downloadFile()
}
private func downloadFile() {
if !self.downloading, let file = self.downloadingFiles.first where !file.completed {
self.reqForDownload(file)
}
}
private func reqForDownload(file: File) -> Void {
downloading = true
request = Alamofire
.download(.GET, file.url, destination: { (url, response) -> NSURL in
return NSURL(fileURLWithPath: file.filePath)
})
.progress { [unowned self](bytesRead, totalBytesRead, totalBytesExpectedToRead) in
dispatch_async(dispatch_get_main_queue(), {
let variable = Float(totalBytesRead)/Float(totalBytesExpectedToRead)
debugPrint(variable)
})
}
.response { [unowned self](request, response, data, error) in
if let error = error {
dispatch_async(dispatch_get_main_queue(), {
let fileRealm = try! Realm()
try! fileRealm.write({
file.completed = false
})
self.allFiles = fileRealm.objects(File).sorted("updatedAt")
})
if error.code == NSURLErrorCancelled {
debugPrint("Canceled download")
}
} else {
debugPrint("Downloaded file successfully")
dispatch_async(dispatch_get_main_queue(), {
let fileRealm = try! Realm()
try! fileRealm.write({
file.completed = true
})
self.allFiles = fileRealm.objects(File).sorted("updatedAt")
})
}
self.downloading = false
}
}
I'm new for Realm but I know the Realm is not thread safe, so I'm tried to use the object in main thread as my code but the error still appeared. Please someone help me, thank you.
I've update my code as @TimOliver's suggest, but it still response the same error. New code as below:
private var allFiles : Results<File>!
private var downloadingFiles : Results<File>! {
return self.allFiles.filter("completed = false")
}
private var downloadedFiles : Results<File>! {
return self.allFiles.filter("completed = true")
}
private var downloading = false
private var request: Alamofire.Request?
func download() {
let fileRealm = try! Realm()
allFiles = fileRealm.objects(File).sorted("updatedAt")
downloadFile()
}
private func downloadFile() {
if !self.downloading, let file = self.downloadingFiles.first where !file.completed {
self.reqForDownload(file)
}
}
private func reqForDownload(file: File) -> Void {
downloading = true
request = Alamofire
.download(.GET, file.url, destination: { (url, response) -> NSURL in
return NSURL(fileURLWithPath: file.filePath)
})
.progress { [unowned self](bytesRead, totalBytesRead, totalBytesExpectedToRead) in
dispatch_async(dispatch_get_main_queue(), {
let variable = Float(totalBytesRead)/Float(totalBytesExpectedToRead)
debugPrint(variable)
})
}
.response { [unowned self](request, response, data, error) in
if let error = error {
let fileRealm = try! Realm()
try! fileRealm.write({
file.completed = false
})
self.allFiles = fileRealm.objects(File.self).sorted("updatedAt")
if error.code == NSURLErrorCancelled {
debugPrint("Canceled download")
}
} else {
debugPrint("Downloaded file successfully")
let fileRealm = try! Realm()
try! fileRealm.write({
file.completed = true
})
self.allFiles = fileRealm.objects(File.self).sorted("updatedAt")
}
self.downloading = false
}
}
Upvotes: 2
Views: 2100
Reputation: 10573
self.allFiles = fileRealm.objects(File.self).sorted("updatedAt")
in the .response()
closure was excuted sub thread. So you access self.allFiles
on main thread later, it would crash.
Results instances are live, auto-updating views into the underlying data, which means results never have to be re-fetched. They always reflect the current state of the Realm on the current thread, including during write transactions on the current thread.
https://realm.io/docs/swift/latest/#auto-updating-results
So you do not need to re-fetch allFiles
. A transaction was committed, allFiles
are automatically up to date.
Upvotes: 1
Reputation: 15991
Like I asked in the comments, if you set an exception breakpoint, you can see exactly which line of code is triggering the Realm exception so you can track in which thread the Realm transaction is occurring, as well as which objects are interacting with it.
If I recall correctly, I believe the closure called in the .response
portion of that method doesn't get called on the main thread by default, however you're attempting to modify the file
object that was definitely queried for on the main thread.
Short of forcing every closure in there to be called on the main thread, it would be more appropriate to have a primary key property in your file
object, hold a reference directly to the primary key value, and then directly query for a thread local version of the file
object when you need to update it (i.e. using the Realm.object(ofType: primaryKey:)
method.
Upvotes: 2