Project works perfectly on android but silently fails on iOS - NativeStorage seems to be the issue


#1

I’m having several issues while trying to run an application on iOS.

Important informations:

  • The whole project was firstly created on a windows 10 machine where Android was tested: every single feature was tested on two different physical devices (a Lenovo tablet and a Nexus 5X phone).
  • The project was then migrated to a Macbook air running MacOSx Sierra. Latest version of ionic was installed asweel, along with every single cordova configuration needed (and Xcode updates).
  • The project was copied from the windows 10 machine (excluding node modules) on an external drive.
  • A brand new blank project was created on the mac machine. After the project was correctly created, all the files but node_modules were used to overwrite all the existing files.
  • These command were ran (in this order precisely):
npm install

— Plugins Installation (manual)
— Relevant plugin: https://ionicframework.com/docs/native/native-storage/

ionic cordova plugin add cordova-plugin-nativestorage
npm install --save @ionic-native/native-storage
ionic cordova platform add ios

The application structure is a regular ionic 2 one with tabs.

In the NgModule, I’m properly importing the NativeStorage and injecting it into the providers:

providers: [{provide: ErrorHandler, useClass: IonicErrorHandler}, NativeStorage]

Moreover, I’m also defining this in the imports section:

imports: [
    IonicModule.forRoot(MyApp),
    IonicStorageModule.forRoot({
      name: '__agentidb',
         driverOrder: ['sqlite', 'websql', 'indexxeddb']
    }),
    BrowserModule,
    FormsModule,
    HttpModule
  ],

The module usage I’m doing is the following:

I have a generic angular 2 service called “Database”, where I’m using the NativeStorage module in the constructor. Relevant code:

import { Injectable } from '@angular/core';
import { NativeStorage } from '@ionic-native/native-storage';

export class DatabaseError {
    public reason: string = "";
    constructor(reason) {
        this.reason = reason;
    }
}

export enum ERROR_CODES {
    NATIVE_WRITE_FAILED = 1,
    ITEM_NOT_FOUND = 2,
    NULL_REFERENCE = 3,
    UNDEFINED_TYPE = 4,
    JSON_ERROR = 5,
    WRONG_PARAMETER = 6
}

@Injectable()
export class DatabaseService {

    constructor(private nativeStorage: NativeStorage) {
        
    }

Relevant injection structure:

app.component.ts -> app.component.html (app)
|----- mainscreen.component.ts -> mainscreen.component.html(<mainscreen>)
         | ---- login.component.ts (actively uses the user service which is using the database service)

the app.component.ts relevant part is:

@Component({
  templateUrl: 'app.html',
  viewProviders: [API, UserService, DatabaseService, ArticlesService, ClientsService, SplashScreen, StatusBar]
})
export class MyApp {

The login component (which is meant to inherit the UserService and DatabaseServer), uses the UserService to check whether there already is some data in any database.

Surprisingly, no service constructor seems to be called whatsoever, I don’t have any console.log being called, and whenever any call from the login component gets to the database server it just silently fails without even throwing any single issue.

I’ve checked the console, and the first fail happens when I try to set a value on an object which is meant to be compiled in the UserService constructor:

constructor(private database: DatabaseService) {
    this.ServiceReady = new ReplaySubject<boolean>();
    console.log("attempting to call table exists of <User>");
    this.database.table_exists("User").then((exists) => {
      if (exists) {
        console.log("table exists already.");
        // check if property exists in table.
        this.database.key_exists("User","LoggedUser").then((res) => {
          if (res) {
            this.initUser();
          }
          else {
            console.log("key does not exist.");
          }
        });
      }
      else {
        // Table does not exists..
        console.log("table does not exist yet. creating one.");
        this.database.create_table("User",{
          LoggedUser: {}
        }).then( (res) => {
          console.log("create table said: " + res);
          this.initUser();
        });
      }
    });
  }

Basically, no console log is shown from this console.log, not even the very first one, and no exception/log is being thrown whatsoever.

Surprisingly, running this on windows creates no issues and the project works perfectly on any android physical device, while in iOS it looks like that “this.database.table_exists” is failing, despite it is not throwing any single error.

table_exists function for reference:

public table_exists(tablename: string): Promise<boolean> {
    return new Promise( (resolve, reject) => {
      this.nativeStorage.getItem(tablename).then((res) => {
        if (res != null) {
          resolve(true);
        }
        else {
          resolve(false);
        }
      }).catch((err) => {
        if (err.code === ERROR_CODES.ITEM_NOT_FOUND) {
            resolve (false);
        }
        else {
            throw "Error while reading data.";
        }
      });
    });
  }

Relevant package.json:

"dependencies": {
        "@angular/common": "4.1.0",
        "@angular/compiler": "4.1.0",
        "@angular/compiler-cli": "4.1.0",
        "@angular/core": "4.1.0",
        "@angular/forms": "4.1.0",
        "@angular/http": "4.1.0",
        "@angular/platform-browser": "4.1.0",
        "@angular/platform-browser-dynamic": "4.1.0",
        "@ionic-native/core": "3.6.1",
        "@ionic-native/native-storage": "3.8.1",
        "@ionic-native/network": "3.8.1",
        "@ionic-native/splash-screen": "3.6.1",
        "@ionic-native/status-bar": "3.6.1",
        "cordova-android": "^6.2.3",
        "cordova-ios": "^4.4.0",
        "cordova-plugin-console": "^1.0.5",
        "cordova-plugin-device": "^1.1.4",
        "cordova-plugin-nativestorage": "^2.2.2",
        "cordova-plugin-splashscreen": "^4.0.3",
        "cordova-plugin-statusbar": "^2.2.2",
        "cordova-plugin-whitelist": "^1.3.1",
        "cordova-sqlite-storage": "^2.0.4",
        "ionic-angular": "3.2.1",
        "ionic-plugin-keyboard": "^2.2.1",
        "ionicons": "3.0.0",
        "rxjs": "5.1.1",
        "sw-toolbox": "3.6.0",
        "zone.js": "0.8.10"
    },
    "devDependencies": {
        "@ionic/app-scripts": "1.3.7",
        "typescript": "2.2.1",
        "@ionic/cli-plugin-ionic-angular": "1.0.0",
        "@ionic/cli-plugin-cordova": "1.0.0"
    },
    "description": "An Ionic project",
    "cordova": {
        "plugins": {
            "cordova-sqlite-storage": {},
            "cordova-plugin-console": {},
            "cordova-plugin-device": {},
            "cordova-plugin-splashscreen": {},
            "cordova-plugin-statusbar": {},
            "cordova-plugin-whitelist": {},
            "ionic-plugin-keyboard": {},
            "cordova-plugin-nativestorage": {}
        },
        "platforms": [
            "android",
            "ios"
        ]
    }

Any idea?


#2

There are several things I don’t understand here.

  • Why do you even need NativeStorage at all here? Why can’t you just use the ordinary Storage that you already have?
  • Why are you declaring providers on the app component instead of the app module?
  • Why are you instantiating Promises manually?
  • Why are you copying projects instead of using git?

#3

There are several reasons for every single point, I will try to summarize them up:

  • I use NativeStorage since I’ve read many articles that recommended to actually use native over hybrid where possible. In this case, the migration from Storage to NativeStorage is significantly easier than what it may look like, since I use a wrapper logic which is indipendent from the type of driver or the type of module used.
  • Providers are not declared in the app module because I have two different core of the current applications, and each of them uses different services, so the app component is injecting the common providers only, while other components are injecting the other necessary providers, this reduces loading times by a whole lot.
  • I’m instantiating Promises manually because I’m used to program in this way, it’s far easier to mantain the code if there is any change in an external library, because (again) the wrapper is still the same, so the changes that needs to be applied to mantain the code are minimal.
  • I was unable to use git in this case, hence I was forced to copy and paste the project. This should have no impact though, because a brand new project was created, and relevant files were copied to it, and each step that was done in windows was replicated in MacOSx.

So, is there any relevant reason why constructor seems not to be even called? Is there any other solution other than migrating back to the regular Storage module?

It’s not an heavy change, I’m just not getting why it doesn’t even tell me why it’s not working. No console error is shown, and constructors are not being called whatsoever in iOS, while in every single android device everything is being called and logged aswell.


#4

hi…
the same thing i guess i did.
i develop my project in windows and build and test for android. all functios and plugins working as i want. i coppy hole project to mac os and build ios platform and from xcode i run on simulator functionality and cod is working properly but plugin are not