Problems with the LocalStorage of the telephone


#1

It turns out that when the user registers or loguea I store the token in the storage to validate which screen I should direct it to, what happens is that the computer works for me but not on the device.
When I do the tests in Xcode I load the page first and then it tells me that if there is a token but as first charge the page does not validate the token, I am subscribed to a promise and even then it does not work for me.
Thanks in advance

My Services… Save

guadarStorage(token, id_usuario, nombreUsuario) {
  // Verificamos desde donde se esta haciendo uso de la aplicación en un dispositico o en una computadora
    console.log("Datos a guardar:", this.token, this.id_usuario, this.nombreUsuario);
    console.log(this.token);
  if( this.platform.is( "cordova" ) ) {
    // Estamos en un dispositivo 
    this.storage.set( 'token', token );
    this.storage.set( 'id_usuario', id_usuario );
    this.storage.set( 'nombre', nombreUsuario );
    console.log("Datos guardados");
  }
  else {
    // Estamos en una computadora
    if(this.token) {//Guardamos
      localStorage.setItem( 'token', token  );
      localStorage.setItem( 'id_usuario', id_usuario  );
      localStorage.setItem( 'nombre', nombreUsuario  );
    }
    else {// Borramos
    localStorage.removeItem('token');
    localStorage.removeItem('id_usuario');
    localStorage.removeItem('nombre');
    }
  }
}

My Services… load

cargarStorage() {
  let promesa = new Promise ( ( resolve, reject )=>{
    // Cargar desde el Storage
    if(this.platform.is( "cordova" )) {
      // Estamos en el dispositivo
      this.storage.ready()
                  .then ( ()=>{
                    console.log("que putas esta pasando");
                      // Aquí ya podemos leer del Storage
                      this.storage.get( 'token' )
                                  .then( token => {// Resivo lo que vienes en los items
                                      if ( token ) {// Verifico que venga algo diferente de nulo o undefine
                                        this.token = token;
                                        console.log("Si habia token", this.token);
                                      }
                      })
                      // Aquí ya podemos leer del Storage
                      this.storage.get( 'id_usuario' )
                                  .then( id_usuario => {// Resivo lo que vienes en los items
                                      if ( id_usuario ) {// Verifico que venga algo diferente de nulo o undefine
                                        this.id_usuario = id_usuario;
                                      }
                      })
                      this.storage.get( "nombre" )
                                  .then( nombre => {// Resivo lo que vienes en los deseos
                                      if ( nombre ) {// Verifico que venga algo diferente de nulo o undefine
                                        this.nombreUsuario = nombre;
                                      }
                      })
                  })
    }
    else {
        // Estamos en la computadora
        if( localStorage.getItem( 'token' ) ) {
          // Si entra existen items en el localStorage
          this.token = localStorage.getItem( 'token' );
          this.id_usuario = +localStorage.getItem( 'id_usuario' );
          this.nombreUsuario = localStorage.getItem( 'nombre' );
        }
    }
    resolve();
  });
  return promesa;
}

My App.componet


      this.usersProv.cargarStorage()
                    .then( ()=>{
                      console.log("Iniciando app...")
                      console.log(this.usersProv.token)
                      console.log("------------------------")
                      if(this.usersProv.token){//Validamos que tenga token y asi lo redireccionamos
                        this.rootPage = MapaPage;
                      } else {
                        this.rootPage = HomePage;
                      }
                      // Okay, so the platform is ready and our plugins are available.
                      // Here you can do any higher level native things you might need.
                      this.statusBar.styleDefault();
                      this.splashScreen.hide();
                    });

my code Xcode

2018-06-28 09:50:16.795639-0600 Carbox Drive[4794:72157] Ionic Native: deviceready event fired after 1618 ms
2018-06-28 09:50:16.797417-0600 Carbox Drive[4794:72157] I**niciando app...**
**2018-06-28 09:50:16.797698-0600 Carbox Drive[4794:72157] **
**2018-06-28 09:50:16.797972-0600 Carbox Drive[4794:72157] ------------------------**
2018-06-28 09:50:16.798845-0600 Carbox Drive[4794:72157] WARN: Native: tried calling StatusBar.styleDefault, but the StatusBar plugin is not installed.
2018-06-28 09:50:16.799079-0600 Carbox Drive[4794:72157] WARN: Install the StatusBar  plugin: 'ionic cordova plugin add cordova-plugin-statusbar'
2018-06-28 09:50:16.805027-0600 Carbox Drive[4794:72157] que putas esta pasando
2018-06-28 09:50:16.808352-0600 Carbox Drive[4794:72157] OPEN database: _ionicstorage
2018-06-28 09:50:16.808798-0600 Carbox Drive[4794:72157] -[SQLitePlugin pluginInitialize] [Line 29] Initializing SQLitePlugin
2018-06-28 09:50:16.809123-0600 Carbox Drive[4794:72157] -[SQLitePlugin pluginInitialize] [Line 40] Detected docs path: /Users/desarrollomktech/Library/Developer/CoreSimulator/Devices/0000429C-9A64-412D-B686-AB0B8A4A72A6/data/Containers/Data/Application/520D0CB0-48E7-495D-8FC9-AD86C30D0AAB/Documents
2018-06-28 09:50:16.809285-0600 Carbox Drive[4794:72157] -[SQLitePlugin pluginInitialize] [Line 44] Detected Library path: /Users/desarrollomktech/Library/Developer/CoreSimulator/Devices/0000429C-9A64-412D-B686-AB0B8A4A72A6/data/Containers/Data/Application/520D0CB0-48E7-495D-8FC9-AD86C30D0AAB/Library
2018-06-28 09:50:16.809475-0600 Carbox Drive[4794:72157] -[SQLitePlugin pluginInitialize] [Line 51] no cloud sync at path: /Users/desarrollomktech/Library/Developer/CoreSimulator/Devices/0000429C-9A64-412D-B686-AB0B8A4A72A6/data/Containers/Data/Application/520D0CB0-48E7-495D-8FC9-AD86C30D0AAB/Library/LocalDatabase
2018-06-28 09:50:16.809892-0600 Carbox Drive[4794:72405] -[SQLitePlugin closeNow:] [Line 203] close: db name was not open: _ionicstorage
2018-06-28 09:50:16.809967-0600 Carbox Drive[4794:72157] new transaction is queued, waiting for open operation to finish
2018-06-28 09:50:16.853405-0600 Carbox Drive[4794:72157] WARN: 
    It looks like you're using ngModel on the same form field as formControlName. 
    Support for using the ngModel input property and ngModelChange event with 
    reactive form directives has been deprecated in Angular v6 and will be removed 
    in Angular v7.
    
    For more information on this, see our API docs here:
    https://angular.io/api/forms/FormControlName#use-with-ngmodel
2018-06-28 09:50:16.943398-0600 Carbox Drive[4794:72349] -[SQLitePlugin openNow:] [Line 141] open full db path: /Users/desarrollomktech/Library/Developer/CoreSimulator/Devices/0000429C-9A64-412D-B686-AB0B8A4A72A6/data/Containers/Data/Application/520D0CB0-48E7-495D-8FC9-AD86C30D0AAB/Library/LocalDatabase/_ionicstorage
2018-06-28 09:50:16.969520-0600 Carbox Drive[4794:72349] -[SQLitePlugin openNow:] [Line 168] Good news: SQLite is thread safe!
2018-06-28 09:50:17.004614-0600 Carbox Drive[4794:72157] OPEN database: _ionicstorage - OK
2018-06-28 09:50:17.004954-0600 Carbox Drive[4794:72157] DB opened: _ionicstorage
2018-06-28 09:50:17.045401-0600 Carbox Drive[4794:72157] **Si habia token b601d5f61ce4ac34f6d04e31135dce1c35a4658c**

In the Xcode opens the home because as it runs it has no token but the token does exist


#2

Didn’t read all your code but noticed a possible problem at the begin, storage.set return a promise, doc

/**
 * Set the value for the given key.
 * @param {any} key the key to identify this value
 * @param {any} value the value for this key
 * @returns {Promise} Returns a promise that resolves when the key and value are set
 */
set(key: string, value: any): Promise<any>;

That might be the problem, on your laptop it goes fast, so no problem, but since the phone is a bit slower the execution might not be finished when you try to access the value

Try to chain the promise like

this.storage.set( 'token', token ).then((savedToken) => {
  console.log('cool now I could do stuffs');
});

#3

Thanks.

I do not understand your solution, excuse me.
My storage if the value is stored but at the time you should read it is not doing it or it is very slow and loads the page as if the user did not have a token but the token does exist but it is loaded after the page is loaded, not I know if I explain


#4

You should probably learn and read more about promises first. But to summarize really roughly, just think that these are “parallel” tasks which “may end at some point but you don’t know when”

since this.storage.set is a promise, you don’t know when it’s finished except if you subscribe to it like I displayed above means maybe when you try to this.storage.get the value maybe because the device is slower the set isn’t finished yet

That might be absolutely not your problem but still, I think it’s a good start


#5

Thanks Richard.
If in reality the promises I still have a bit of confusion but I think that if I am subscribed to the promise of cargaStorage or it is really where I am failing, I do not know and that is why I humbly ask for help.


#6

No worries at all it’s cool. To be safe, if you see a method returning a promise just do always like you did with get respectively “subscribing” to it with .then(() =>) and you gonna be safe

But like I said, not sure it’s your bug, just a start, good luck


#7

In addition to the (very real) race condition @reedrichards is concerned with, guadarStorage does not appear to be waiting for storage to be ready before it is trying to interact with it.

The bottom line way I avoid race conditions with storage is to only use it to communicate with the next time the app is started, never within a single run of the app. That means:

  • only reading from storage once at app startup time
  • always having another mechanism aside from storage to convey changes that occur during an app run, such as a Subject in a provider

#8

Thanks rapropos.
I have seen you with your solutions really pro, I congratulate you since you always try to help us and solve our silly things.
Maybe I have not explained myself well, when the user registers successfully I give him a unique token which I keep and is the one I use to validate that he already made the registration and I also opted it when the user closed session and reopened session, ok the token if I save it in the storage because in the Xcode it tells me, but I “try to subscribe to the method of LoadStorage” but the first one validates the token and then loads the Storage then it tells me that there is no token, the truth I do not know what’s wrong … and that’s what I’m trying to do :frowning:
Thanks in advance

ingresar (correo:string, contrasena:string) {
  let data = new URLSearchParams();
  data.append("correo", correo);
  data.append("contrasena", contrasena);

  let url = URL_SERVICIOS + "usuarios/login";

  return this.http.post(url, {correo, contrasena} )
                  .map( resp=>{
                    if( resp['error'] ) {
                      this.alertCtrl.create({// creo la alerta
                        title:"Error en sus credenciales",
                        subTitle: resp['mensaje'],
                        buttons: [ "OK" ]
                      }).present();
                    }
                    else {
                      this.alertCtrl.create({// creo la alerta
                        title:"Bienvenido a Carbox",
                        subTitle: "El App que te genera",
                        buttons: [ "OK" ]
                      }).present();
                      this.token = resp['token']; //Grabo el token
                      this.id_usuario = resp['id']; //Grabo el usuario
                      this.nombreUsuario = resp['nombre']; //Grabo el usuario
                      console.log("Datos:", this.token, this.id_usuario, this.nombreUsuario);



                     Here I call saveStorage

                      this.guadarStorage(this.token, this.id_usuario, this.nombreUsuario);



                    }
                  })
}

Here I try to subscribe to loadStorage


      this.usersProv.cargarStorage()
                    .then( ()=>{
                      console.log("Iniciando app...")
                      console.log(this.usersProv.token)
                      console.log("------------------------")
                      if(this.usersProv.token){//Validamos que tenga token y asi lo redireccionamos
                        this.rootPage = MapaPage;
                      } else {
                        this.rootPage = HomePage;
                      }
                      // Okay, so the platform is ready and our plugins are available.
                      // Here you can do any higher level native things you might need.
                      this.statusBar.styleDefault();
                      this.splashScreen.hide();
                    });

#9

When you call cargarStorage(), think of it as receiving what was put in storage during a previous app run. If that’s not what is needed, then storage is the wrong tool for the job.

What I would do is something like the following:

class TokenService {
  private _token = new BehaviorSubject<string>("");
  constructor(private _storage: Storage) {
    this._storage.ready()
      .then(() => this.storage.get("token"))
      .then(ot => this._token.next(ot));
  }
  getToken(): Observable<string> {
    return this._token.asObservable();
  }
  setToken(nt: string): void {
    this._token.next(nt);
    this._storage.ready().then(() => this._storage.set("token", nt));
  }
}

This way you are using rx for in-app communication, and storage for communicating across app restarts. Therefore you don’t care that setToken is optimistic and ignores the promise returned by the storage set call. The way you have written it now, you are reliant on the timing of set and you’re not paying attention to it.


#10

Thanks rapropos
I had the problem in saving and exporting.
I’m honest with your code, I did not know how to implement it, but with closed eyes I know that it should work.
regards