Search code examples
iosswiftsqliteswiftuisqlite.swift

Connecting SQLite 3 Database to Swift


I'm attempting to open an SQLite3 database in my SwiftUI code, to access the file path within the bundle, I'm using the following function;

let path = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)
    .first!

However I keep getting the error unable to open database file (code: 14), but after trying to run it a few times I realized that the second last folder from path is different every time I run the code. How can I find a path to the bundle that will not change so the code works?

These are some examples of how the path changes:

Second last folder is named: CAC91DB3-E264-436F-89A8-1E3D76C4DC56

/Library/Developer/CoreSimulator/Devices/EFD14A1B-7207-4840-9ACE-8E44A269CC70/data/Containers/Data/Application/CAC91DB3-E264-436F-89A8-1E3D76C4DC56/Documents

Second last folder is named: 0A23051F-9362-4B4D-B49B-BE0F028384DC

/Library/Developer/CoreSimulator/Devices/EFD14A1B-7207-4840-9ACE-8E44A269CC70/data/Containers/Data/Application/0A23051F-9362-4B4D-B49B-BE0F028384DC/Documents

Edit: This is the code that I am using:

Import swiftUI
Import Foundation
Import SQLite

func openDatabase() {
    
    let ProductID = Expression<Int64>("ProductID")
    let Name = Expression<String>("Name")
    let Calories = Expression<Double>("Calories")
    let Protein = Expression<Double>("Protein")
    let Carbs = Expression<Double>("Carbs")
    let Fats = Expression<Double>("Fats")
    let Weight = Expression<Double>("Weight")
    let Category = Expression<String>("Category")
    
    
    let documentDirectory = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first!
    let fileURL = documentDirectory.appendingPathComponent("Database.db")
    
    let db = try! Connection("\(fileURL)")
    
    let Products = Table("Products")
    
   //Thread 1: Fatal error: 'try!' expression unexpectedly raised an error: no such table: Products (code: 1)
    for prID in try! db.prepare(Products) { 
        print("productid: \(prID[ProductID]), name: \(prID[Name]), calories: \(prID[Calories]), protein: \(prID[Protein]), carbs: \(prID[Carbs]), fats: \(prID[Fats]), weight: \(prID[Weight]), category: \(prID[Category])")
        
        DatabaseTest().dataTest.append(Product(name: prID[Name], calories: prID[Calories], protein: prID[Protein], carbs: prID[Carbs], fats: prID[Fats], weight: prID[Weight]))
        
    }
    
}

For clarification on sqlite.swift reference: (https://github.com/stephencelis/SQLite.swift/blob/master/Documentation/Index.md)


Solution

  • This is because all applications in iOS are sandboxed. There is no way to avoid that. You can not save the full path. It will change every time you launch your App. What you need is to save only your file name and its parent directory. Use them to contruct a new fileURL and/or path every time you need to access them.

    edit/update:

    If your file is located inside your App Bundle:

    let databaseURL = Bundle.main.url(forResource: "Database", withExtension: "db")!
    

    Note: If you need the database path you have to get your fileURL path property not the absoluteString. Last but not least the Bundle is read only. You need to move/copy your database to another directory (application support) to be able to modify it.

    let databasePath = databaseURL.path