@ionic/storage data gets deleted after Capacitor migration and upgrades

The Problem
When users update their app with my latest provided version, all of their storage data (now managed by @ionic/storage-angular) is gone. A new empty indexdb is created. This is a one-time thing, as the new data set now persists.

Context
I recently migrated to Capacitor. Here are some changes related to my issue:

  • Switched from “@ionic/storage”: “~2.2.0” (cordova-sqlite-storage as a dependency but I believe unused) to “@ionic/storage-angular”: “^4.0.0”
  • Upgrading from “cordova-android”: “^9.1.0” to whatever is supported in “@ionic/angular”: “^7.0.0”
  • Dropped config.xml in favor of Capacitor using ionic.config.ts
  • No longer using “cordova-plugin-ionic-webview” since Capacitor provides its own web view

What I’ve tried already

  • Putting this in capacitor.config.ts
    cordova: {
    preferences: {
      AndroidInsecureFileModeEnabled: "true"
    }
  },
  • As well as just trying this:
server: {
    androidScheme: "http",
    hostname: "localhost",
  },
  • A lot of research into potential causes:
    • Schema changing from file:/// to http://localhost (or https) and applying AndroidInsecureFileModeEnabled which doesn’t seem to work for me. (is it being overwritten?) This seems related to the Cordova Android 10.0 release.
    • Look into how Web View is implemented between Cordova and Capacitor, in particular with how their sandboxing works. I found more questions than answers.
    • Look into the possibility of accessing the old indexdb upon updating and migrate the data over. No luck, I’m not sure what I’m looking for.
    • Can I update the old version to start a migration path. Answer: no, due to Google requirements which mean I can’t submit a new version without upgrading all the dependencies to hit the latest version of Android. I’m stuck unless I do the upgrades.
    • Is there a problem going from @ionic/storage to @ionic/storage-angular? Conclusively, no. The latter is a continuation of what @ionic/storage used to be, since they’ve added support for React and Vue to the former. None of the release notes suggest there’d be issues in upgrading.

Note: I’m unsure what the schema and hostname were in the old version of the app. I’ve been resetting my environment back to older version of java to try and compile a debug version because I can’t debug a release/prod version of an APK. Might be a dead end.

I know I’m not the first to run into this issue. Unfortunately, I haven’t found any forum discussion (here or external) that figured out a solution. What are my options? Am I overlooking something quite simple?

I got an old debug version running. I threw it on a device and hit chrome://inspect. Here’s what I saw:

Old Version
Base URL: http://localhost (probably from the webview plugin, otherwise it would’ve been the very long file:///android_asset/www/index.html url).
Ionic Storage: Nowhere to be found. The app was indeed saving data, but nothing was showing up in devtools at all. Is it storing the data in some local db that functions externally from the webview? It may have something to do with the cordova-sqlite-storage plugin I used to have.

New Version
Base URL: https://localhost (I’ll have to put in the Capacitor config to use http, though that’s not the root problem)
Ionic Storage: indexedDB_ionickv
Note: This is the same location I get if I do ionic serve on the old one, but that changes once it’s on an actual device. The new version uses _ionickv both places.

If anybody has any ideas, I’m all ears.

Update: On the old device I ran console.log("driver name:", this.storage.driver) where storage is the constructor where I’m declaring use of @ionic/storage.

It showed driver name: cordovaSQLiteDriver. No wonder it wouldn’t show up in devtools. This is my first lead in a while. Thanks, me! I never told Storage to use cordovaSQLiteDriver as the driver, yet it opted for it anyway. Sounds like I’ll need to replicate this on V2, getting it to use the cordovaSQLiteDriver driver and then possibly having it migrate the data over to IndexedDB or something a bit more sustainable in the future. I’ll report back.

1 Like

Update: No dice but I’m making progress. I’ve determined that my old version had the database name of “_ionicstorage”. In Android Studio I can debug the database file which is in data/data/<package_name>/databases → _ionicstorage (no file extension, but can be read as a sqlite file).

If I open the file, the table is _ionickv.

I’ve tried using cordova-sqlite-porter to copy the data over as JSON. No luck so far, I think I’m getting the parameters wrong. For instance, my function:

public migrateDb(): void {
    // To get around window not having property of openDatabase
    const win: any = window;

    var db = win.openDatabase("_ionicstorage", "1.0", "databases", 5 * 1024 * 1024);
    var successFn = function (json, count) {
      console.log("Exported JSON: " + json);
      alert(
        "Exported JSON contains equivalent of " + count + " SQL statements"
      );
    };
    SQLitePorter.exportDbToJson(db, {
      successFn: successFn,
    });
  }

Here’s the documentation for openDatabase(). However, it’s considered outdated so I don’t know if there is still support for it, which could be my issue.

Really all I need to do is access the _ionicstorage file in data/data/<package_name>/databases from the client side, but none of my solutions have worked out so far.

Anybody have clues they could give me?

I got it.

I was correct in my previous post that window.openDatabase() was incorrect - I was led astray by the sqlite-porter documentation. The plugin SQLite should be used to “create” (which will just reconnect if it already exists) the old database, which returns the database object as a promise which SQLitePorter can then use to export to JSON. SQLitePorter itself cannot access any databases, you have to provide one to it.

My code explains it best, I think:

// Add both of these as packages AND plugins, so that cap sync catches them
import { SQLitePorter } from "@awesome-cordova-plugins/sqlite-porter";
import { SQLite } from "@awesome-cordova-plugins/sqlite/ngx";

...
// In the constructor 
    private sqlite: SQLite
// SQLitePorter can't be used in the constructor, access it directly from the import

...
// call this function after platform is ready and platform type is "hybrid"
  public migrateDb(): void {
    this.sqlite
      .create({
        name: "_ionicstorage",
        location: "default",
      })
      .then((db: any) => {
        SQLitePorter.exportDbToJson(db)
          .then((jsonData: any) => {
            // The missing data
            let dataStr = JSON.stringify(jsonData);

            // alert is for testing - put logic here instead of alert to handle the data properly.
            alert(dataStr);
          })
          .catch((error) => {
            console.error(error);
          });
      })
      .catch((error) => {
        console.error(error);
      });
  }