Raymond Liao
Raymond Liao

Reputation: 1795

swift realm::IncorrectThreadException: Realm accessed from incorrect thread

I've created a model named "File", and it looks OK with Realm Browser: 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

Answers (2)

kishikawa katsumi
kishikawa katsumi

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

TiM
TiM

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

Related Questions