2

I have created a Database class as so:

class Database {
    static let instance = Database()
    private let categories = Table("Category")
    private var db: Connection?

    let cat_id = Expression<String>("id")
    let cat_name = Expression<String>("name")


    private init() {
        let path = NSSearchPathForDirectoriesInDomains(
            .documentDirectory, .userDomainMask, true
            ).first!
        do {
            db = try Connection("\(path)/SalesPresenterDatabase.sqlite3")
            createTable()
        } catch {
            print("error")
        }
    }
    func createTable() {
        do{
            try self.db!.run(self.categories.create(ifNotExists: true) { table in
                table.column(self.cat_id)
                table.column(self.cat_name)
            })
        }catch{
             print("error")
        }
    }

    func addRow(table: DBTableNames, object: [Any]) -> Int64? {
      do {
         try self.db!.transaction() {
            for obj in object{
                if table.rawValue == DBTableNames.Category.rawValue{
                    let cats : CategoryObject = obj as! CategoryObject
                    let insert = self.categories.insert(self.cat_id <- cats.id,
                                                        self.cat_name <- cats.name)
                    try self.db!.run(insert)

                }
             }
          }
        }catch{
           print("Insert failed \(error)")
           return -1
        }
        return 1
         }
}

I then go on to call my code to add a row by running the following:

    let returnValue = Database.instance.addRow(table: DBTableNames(rawValue: entity)!,
                                               object: databaseObject)

The problem I have is that it always throws an error saying:

Insert failed The operation couldn’t be completed. (SQLite.Result error 0.) and full: cannot rollback - no transaction is active (code: 1)

If I see this error one more time my Mac will be going out of the window!

The whole operation is in a background thread but I have tried the following as well:

    DispatchQueue.main.async{
    let returnValue = Database.instance.addRow(table: DBTableNames(rawValue: entity)!,
                                                   object: databaseObject)
}

It didn't work. It doesn't make sense as I also tried creating the tables in a transaction and that worked perfectly.

Any help would be greatly appreciated!

Richard Thompson
  • 404
  • 8
  • 23
  • What happens when you run this in the playground? – rmon2852 Apr 13 '17 at 10:07
  • Ok I'm starting to believe this to be a threading issue. I can get this working in isolation of the rest of the code. Now one thing I haven't stated is that the code is executed from an Alamofire completion block... – Richard Thompson Apr 13 '17 at 10:40

1 Answers1

0

Your func createTable() and func addRow() methods are not thread safe. Multiple threads can access it at the same time.

Create a private serial DispatchQueue inside your Singleton class and do above functions through this serial queue. This will prevent accessing the database from several threads at the same time, and serial queue will queue concurrent tasks.

Rukshan
  • 7,902
  • 6
  • 43
  • 61
  • Yes, or use an SQLite library that does all the difficult threading job for you: https://github.com/groue/GRDB.swift/blob/master/README.md#concurrency – Gwendal Roué May 09 '17 at 08:46