Search code examples
iosswiftxcodemultithreadingrealm

'RLMException', reason: 'Realm accessed from incorrect thread.'


i need to write my person model to realm db. i never understod what means because i'm using one instance of realm and i think i'm using the same thread.am i wrong ??

 func updatePerson(ps:personTmp){



        let db=fetchPerson()

        guard let url=URL(string: "\(address)pe?Authorization=\(db.serverToken)") else {return}

        var urlRequest=URLRequest(url: url)

        urlRequest.httpMethod="PUT"
        urlRequest.addValue("Application/Json", forHTTPHeaderField: "Content-type")

        print(ps.dictionary)
        let body = try! JSONSerialization.data(withJSONObject: ps.toJSON(), options: .prettyPrinted)

         urlRequest.httpBody=body

        URLSession.shared.dataTask(with: urlRequest) { (data, response, err) in


            if let content=data {

                let json = try! JSONSerialization.jsonObject(with: content, options: .allowFragments) as! [String:AnyObject]

                let ersonJson =  json["data"] as! AnyObject

                let tmp = Mapper<person>().map(JSONObject: ersonJson)!
                tmp.serverToken = db.serverToken

                print(tmp)

                     self.dbManager.deleteAllFromDatabase()

                            if self.dbManager.saveData(ps: tmp) {


                            }
}

and this is my

class databaseManager {


    var database:Realm
    //var shareInstance=databaseManager()

     static let share=databaseManager()
    var config = Realm.Configuration()

    init() {
        config = Realm.Configuration(

            // set a new version number, the version number must bigger than before

            // if you never set it, it's 0

            schemaVersion: 1,

            migrationBlock: { migration, oldSchemaVersion in

                if (oldSchemaVersion < 1) {
                    // do nothing
                }
        })

        // tell Realm the new config should be used

        Realm.Configuration.defaultConfiguration = config

        // open realm file and it will do auto-migration



        self.database = try! Realm()
    }




    func fetchData() ->   person {

        let nill=person()

        DispatchQueue.global(qos: .background).async {

        Realm.Configuration.defaultConfiguration = self.config

        }

        if let results =  self.database.objects(person.self).first {

            return results

        }else {

            return nill
        }



    }





    func saveData(ps: person) ->Bool  {

        var bool:Bool?



            try! self.database.write {

                if (ps.serverToken.isEmpty || ps.identityId.isEmpty) {
                    bool=false

                }else {
                    bool = true
                    self.database.add(ps, update: true)

                }

            }



        return bool!


}





    func deleteAllFromDatabase()  {



        DispatchQueue.main.async {

            try!   self.database.write {

                self.database.deleteAll()

            }

        }


    }

could you tell me what is wrong with my saving function ?? what would I do ? how can I write my model withOut thread conflicting ?


Solution

  • Using a Singleton does not mean that it will always be called on the same thread... for example..

    // 1. here you are likely in the main thread
    URLSession.shared.dataTask(with: urlRequest) { (data, response, err) in 
       // 2. here you are in a background thread
    }
    

    Your singleton (databaseManager) has likely been created on the main thread. When you are within a closure of an asynchronous function as in point 2 above, you are in a background thread. So if you create the objects in the background thread and then pass it to databaseManager to save, you are passing an object created on a background thread, to a realm instance that was created on the main thread. Causing your issue.

    If I remember rightly, you should be able to dispatch to the main thread within the closure and get rid of this error:

    DispatchQueue.main.async {
        let json = try! JSONSerialization.jsonObject(with: content, options: .allowFragments) as! [String:AnyObject]
        let ersonJson =  json["data"] as! AnyObject
        let tmp = Mapper<person>().map(JSONObject: ersonJson)!
        tmp.serverToken = db.serverToken
    
        self.dbManager.deleteAllFromDatabase()
        if self.dbManager.saveData(ps: tmp) {
    
        }
    }