简体   繁体   中英

swift realm insert array in background thread use in main

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

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
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM