2

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 
    }
}
Raymond Liao
  • 1,799
  • 3
  • 20
  • 32
  • I think we're going to need some more information to nail this down. Can you please add an exception breakpoint (http://stackoverflow.com/questions/17802662/exception-breakpoint-in-xcode) and let me know the part of the code where it's triggering? – TiM Oct 24 '16 at 22:21

2 Answers2

2

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.

TiM
  • 15,812
  • 4
  • 51
  • 79
  • Sorry for my late reply. As your mentioned I've made a test, you are correct. In the '.download' and '.response' portions are not in the main thread, but I used the Realm object in them. Thanks for your kindly help. – Raymond Liao Nov 16 '16 at 02:07
1

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.

kishikawa katsumi
  • 10,418
  • 1
  • 41
  • 53