Skip to content

SQLitePCL.Batteries.Init

Eric Sink edited this page Jan 4, 2017 · 6 revisions

What is SQLitePCL.Batteries_V2.Init() ?

An initialization function provided by SQLitePCL.raw's bundle packages.

What is SQLitePCL.Batteries.Init() ?

The original version of the method, before the "V2" version came around.

Both versions of this method do exactly the same thing.

The original version of this method did not support bait-and-switch across bundle types. I realized this was a design mistake too late, as correcting it would be a breaking change. So I left the original unchanged and added a V2 version of the method.

The V2 version is the preferred choice. For that reason, the rest of this writeup speaks in terms of the V2 version.

What is a bundle package?

A bundle package is a meta-package that does two things:

  • Use nuget dependencies to bring in other packages as needed for the platform. These include a SQLitePCLRaw.provider package, and if needed, one or more SQLitePCLRaw.lib packages.
  • Provide the SQLitePCL.Batteries_V2.Init() function to get things properly configured for that platform.

Bundle packages have ids that start with SQLitePCLRaw.bundle_. For example:

  • SQLitePCLRaw.bundle_e_sqlite3
  • SQLitePCLRaw.bundle_sqlcipher
  • SQLitePCLRaw.bundle_green

What is the difference between the bundle packages?

Each bundle package has a different policy about which instance of the native SQLite library is being used. The name reflects that policy.

  • bundle_e_sqlite3 always brings in e_sqlite3, a native SQLite library which is built specifically for SQLitePCL.raw users.
  • bundle_sqlcipher brings in the appropriate platform build of sqlcipher, a variant of SQLite with support for encryption. The sqlcipher builds are maintained by Couchbase.
  • bundle_green is just like bundle_e_sqlite3, except that on iOS, it uses the SQLite library provided by iOS.

What are the SQLitePCLRaw.provider packages?

Each one contains an implementation of ISQLite3Provider.

What is ISQLite3Provider?

It is the even-lower-level interface that SQLitePCL.raw uses to talk to the native SQLite library.

The ISQLite3Provider abstraction hides the details of how the native code is called. The pinvoke/DllImport code exists inside the implementation of ISQLite3Provider, not in the main assembly. And since Silverlight platforms do not support DllImport, Windows Phone 8.0 has an implementation of ISQLite3Provider which is completely different, using C++/CLI instead.

The main SQLitePCL.raw assembly does not contain any implementations of ISQLite3Provider. An implementation must be injected by calling SQLitePCL.raw.SetProvider().

What does Batteries_V2.Init() do?

It calls SQLitePCLRaw.SetProvider() to inject an instance of ISQLite3Provider, which tells SQLitePCL.raw how to talk to a specific instance of the native SQLite3 library.

Where should I call SQLitePCL.Batteries_V2.Init()?

From your app's platform-specific initialization code. For example:

  • On iOS, you might call it from the FinishedLaunching() method of your AppDelegate class.
  • On Android, you might call it from somewhere in your MainApplication or MainActivity class.
  • In an MvvmCross app, you might call it from InitializeFirstChance().
  • etc

I am referencing SQLitePCL.raw from a PCL. Can I just call Batteries_V2.Init() from my portable code?

Yes, you can call Batteries.Init() from portable code, using the bait-and-switch technique. In other words, you have to add the bundle package to both your portable project and your app project.

Note that the original Batteries.Init() cannot bait-and-switch from one bundle assembly to a different one. For example: If your PCL references bundle_green for its call to Batteries.Init(), then you need to add bundle_green to your app project -- adding bundle_sqlcipher instead will not work.

But the newer Batteries_V2.Init() supports bait-and-switch across different types of bundles.

My library depends on SQLitePCL.raw. How do I spare users of my library from the need to call SQLitePCL.Batteries_V2.Init()?

  • Take a dep on SQLitePCLRaw.bundle_e_sqlite3 (or similar)
  • Call SQLitePCLRaw.Batteries_V2.Init(), preferably from a static constructor

If your library is a PCL, this will be a bait-and-switch situation, so SQLitePCLRaw.bundle_whatever will need to end up getting added to the app project, one way or another.

What happens if I my library doesn't call SQLitePCL.Batteries_V2.Init() at all?

Then your library users will need to do so.

How will they know what to do?

If no implementation of ISQLite3Provider is injected, all operations will throw an exception which attempts to explain what needs to be done. At the time of this writing, that message says:

You need to call SQLitePCL.raw.SetProvider(). If you are using a bundle package, this is done by calling SQLitePCL.Batteries.Init().

I may change this error message to improve it. I am also considering allowing it to be customized, so that libraries can provide more specific instructions.

Why might it be beneficial to just let my library's user call SQLitePCL.Batteries_V2.Init()?

It allows them to choose which bundle they want to use. For example, they could choose SQLitePCLRaw.bundle_sqlcipher if they want.

So if my library package takes a dep on SQLitePCLRaw.bundle_green, can my users choose, say, bundle_sqlcipher instead?

With the original Batteries.Init(), no.

With Batteries_V2.Init(), yes.

Why might it be better to do the dependencies and the Batteries_V2.Init() call myself?

Maybe your library wants to use SQLite as merely an implementation detail that is not visible to users.

This is kind of a pain.

Yep.

Couldn't SQLitePCL.raw just always use the SQLite library provided by the OS?

Not anymore. Android N disallows this.

In other words, if your library supports Android and wants to use SQLite, even as a hidden implementation detail, your library's users will somehow have to bundle a SQLite library into their app.

Maybe SQLitePCL.raw should just always use a bundled SQLite lib?

Taking away flexibility in this area would definitely make things simpler, and would probably eliminate the need for an initialization call, but requests for that flexibility have been very common. Lots of people ask me how they can substitute sqlcipher, or a custom SQLite build, and so on.

Maybe you could just bundle several ISQLite3Provider implementations into the main assembly and call them all in a certain order and use the first one that works?

I tried that. Some AOT-based implementations choke if the app contains DllImport calls that are unresolved. Windows Universal 8.0 apps were failing store validation because of SQLitePCL.raw. As AOT gets more common in the .NET world, I expect more such problems.

Clone this wiki locally