SQLite db create error in real iOS device - ios

I'm trying to learning swiftUi and I've included SQLIte in my project. I've successfully created db, tables and func to read and insert records and I tested it in Xcode emulator and all works fine. I'm using Xcode 12.1. When I try to launch my app to real device (iphone 11, software version 13.5.1), the db isn't create and I get this error:
2021-04-14 18:00:59.296690+0200 playToys[285:7409] [logging-persist]
cannot open file at line 43353 of [378230ae7f] 2021-04-14
18:00:59.296742+0200 playToys[285:7409] [logging-persist]
os_unix.c:43353: (0)
open(/var/mobile/Containers/Data/Application/3D858B04-DA3A-488E-804A-DFD13DD882E3/Documents.playToysDb.sqlite)
Undefined error: 0 There is error in creating DB puntamento creazionefile:///var/mobile/Containers/Data/Application/3D858B04-DA3A-488E-804A-DFD13DD882E3/Documents.playToysDb.sqlite/
2021-04-14 18:00:59.298066+0200 playToys[285:7409] [logging] API call
with NULL database connection pointer 2021-04-14 18:00:59.298104+0200
playToys[285:7409] [logging] misuse at line 131400 of [378230ae7f]
Prepration table cliente fail 2021-04-14 18:00:59.298127+0200
playToys[285:7409] [logging] API call with NULL database connection
pointer 2021-04-14 18:00:59.298142+0200 playToys[285:7409] [logging]
misuse at line 131400 of [378230ae7f] Prepration createing Currencies
table fail 2021-04-14 18:00:59.298161+0200 playToys[285:7409]
[logging] API call with NULL database connection pointer 2021-04-14
18:00:59.298174+0200 playToys[285:7409] [logging] misuse at line
131400 of [378230ae7f] Prepration createing Categorie table fail
This is a part of code to create and open db:
class DBHelper {
static let dbHelperInstance = DBHelper()
var db : OpaquePointer?
var path : String = "playToysDb.sqlite"
private init() {}
func initDatabase(){
self.db = createDB()
self.createTableCurrency()
self.initDefaultItems()
}
func createDB() -> OpaquePointer? {
let filePath = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathExtension(path)
var dbPointer : OpaquePointer? = nil
if sqlite3_open(filePath.path, &dbPointer) != SQLITE_OK {
print("There is error in creating DB")
return nil
}else {
print("Database has been created with path \(path)")
return dbPointer
}
}
func openDatabase()
{
let fileURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
.appendingPathExtension(path)
if sqlite3_open(fileURL.path, &db) == SQLITE_OK {
print("Database Created !!")
}
}
}
Any idea? Thank you

You are using appendingPathExtension. That is for adding an extension to a filename. You meant appendingPathComponent, which is for adding an entire filename (or other path component) to a URL.
That is why you are seeing a . between Documents and your filename, rather than a /, in the path shown in your error message. The simulator will blithely create a file called Documents.playToysDb.sqlite in your app’s root directory, but a device cannot. It expects it to be within the Documents directory, resulting in the failure when trying to create that file.
You asked about the correct way to get the database URL. A few observations:
You asked about urls(for:in:)[0] vs url(for:in:appropriateFor:create:). I would use the latter.
I would advise using .applicationSupportDirectory rather than .documentDirectory. The former is for the app’s support files. The latter is intended for user-facing files, which is not appropriate here. You can use .documentDirectory, but I would not advise it.
I would also avoid repeating this open in createDB and openDatabase. In fact, I would have a single method that would try to open the existing database, and if it fails, only then create the database and create/populate the tables.
Implicit in the above point, I would advise sqlite3_open_v2, in which you can chose to create a blank database or not. So I call it first without SQLITE_OPEN_CREATE (which, if successful, I know the database has already been created). Only if that fails do I try it again with that option (and then proceed to create the necessary tables).
Thus:
class DBHelper {
static let shared = DBHelper()
private var db: OpaquePointer?
private let filename = "playToysDb.sqlite"
private init() {
openDatabase()
}
}
private extension DBHelper {
var errorMessage: String {
sqlite3_errmsg(db).flatMap { String(cString: $0) } ?? "Unknown Error"
}
func openDatabase() {
let fileURL = try! FileManager.default
.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent(filename)
// try to open existing database (but don't create); if successful, return immediately
if sqlite3_open_v2(fileURL.path, &db, SQLITE_OPEN_READWRITE, nil) == SQLITE_OK {
print("Database has been opened with path \(fileURL.path)")
return
}
// if you got here, clean up and then try creating database
sqlite3_close(db) // clean up after the above `sqlite3_open` call
guard sqlite3_open_v2(fileURL.path, &db, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE, nil) == SQLITE_OK else {
print("There is error in creating DB: ", errorMessage)
sqlite3_close(db) // if creation failed, make sure to again clean up
db = nil
return
}
// if you got here, we created blank database, so populate however you want
createTableCurrency()
populateDefaultItems()
print("Database has been created with path \(fileURL.path)")
}
func createTableCurrency() { ... }
func populateDefaultItems() { ... }
}
Note, in addition to the above, I have tackled a few unrelated observations:
As a matter of convention, I used shared for the name of the shared instance.
I would mark the private properties and methods as such, to avoid external manipulation of them.
If a query ever fails, I would call sqlite3_errmsg to get the error reported by SQLite.
If open fails, always call sqlite3_close. As the docs say:
Whether or not an error occurs when it is opened, resources associated with the database connection handle should be released by passing it to sqlite3_close() when it is no longer required.

thank you for reply. I solved my issue using appendingPathComponent and using the first directory available for the user. Is that a correct way?
class DBHelper {
static let dbHelperInstance = DBHelper()
var db : OpaquePointer?
var path : String = "playToysDb.sqlite"
private init() {}
func initDatabase(){
self.db = createDB()
self.createTableCurrency()
self.initDefaultItems()
}
func createDB() -> OpaquePointer? {
let filePath = getDocumentsDirectory().appendingPathComponent(path);
var dbPointer : OpaquePointer? = nil
if sqlite3_open(filePath.path, &dbPointer) != SQLITE_OK {
print("There is error in creating DB")
return nil
}else {
print("Database has been created with path \(path)")
return dbPointer
}
}
func openDatabase()
{
let filePath = getDocumentsDirectory().appendingPathComponent(path);
if sqlite3_open(fileURL.path, &db) == SQLITE_OK {
print("Database Created !!")
}
}
func getDocumentsDirectory() -> URL {
// find all possible documents directories for this user
let paths = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask)
// just send back the first one, which ought to be the only one
return paths[0]
}
}
This approach works too, which is the differences?
class DBHelper {
static let dbHelperInstance = DBHelper()
var db : OpaquePointer?
var path : String = "playToysDb.sqlite"
private init() {}
func initDatabase(){
self.db = createDB()
self.createTableCurrency()
self.initDefaultItems()
}
func createDB() -> OpaquePointer? {
let fileURL = try! FileManager.default
.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent(path)
var dbPointer : OpaquePointer? = nil
if sqlite3_open(fileURL.path, &dbPointer) != SQLITE_OK {
print("There is error in creating DB")
return nil
}else {
print("Database has been created with path \(path)")
return dbPointer
}
}
func openDatabase()
{
let fileURL = try! FileManager.default
.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent(path)
if sqlite3_open(fileURL.path, &db) == SQLITE_OK {
print("Database Created !!")
}
}
}

Related

Getting "The file couldn’t be opened because there is no such file" error from FileManager even though it's actually opened

I'm retrieving a plist file, updating it, and writing it to disk.
1) Retrieval
func pListURL() -> URL? {
guard let result = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true).appendingPathComponent("somePlist.plist") else { return nil }
return result
}
2) Update
var data: [String: Int] = [:]
if let url = pListURL() {
do {
let dataContent = try Data(contentsOf: url)
if let dict = try PropertyListSerialization.propertyList(from: dataContent, format: nil) as? [String: Int] {
data = dict
}
} catch {
print(error)
}
}
// update code
3) Write
if let path = pListURL() {
do {
let plistData = try PropertyListSerialization.data(fromPropertyList: data, format: .xml, options: 0)
try plistData.write(to: path)
} catch {
print(error)
}
}
The odd thing is I get an error that says:
The file “somePlist.plist” couldn’t be opened because there is no such file.
even though when I check the plist, it's actually properly created and updated as it should be. As far as I know, the create parameter of FileManager.default.url(for:in:appropriateFor:create: ) ensures that it "creates the directory if it does not already exist", which means somePlist.plist is created if plist doesn't exist already.
As far as I know, the create parameter of FileManager.default.url(for:in:appropriateFor:create: ) ensures that it "creates the directory if it does not already exist", which means somePlist.plist is created if plist doesn't exist already.
No, it means the directory is created but the file is not created.
In the update part ignore the couldn’t be opened error and write the (new) data to disk or check if the file exists with fileExists(atPath.
And you can declare the return value in pListURL as non-optional. It's guaranteed that the folder Documents exists
func pListURL() -> URL {
return try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent("somePlist.plist")
}
Update:
These are reliable versions of update and write
func update(dictionary: [String:Int]) {
let url = pListURL()
guard FileManager.default.fileExists(atPath: url.path) else { write(dictionary: dictionary); return }
do {
let dataContent = try Data(contentsOf: url)
if var dict = try PropertyListSerialization.propertyList(from: dataContent, format: nil) as? [String: Int] {
for (key, value) in dictionary {
dict.updateValue(value, forKey: key)
}
write(dictionary: dict)
} else {
write(dictionary: dictionary)
}
} catch {
print(error)
}
}
func write(dictionary: [String:Int]) {
let url = pListURL()
do {
let plistData = try PropertyListSerialization.data(fromPropertyList: dictionary, format: .xml, options: 0)
try plistData.write(to: url)
} catch {
print(error)
}
}

How to get the root cause when createFile fails?

Trying to create a file using createFile API on iOS.
createFile(path, data, attr)
Document says,
Return Value:
true if the operation was successful or if the item already exists, otherwise false.
I wonder if the operation was not successful, how can I get the exact cause of the failure?. Expected some API like,
createFile(path, data, attr, &error)
Use Data.write(to:options:) instead, it throws.
I use FileManager.default.createDirectory to create folders. I create Data that I want to write, and then I use Data.write to save it to disk.
var docDir = URL()
do {
docDir = try FileManager.default.url(
for: .documentDirectory,
in: .userDomainMask,
appropriateFor: nil,
create: true
)
} catch {
fatalError("\(error)")
}
let subDir = docDir.appendPathComponents("mySubDir")
do {
try FileManager.default.createDirectory(
at: subdir,
withIntermediateDirectories: true,
attributes: nil
)
} catch {
if (error as NSError).code == 516 {
// "The file “x” couldn’t be saved in the folder “y” because a file with the same name already exists."
// This is OK.
} else {
fatalError("\(error)")
}
}
let filePath = subdir.appendingPathComponent("yourFileName")
let object:Codable = Whatever()
let data = try! JSONEncoder().encode(object)
do {
try data.write(to: filePath)
} catch {
fatalError("\(error)")
}
I do not know why Apple devs chose FileManager.createFile to be different. It seems strange to me.

iOS sandbox independent file path

I am developing a simple app for iOS.
And I was really surprised that when image is written like in the code below, the path stays valid before the app restarts. After the app restarts the new sandbox is created and this invalidates the previous path. I am trying to find a way to have sandbox independent file path. Is it even possible without going through the resolution cycle (i.e. FileManager.default.url...)?
static func saveToDocument(name: String, image: UIImage) -> String? {
guard let data = image.pngData() else { return nil; }
guard let directory = try? FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false) as NSURL else {
return nil
}
do {
let file = directory.appendingPathComponent("\(name).png");
try data.write(to: file!)
return file?.absoluteString
} catch {
return nil
}
}
Stop searching, there is no way, and there is a good reason for that: The safety of your data.
You always have to get the URL of the documents directory with FileManager.default.url(for, it's not that expensive.
Guarding the URL is not necessary, the documents folder is guaranteed to exist.
Return only the file name rather than the complete URL string and I recommend to make the function throw to hand over all errors.
enum FileError : Error {
case noData
}
static func saveToDocument(name: String, image: UIImage) throws -> String {
guard let data = image.pngData() else { throw FileError.noData }
let directory = try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)
let fileURL = directory.appendingPathComponent("\(name).png")
try data.write(to: fileURL)
return fileURL.lastPathComponent
}
And never use NS... classes like NSURL in Swift if there is a native counterpart (URL).

Move database file from bundle to documents folder - FMDB

I'm using FMdatabase.
I want to use a prepared database.
I think I should move database file from bundle to documents folder.
my code:
import FMDB
class DatabaseManager {
private let dbFileName = "kashanmapDB_upgrade_3-4.db"
private var database:FMDatabase!
let TABLE_LOCATION_FA = "LocationInfoFa";
let TABLE_LOCATION_EN = "LocationInfoEn";
let TABLE_GREAT_PEOPLE_FA = "GreatPeopleInfoFa";
let TABLE_GREAT_PEOPLE_EN = "GreatPeopleInfoEn";
let TABLE_TAGS = "Tags";
let TABLE_RELATION_TAG_LOCATION = "RelationTagLocation";
let TABLE_NECESSARY_INFORMATION = "NecessaryInformation";
let TABLE_SLIDER_FA = "SliderFa";
let TABLE_SLIDER_EN = "SliderEn";
let DATABASE_VERSION = 4;
static var LANGUAGE = 1 ; //1:Fa , 2:En
var utilities = Utilities()
init() {
openDatabase()
if(utilities.getData(key: "lang") == "2")
{
DatabaseManager.LANGUAGE = 2
}
}
func copyDatabaseIfNeeded() {
// Move database file from bundle to documents folder
let fileManager = FileManager.default
let documentsUrl = fileManager.urls(for: .documentDirectory,
in: .userDomainMask)
guard documentsUrl.count != 0 else {
return // Could not find documents URL
}
//let finalDatabaseURL = documentsUrl.first!.appendingPathComponent("kashanmapDB_upgrade_3-4.db")
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let finalDatabaseURL = URL(fileURLWithPath: paths).appendingPathComponent(dbFileName)
if !( (try? finalDatabaseURL.checkResourceIsReachable()) ?? false) {
print("DB does not exist in documents folder")
let documentsURL = Bundle.main.resourceURL?.appendingPathComponent("kashanmapDB_upgrade_3-4.db")
do {
try fileManager.copyItem(atPath: (documentsURL?.path)!, toPath: finalDatabaseURL.path)
} catch let error as NSError {
print("Couldn't copy file to final location! Error:\(error.description)")
}
} else {
print("Database file found at path: \(finalDatabaseURL.path)")
}
}
func openDatabase() {
self.copyDatabaseIfNeeded()
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let dbPath = URL(fileURLWithPath: paths).appendingPathComponent(dbFileName)
let str_path = Bundle.main.resourceURL!.appendingPathComponent(dbFileName).path
let database = FMDatabase(path: str_path)
/* Open database read-only. */
if (!(database.open(withFlags: 2))) {
print("Could not open database at \(dbPath).")
} else {
print("opened database")
self.database = database;
}
}
at the first time (when application installed ) I got this error message:
DB does not exist in documents folder
and I always got this message:
Error Domain=FMDatabase Code=8 "attempt to write a readonly database" UserInfo={NSLocalizedDescription=attempt to write a readonly database}
Hmmm... Looking at your code:
func openDatabase() {
self.copyDatabaseIfNeeded()
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let dbPath = URL(fileURLWithPath: paths).appendingPathComponent(dbFileName)
let str_path = Bundle.main.resourceURL!.appendingPathComponent(dbFileName).path
let database = FMDatabase(path: str_path)
/* Open database read-only. */
if (!(database.open(withFlags: 2))) {
print("Could not open database at \(dbPath).")
} else {
print("opened database")
self.database = database;
}
}
It appears you are setting dbPath equal to the path to the file in documents folder, but then you're trying to open database which is at str_path which is equal to the Bundle path.
Maybe just change:
let database = FMDatabase(path: str_path)
to:
let database = FMDatabase(path: dbPath)
Having copied the database, you are trying to open the database from the bundle. Open the one in the Documents folder. If you define the bundle URL inside the if statement that handles the missing database (like shown below), there's no possibility of accidentally grabbing the wrong database.
As an aside, Apple is getting more particular about what gets stored in Documents folder (see iOS Storage Best Practices). You might want to use Application Support folder instead.
let fileURL = try FileManager.default
.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent("test.sqlite")
let fileExists = (try? fileURL.checkResourceIsReachable()) ?? false
if !fileExists {
let bundleURL = Bundle.main.url(forResource: "test", withExtension: "sqlite")!
try FileManager.default.copyItem(at: bundleURL, to: fileURL)
}
let db = FMDatabase(url: fileURL)
guard db.open() else {
print("unable to open")
return
}
Alternatively, it’s often preferred to adopt the “ask for forgiveness rather than permission” strategy. I.e., rather than checking for existence before you open the database every time you open it, just try to open it and handle the file-not-found error scenario (which will happen just once, the first time you try opening it). Bottom line, just try opening the database, and if it fails, copy it from the bundle and try again.
The trick is to supply the SQLITE_OPEN_READWRITE parameter (made available if you import SQLite3) but not the SQLITE_OPEN_CREATE so that it won’t create a blank database if it’s not found that first time you try opening it:
let fileURL = try FileManager.default
.url(for: .applicationSupportDirectory, in: .userDomainMask, appropriateFor: nil, create: true)
.appendingPathComponent("test.sqlite")
let db = FMDatabase(url: fileURL)
if !db.open(withFlags: SQLITE_OPEN_READWRITE) {
let bundleURL = Bundle.main.url(forResource: "test", withExtension: "sqlite")!
try FileManager.default.copyItem(at: bundleURL, to: fileURL)
guard db.open(withFlags: SQLITE_OPEN_READWRITE) else {
print("unable to open")
return
}
}

error Domain=FMDatabase Code=8 "attempt to write a readonly database"

I'm using FMDatabase library to used prepped database sqlite.
I got this error:
Error Domain=FMDatabase Code=8 "attempt to write a readonly database" UserInfo={NSLocalizedDescription=attempt to write a readonly database}
2017-10-27 19:59:10.983238+0330 kashanmap[417:63718] Unknown error calling sqlite3_step (8: attempt to write a readonly database) eu
2017-10-27 19:59:10.983473+0330 kashanmap[417:63718] DB Query: insert into LocationInfoFa
it's my class:
import FMDB
class DatabaseManager {
private let dbFileName = "kashanmapDB_upgrade_3-4.db"
private var database:FMDatabase!
let TABLE_LOCATION_FA = "LocationInfoFa";
let TABLE_LOCATION_EN = "LocationInfoEn";
let TABLE_GREAT_PEOPLE_FA = "GreatPeopleInfoFa";
let TABLE_GREAT_PEOPLE_EN = "GreatPeopleInfoEn";
let TABLE_TAGS = "Tags";
let TABLE_RELATION_TAG_LOCATION = "RelationTagLocation";
let TABLE_NECESSARY_INFORMATION = "NecessaryInformation";
let TABLE_SLIDER_FA = "SliderFa";
let TABLE_SLIDER_EN = "SliderEn";
let DATABASE_VERSION = 4;
static var LANGUAGE = 1 ; //1:Fa , 2:En
var utilities = Utilities()
init() {
openDatabase()
if(utilities.getData(key: "lang") == "2")
{
DatabaseManager.LANGUAGE = 2
}
}
func openDatabase() {
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let dbPath = URL(fileURLWithPath: paths).appendingPathComponent(dbFileName)
let str_path = Bundle.main.resourceURL!.appendingPathComponent(dbFileName).path
let database = FMDatabase(path: str_path)
/* Open database read-only. */
if (!(database.open(withFlags: 2))) {
print("Could not open database at \(dbPath).")
} else {
print("opened database")
self.database = database;
}
}
func closeDatabase() {
if (database != nil) {
database.close()
}
}
path of my database:
my query:
do {
let db = database
let q = try db?.executeUpdate("insert into \(table) (catid,subcat_id,id,subcat_title,title,description,lat,lon,takhfif,images,wifi,apple_health,wc,full_time,pos,work_hours,phone,mobile,fax,website,email,address,facebook,instagram,linkedin,telegram,googleplus,twitter,publish,feature,manager,city,rating_sum,rate_count,lastip,parking,isMallID,mallID,discount_images,price_images,newProduct_images,services_images,order_online,out_upon,cat_title,cat_icon,last_modify,item_logo,cat_logo,rate_sum1,rate_sum2,rate_sum3,rate_count1,rate_count2,rate_count3,rate_title1,rate_title2,rate_title3,rate_enable,installments_text,installments_image) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)", values:[ catid,subcat_id,id,subcat_title,title,description,lat,lon,takhfif,images,wifi,apple_health,wc,full_time,pos,work_hours,phone,mobile,fax,website,email,address,facebook,instagram,linkedin,telegram,googleplus,twitter,publish,feature,manager,city,rating_sum,rate_count,lastip,parking,isMallID,mallID,discount_images,price_images,newProduct_images,services_images,order_online,out_upon,cat_title,cat_icon,last_modify,item_logo,cat_logo,rate_sum1,rate_sum2,rate_sum3,rate_count1,rate_count2,rate_count3,rate_title1,rate_title2,rate_title3,rate_enable,installments_text,installments_image])
} catch {
print("\(error)")
}
there are some solutions in stack overflow but them don't accepted as true answer.
updated2
I got this error:
DB does not exist in documents folder
my code:
init() {
openDatabase()
if(utilities.getData(key: "lang") == "2")
{
DatabaseManager.LANGUAGE = 2
}
}
func copyDatabaseIfNeeded() {
// Move database file from bundle to documents folder
let fileManager = FileManager.default
let documentsUrl = fileManager.urls(for: .documentDirectory,
in: .userDomainMask)
guard documentsUrl.count != 0 else {
return // Could not find documents URL
}
let finalDatabaseURL = documentsUrl.first!.appendingPathComponent("kashanmapDB_upgrade_3-4.db")
if !( (try? finalDatabaseURL.checkResourceIsReachable()) ?? false) {
print("DB does not exist in documents folder")
let documentsURL = Bundle.main.resourceURL?.appendingPathComponent("kashanmapDB_upgrade_3-4.db")
do {
try fileManager.copyItem(atPath: (documentsURL?.path)!, toPath: finalDatabaseURL.path)
} catch let error as NSError {
print("Couldn't copy file to final location! Error:\(error.description)")
}
} else {
print("Database file found at path: \(finalDatabaseURL.path)")
}
}
func openDatabase() {
self.copyDatabaseIfNeeded()
let paths = NSSearchPathForDirectoriesInDomains(.documentDirectory, .userDomainMask, true)[0] as String
let dbPath = URL(fileURLWithPath: paths).appendingPathComponent(dbFileName)
let str_path = Bundle.main.resourceURL!.appendingPathComponent(dbFileName).path
let database = FMDatabase(path: str_path)
/* Open database read-only. */
if (!(database.open(withFlags: 2))) {
print("Could not open database at \(dbPath).")
} else {
print("opened database")
self.database = database;
}
}
You are facing this error because you are trying to write (update) the .db file in the bundle directory which is not allowed:
AppName.app:
This is 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.
File System Basics - Table 1-1: Commonly used directories of an iOS app
If you are aiming to update the file, you should implement a logic to copy it -if it's not already has been copied before- into the documents directory, thus you'd be able to read/write transactions with the copied file.
Remark that for iOS 11 and above, you might want to copy the database file into the Application Support directory if you don't want to let be viewable to the end users when navigating to your app by the Files iOS app. For details, check the iOS Storage Best Practices Apple video session.
You'd notice that this logic should be applied to any file in the app main bundle, for instance it is also applicable for JSON files.

Resources