How to Update Array on Local Storage in Ionic 4?

I’m trying to update an array in Ionic 4 with the following Data Structure,

[{"name":"foo","country":"boo","photos":['img1.jpeg', 'img2.jpeg']}]

My storage.service.ts is like,

import { Injectable } from '@angular/core';
import { Plugins, Capacitor } from '@capacitor/core';

const { Storage } = Plugins;

@Injectable({
    providedIn: 'root'
})
export class StorageService {
     public objects = [];

    constructor() { }

    async setObject(key: string, value: any) {

      let retrieve_key = await Storage.get({ key });

      if (retrieve_key.value==null){
        await Storage.set({key, value: JSON.stringify([value])});
      }
      else{
        this.objects = (JSON.parse(retrieve_key.value));
        this.objects.length>0?this.clear:console.log('Pushed')
        await Storage.set({ key, value: JSON.stringify(this.objects.unshift(value))});
      }

    }

    async clear() {
        await Storage.clear();
    }
}

My person.page.ts is like,

setStorage() {
    //this.storage.setString('name', this.name);
    this.storage.setObject('person', {
      name: this.name,
      country: this.country,
      photosx: this.photoService.photos
    });
  }

I am able to create the array but unable to update the array with new value, stuck with following error,

ERROR Error: Uncaught (in promise): TypeError: this.objects.unshift is not a function
TypeError: this.objects.unshift is not a function
    at StorageService.<anonymous> (storage.service.ts:32)
    at Generator.next (<anonymous>)
    at fulfilled (tslib.es6.js:71)
    at ZoneDelegate.invoke (zone-evergreen.js:364)
    at Object.onInvoke (core.js:41667)
    at ZoneDelegate.invoke (zone-evergreen.js:363)
    at Zone.run (zone-evergreen.js:123)
    at zone-evergreen.js:857
    at ZoneDelegate.invokeTask (zone-evergreen.js:399)
    at Object.onInvokeTask (core.js:41645)
    at resolvePromise (zone-evergreen.js:798)
    at zone-evergreen.js:705
    at fulfilled (tslib.es6.js:71)
    at ZoneDelegate.invoke (zone-evergreen.js:364)
    at Object.onInvoke (core.js:41667)
    at ZoneDelegate.invoke (zone-evergreen.js:363)
    at Zone.run (zone-evergreen.js:123)
    at zone-evergreen.js:857
    at ZoneDelegate.invokeTask (zone-evergreen.js:399)
    at Object.onInvokeTask (core.js:41645)

What’s wrong I am doing?

I would get rid of providedIn: 'root'. It seems like a neat idea at first, but it really limits your flexibility to mock services out for testing and debugging. Then, I would reevaluate what, how, and when you’re storing.

I treat storage like the “hibernation” feature of laptops, where state is saved in preparation for a situation where everything might be lost, and upon resumption we read back our saved state and the rest of the app never knows anything happened. This has a few implications:

  • only read from storage at fixed times: at app startup or resumption, or at first request
  • no need to ever bother waiting on storage writes
  • don’t rely on storage to communicate amongst parts of a running app, just use live copies of data residing in services for this

I prefer Ionic Storage to Capacitor Storage here, because it’s a front for localForage, meaning it degrades gracefully to IndexedDB in browsers, and because it handles typed marshalling and unmarshalling automatically.

The data structure you describe in your question is rather unusual, in that it’s a one-element array. The best fit for NoSQL-type storage engines like the ones we’re working with here is a bunch of relatively small independent objects that can be easily identified by a unique index. The obvious candidate here is name: is that guaranteed to be globally unique?

I vastly prefer having services like this deal strictly in business-layer objects in their public interfaces, and I really dislike the way any is being abused here. So I would instead have a public face that looks like:

interface Destination {
  name: string;
  country: string;
  photos: string[];
}

class DestinationService {
  loadDestination(name: string): Promise<Destination> {...}
  saveDestinaton(d: Destination): void {...}
}
2 Likes

Thank You @rapropos for your response, I will check and let you know the update.