Database error when testing on device

Hello everyone. I have the following problem. I have an application that queries a database in sqlite. When I run the app in xcode on my mac I give it the path to the database to make it work. So far so good. But when I want to test this application on a physical device (iPhone 11), I get the error that the application cannot find the database. My question or problem is where I have to store the database on the iPhone so that the application can make the queries. Thank you.

Answered by DTS Engineer in 746231022

What Scott said but also, I think this is your main problem:

I generate the database connection code, which is this

let fileURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)                .appendingPathComponent("TurMer.sqlite")

You have set things up so that your database is bundled in your app. That means that you won’t be able to find it in the Documents directory. Rather, you need to use Bundle. Try this:

let fileURL = Bundle.main.url(forResource: "TurMer", withExtension: "sqlite")!

Note the force unwrap (!) at the end. This is standard practice for this API when accessing items within your own bundle. This will trap if the item isn’t present. This is exactly what you want in this case because, if the item isn’t present, it means that your app was built incorrectly.

And remember that your bundle is read-only so, as Scott suggested, you want to open it with SQLITE_OPEN_READONLY.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Do you want to read and write this database? Or it is a read-only database bundled with your app?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

1.- My database is only read.

2.-

When I run the application in xcode, it shows me a route, there I copy

the database, I run the app in xcode and it works perfectly for me.

3.-

When I run the app on the iphone.

the problem is that I put that path /var/mobile/Containers... in the

"finder" and that path doesn't exist, so when the application is run

on the iPhone it doesn't physically find the database, that is where

my problem lies or is. The application cannot find the database.And

I'm going crazy for 1 month that I can't test the application on the

iphone.

Are you NovatoSwift on this Swift Forums thread?

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Yes, I am.

OK, cool. You’ve definitely found the right place for your question. Swift Forums tends to focus on Swift the language. In contrast, this stuff is very specific to Apple platforms.

All iOS apps are sandboxed. By default the app can only access files inside its sandbox [1]. This means:

  • Read-only files within the app’s bundle.

  • Files within the app’s containers.

Every app starts with a single default container. You can add containers using the app groups entitlement but that’s an advanced topic that’s not relevant to you right now.

The simulator does not enforce this sandboxing. Your app running in the simulator can access any file in the host Mac’s file system [2].

Your app finds its container using APIs like url(for:in:appropriateFor:create:). The exact path you get back is not specified — you’ll see differences between platform and even from release-to-release of a given platform — so you always have to call the API. However, on current versions of iOS you should expect to get back something in /var/mobile/Containers/.

You wrote:

the problem is that I put that path /var/mobile/Containers/ in the "finder" and that path doesn't exist

Right. The Mac and the iOS device don’t share a file system. You can think of that USB cable connecting them as a network connection.

This works both ways:

  • The macOS Finder can’t navigate to directories on the iOS device.

  • The iOS app can’t directly access files on the Mac file system.

So, if you have a database file on the Mac, your iOS app won’t be able to access it.


How you proceed here depend on your goals. For initial bring up the easiest option is to embed your database into your app’s bundle. It will then be installed on the device along with the rest of the app. You can access it using Bundle APIs like url(forResource:withExtension:).

Keep in mind that your bundle is read-only, so you’ll only be able to read your database this way. If you need read/write access then you need to programmatically set up your database in your app’s container. You can either do this explicitly or copy a ‘template’ database that lives your in app bundle.

If you want to see what your app’s container looks like, you can grab a copy using Xcode. See Xcode Help > Manage devices > Manage app containers.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

[1] There is something called a sandbox extension that lets apps access files outside of their static sandbox. This supports, for example, opening documents on iCloud Drive. However, that’s an advanced topic that we can skip for the moment.

[2] Subject to the Mac’s native file system permissions.

Good Morning in Spain. First of all, thank you very much for the help.

The previous answer has helped me a lot and I have understood many things, but it still does not work, there is something that I do not do well or do not understand. What I do not understand is how I can have a project stopped for more than 1 month because I am not able to test an application on an iPhone physical device. An application that works correctly for me in xcode and everything is because of the database.

The steps I do are add the database to the project as indicated in the screenshots (photo1, photo2).Here I check that the database is really inside the project

Once this is done, I generate the database connection code, which is this

    let fileURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)                .appendingPathComponent("TurMer.sqlite")

Once this line is executed, it tells me that everything is correct. Moving on to execute the second line which is

if sqlite3_open(fileURL.path, &BaseDatos) != SQLITE_OK

In this line we have opened the database. Everything is correct, it tells me that the database exists (for that I have created a breakpoint)

Ok, at this point, we have found the database and opened it. Up to here everything correct.

Now we are going to execute a query in said database. I do it with the following code

cadenasql="SELECT * FROM Clientes"            

      var stmt:OpaquePointer?              if sqlite3_prepare_v2(BaseDatos, cadenasql, -1, &stmt, nil) == SQLITE_OK       {

As you can see, it is a simple query to a database table. Well this is where I ALWAYS get the problem. Cannot find any tables in the database. The database has 8 tables, and it doesn't find any of them. And it is something that I do not understand, if we have found the database and we have opened it (and it has not given me any error), why does it not find ANY TABLE inside the database?

Always the same message ---> No much: ( name of table).

And I don't understand, I don't understand, I don't understand...why????????? It's maddening. I have searched for countless forums, documents, etc on the internet and I follow the steps of all of them and always always the same error. Before it couldn't find the database, it already does, but it can't find the tables. I'm going crazy. I don't understand how it can cause so much trouble. I have programmed in other languages ​​and none have given me so many problems ( c, cobol, assembler, pascal, python, R, java)... I'm about to forget all my project, please, any help... some point of light in the face of so much despair. thank you !!

let fileURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false).appendingPathComponent("TurMer.sqlite”)

Once this line is executed, it tells me that everything is correct.

Not quite correct. That line returns a URL for where that file would be located (within your Documents directory) whether it exists or not. It doesn’t check if that a file actually exists at that location. In this case, no such file exists. When your app first starts, the Documents directory is empty.

See @eskimo’s reply above for finding your database file within your app bundle, which is totally separate from the Documents directory.

if sqlite3_open(fileURL.path, &BaseDatos) != SQLITE_OK

In this line we have opened the database. Everything is correct, it tells me that the database exists

Not quite. Note the documentation for sqlite3_open says that the database will be created if it doesn’t already exist. So this actually creates and opens an empty database. That’s why the subsequent query fails.

In general, don’t use sqlite3_open in new code. You should switch to sqlite3_open_v2 which gives you better control over its behavior. Use the flag SQLITE_OPEN_READONLY (since you said earlier that your database is read-only) or use SQLITE_OPEN_READWRITE if desired. This will cause sqlite3_open_v2 to fail if the file doesn’t already exist, which would have alerted you to the problem earlier.

Accepted Answer

What Scott said but also, I think this is your main problem:

I generate the database connection code, which is this

let fileURL = try! FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: false)                .appendingPathComponent("TurMer.sqlite")

You have set things up so that your database is bundled in your app. That means that you won’t be able to find it in the Documents directory. Rather, you need to use Bundle. Try this:

let fileURL = Bundle.main.url(forResource: "TurMer", withExtension: "sqlite")!

Note the force unwrap (!) at the end. This is standard practice for this API when accessing items within your own bundle. This will trap if the item isn’t present. This is exactly what you want in this case because, if the item isn’t present, it means that your app was built incorrectly.

And remember that your bundle is read-only so, as Scott suggested, you want to open it with SQLITE_OPEN_READONLY.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Accepted Answer

And remember that your bundle is read-only so, as Scott suggested, you want to open it with SQLITE_OPEN_READONLY.

One more thing I just remembered (from doing this years ago)... you may also need to do this when you call sqlite3_open_v2:

  • add the SQLITE_OPEN_URI flag
  • pass the path as a file: URI rather than a raw path
  • append query parameter ?immutable=1 to that URI

This makes SQLite work correctly when the file is on a read-only disk, which is basically what your app bundle is. See these pages for more detail:

  • https://www.sqlite.org/c3ref/open.html
  • https://www.sqlite.org/uri.html#coreqp
Database error when testing on device
 
 
Q