Kevin Maldjian
Kevin Maldjian

Reputation: 147

Swift GRDB Writing to Database Error "attempt to write a readonly database"

I am attempting to write to a database of quotes I have created. If the user selects the "favorite" button, it will add a 1 (or true) value into the favorite column on the SQLITE database. For some reason I cannot write to the database and when I execute this code:

    @IBAction func addFavorite(_ sender: Any) {
        var configuration = Configuration()
        configuration.readonly = false
        let dbPath = Bundle.main.path(forResource: "data", ofType: "db")!
        let dbQueue = try! DatabaseQueue(path: dbPath, configuration: configuration)

        try! dbQueue.inDatabase { db in
            try db.execute("""
UPDATE quotes SET favorite = 1 WHERE quote = ?;
""",
                                           arguments: [quoteLabel.text])
    }

I expect the result to write to the database and place a 1 in the favorite column but sadly this is not the case. I get this error:

Thread 1: Fatal error: 'try!' expression unexpectedly raised an error: SQLite error 8 with statement UPDATE quotes SET favorite = 1 WHERE quote = ? arguments ["Abstinence is the great strengthener and clearer of reason."]: attempt to write a readonly database

I have tried to chmod 755 the database and that did not work either. Am I missing something?

Upvotes: 3

Views: 1587

Answers (1)

Gwendal Roué
Gwendal Roué

Reputation: 4044

The path to your database is a path to an application resource. App resources can not be modified in iOS.

See File System Basics for more information. Particularly relevant quote:

[...] the app’s bundle. This directory contains the app and all of its resources.

You cannot write to this directory. To prevent tampering, the bundle directory is signed at installation time. Writing to this directory changes the signature and prevents your app from launching. You can, however, gain read-only access to any resources stored in the apps bundle. For more information, see the Resource Programming Guide.

Now, how do you write in your database, if it can't be modified?

Well, you copy it in a place where you can modify it. You'll never modify the bundled database resource, and only ever modify the copy.

There is available documentation again: the How do I open a database stored as a resource of my application? GRDB FAQ says:

If the application should modify the database resource, you need to copy it to a place where it can be modified. For example, in the Application Support directory. Only then, open a connection:

let fileManager = FileManager.default

let dbPath = try fileManager
    .url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
    .appendingPathComponent("db.sqlite")
    .path

if !fileManager.fileExists(atPath: dbPath) {
    let dbResourcePath = Bundle.main.path(forResource: "db", ofType: "sqlite")!
    try fileManager.copyItem(atPath: dbResourcePath, toPath: dbPath)
}

let dbQueue = try DatabaseQueue(path: dbPath)

Upvotes: 6

Related Questions