Storing User Data on the Web

by Ruaridh Bell on June 30, 2023 6 comments

Introduction

In the world of software development, one of the most important considerations is how to store and manage data across different sessions and users. One way to achieve this is through local storage. In this blog post, we will explore how to store user specific data within LiveCode as well as in your deployed web apps using local storage, demonstrating how it works, and its benefits.

Using LocalStorage within LiveCode

Within this post in order to demonstrate how each of these methods work we are going to be creating an app that allows you to store your name, age and location/city that will eventually be displayed when you start up your app, just using local storage methods. This stack can be downloaded here.

Writing to Preferences

Within LiveCode the conventional way of saving user specific data would be to store it within a file in the Preferences folder. This can be done using the short code snippet below. This function takes in a key and a value and stores the data in a key-value pair within a json file with the same name as the key.

command writePref pKey, pValue
  local tPrefsFile
  put getPrefFileName() into tPrefsFile

  open file tPrefsFile
  read from file tPrefsFile until EOF

  local tPrefsData
  if it is not empty then
    put jsonToArray(it) into tPrefsData
    put pValue into tPrefsData[pKey]
  end if

  open file tPrefsFile for write
  Write arrayToJson(tPrefsData) to file tPrefsFile
  close file tPrefsFile
end writePref

function getPrefFileName
  switch the platform
    case "macos"
      return specialFolderPath("Preferences") & "/" & \
        the short name of this stack & ".json"
      break
    case "win32"
      return specialFolderPath("support") & "\" & \
        the short name of this stack & ".json"
      break
    case "linux"
      return specialFolderPath("home") & "/" & \
        the short name of this stack & ".json"
      break
  end switch
end getPrefFileName

You can read more about “specialFolderPath” and “write to file” via the LiveCode Dictionary.

Recovering data from Preferences

Once we have written to the preferences we can then use the function below to retrieve the key-value pairs

function getPref pKey
  local tPrefsFile
  put getPrefFileName() into tPrefsFile

  open file tPrefsFile
  read from file tPrefsFile until EOF
  close file tPrefsFile

  if it is not empty then
    local tUserData
    put JsonToArray(it) into tUserData
    return tUserData[pKey]
  end if
end getPref

Removing data from Preferences

We also need to be able to remove data from the Preferences, this can be done in two ways, deleting individual key-value pairs or clearing all the stored data. Both methods are shown below

command deletePref pKey
  local tPrefsFile
  put getPrefFileName() into tPrefsFile

  open file tPrefsFile
  read from file tPrefsFile until EOF

  local tPrefsData
  if it is not empty then
    put jsonToArray(it) into tPrefsData
    delete variable tPrefsData[pKey]
  end if

  open file tPrefsFile for write
  write arrayToJson(tPrefsData) to file tPrefsFile
  close file tPrefsFile
end deletePref

command clearPref
  local tPrefsFile
  put getPrefFileName() into tPrefsFile

  delete file tPrefsFile
end clearPref

Deploying to Web

When deploying your application to the web you are essentially sandboxed in and are not able to access the filesystem to be able to read from or write to the preferences folder. Although it would be simpler to use this same method of storing data to a file, this is however not possible within LiveCode. This is where localStorage can be useful.

The LocalStorage object is one of the most widely used objects in web programming as it provides a simple solution for storing key-value pairs locally on a user’s computer with no expiration date.These values can be strings, numbers, booleans, or even objects. Here, stored data will remain available even when you close the browser or tab. 

{
  key: value
}

While LocalStorage is available for the majority of modern browsers some older ones may not support it, you can quickly verify if your browser supports the web storage API by opening your browsers DevTools, navigating to the “Console”, typing in the code snippet shown below.

if (typeof(Storage) !== "undefined") {
    // Has LocalStorage support
    console.log("Local Storage Supported");
    } else {
    // No LocalStorage Support.
    console.log("Local Storage Not supported");
}

It’s worth noting that LocalStorage has some limitations. Firstly, it is limited to storing data up to a size limit of 5MB, this varies on browsers as some can have less, however most use this 5MB limit. Secondly, LocalStorage is synchronous, meaning that any data read or written to LocalStorage will block the main thread until the operation is complete. This can lead to performance issues if you’re storing large amounts of data or performing frequent reads and writes.

Using LocalStorage traditionally in JavaScript

To use localStorage in your web applications, there are four main methods to choose from:

  • localStorage.setItem() – to create a new key-value pair
  • localStorage.getItem() – to retrieve the value of a key
  • localStorage.removeItem() – to remove a specific pair
  • localStorage.clear() – deletes ALL saved pairs for that domain

A quick and easy way to view all the data saved in your browser’s localStorage is to navigate to the browsers dev tools, then to Application tab and in the sidebar click Local Storage followed by the current web url you are on.

Storing Values in LocalStorage: setItem()

This method just as the name implies allows you to save the values inside LocalStorage in the browser. setItem takes in two parameters, a key and a value. This key can then be used later on with getItem() to fetch the data that is stored.

localStorage.setItem("name", "John Smith");

Recovering data from LocalStorage: getItem()

Similarly with setItem we must pass in a parameter to get the information we are trying to retrieve and as mentioned previously we can use the key that we used to store the data to subsequently retrieve it.

localStorage.getItem("name"); // Output: John Smith

Removing data from LocalStorage: removeItem() and clear()

After data is no longer needed, it’s a good practice to delete it from the localStorage to avoid accumulation of unnecessary data. When passed a key, the removeItem() method removes that key from the storage if it exists. However, if you wish to remove all the stored data the clear() method can be used.

localStorage.removeItem("name");
localStorage.clear();

You can read more about each of these methods in the mdn web docs.

Adapting our app for web

To achieve the same result as on desktop in our app where we stored user information that would appear when the app was loaded the following snippet of code can be used.

do "window.localStorage.setItem(key, value);" as "javascript"

The do as method in LiveCode takes the written code and runs it in the language specified. This allows us to run JavaScript in the browser where we are able to make use of LocalStorage.

Please note that the web browser and JavaScript expect the key and value to be a string and since our values are also passed as strings we must convert them to that, the following function can be used.

function escapeData pValue
  replace backslash with "\\" in pValue
  replace quote with backslash & quote in pValue
  replace return with "\n" in pValue
  replace cr with "\r" in pValue

  return quote & pValue & quote
end escapeData

This function takes in a value and converts all special characters such as, return, quotes, or backslash to their string counterpart so that they don’t interfere with anything, then returns the value as a string surrounded by quotes.

command writePref pKey, pValue
  if the platform is "web" then
    do "window.localStorage.setItem(" & escapeData(pKey) & "," & \
        escapeData(pValue) & ");" as "javascript"
  else
    local tPrefsFile
    put getPrefFileName() into tPrefsFile

    open file tPrefsFile
    read from file tPrefsFile until EOF

    local tPrefsData
    if it is not empty then
      put jsonToArray(it) into tPrefsData
      put pValue into tPrefsData[pKey]
    end if

    open file tPrefsFile for write
    Write arrayToJson(tPrefsData) to file tPrefsFile
    close file tPrefsFile
  end if
end writePref

function getPrefFileName
  switch the platform
    case "macos"
      return specialFolderPath("Preferences") & "/" & \
          the short name of this stack & ".json"
      break
    case "win32"
      return specialFolderPath("support") & "/" & \
        the short name of this stack & ".json"
      break
    case "linux"
      return specialFolderPath("home") & "/" & \
        the short name of this stack & ".json"
      break
  end switch
end getPrefFileName

Retrieving data from LocalStorage

Similarly to before we can retrieve data that is stored in LocalStorage by passing in a key

function getPref pKey
  if the platform is "web" then
    do "window.localStorage.getItem(" & escapeData(pKey) & ");" as "javascript"
  else
    local tPrefsFile
    put getPrefFileName() into tPrefsFile

    open file tPrefsFile
    read from file tPrefsFile until EOF
    close file tPrefsFile

    if it is not empty then
      local tUserData
      put JsonToArray(it) into tUserData
      return tUserData[pKey]
    end if
  end if
end getPref

Removing data from LocalStorage

To remove data from LocalStorage two methods can be used, removeItem() and clear(). removeItem expects a key to be passed in where this is the item to be removed whereas clear removes all data currently stored in LocalStorage.

command deletePref pKey
  if the platform is "web" then
    do "window.localStorage.removeItem(" & escapeData(pKey) & ");" \
        as "javascript"
  else
    local tPrefsFile
    put getPrefFileName() into tPrefsFile

    open file tPrefsFile
    read from file tPrefsFile until EOF
  local tPrefsData
    if it is not empty then
      put jsonToArray(it) into tPrefsData
      delete variable tPrefsData[pKey]
    end if

    open file tPrefsFile for write
    write arrayToJson(tPrefsData) to file tPrefsFile
    close file tPrefsFile
  end if
end deletePref
command clearPref
  if the platform is "web" then
    do "window.localStorage.clear();" as "javascript"
  else
    local tPrefsFile
    put getPrefFileName() into tPrefsFile

    delete file tPrefsFile
  end if
end clearPref

Conclusion

In summary, LocalStorage is a powerful tool and its ease of use makes it great for storing small amounts of data in a user’s web browser. However, it’s important to keep in mind the limitations of LocalStorage, particularly its size limits and synchronous nature. If you need to store larger amounts of data or perform frequent reads and writes, you may want to consider using other storage mechanisms such as a server-side database.

Ruaridh BellStoring User Data on the Web

6 comments

Join the conversation
  • François Tarpin - July 3, 2023 reply

    Great article !! Powerful and easy !
    Only, I am a bit surprised that you write file access with ‘open’, ‘write’ and ‘close’ instead of ‘put … into URL…’ which is simpler. Is there any specific reason for that?

    Ruaridh Bell - July 3, 2023 reply

    Hey François,

    There was no specific reason for this method and in hindsight using that method would’ve been easier haha. So you could just replace the code;

    open file tPrefsFile for write
    Write arrayToJson(tPrefsData) to file tPrefsFile
    close file tPrefsFile

    with

    put arrayToJson(tPrefsData) into URL(“file:” & tPrefsFile)

    To achieve the same result and similarly, for example in the function ‘getPref’, you could replace the code

    open file tPrefsFile
    read from file tPrefsFile until EOF
    close file tPrefsFile

    if it is not empty then
    local tUserData
    put JsonToArray(it) into tUserData
    return tUserData[pKey]
    end if

    with

    local tFileData
    put URL(“file:” & tPrefsFile) into tFileData

    if tFileData is not empty then
    local tUserData
    put JsonToArray(tFileData) into tUserData
    return tUserData[pKey]
    end if

  • Rolf Kocherhans - July 4, 2023 reply

    Without the powerbutton widget one can not see the buttons on the stack. On my LC 10.0.0-dp-6 the powerbutton widget is not preinstalled ? Is this correct ?

    Ruaridh - July 4, 2023 reply

    Hey Rolf,

    Sorry about that, I’ve edited the stack to utilise regular buttons and you can download that here (https://livecode.com/documents/BlogPostregularbuttons.livecode.zip).

    Regarding the Power Button, that is an add-on and you can get it from our Extensions store here (https://livecode.com/extensions/power-button/1-2-4/) if you don’t have it already.

  • Andreas - July 8, 2023 reply

    Thanks Ruaridh, very useful! But in the web deployment, how does the getPref function return the value from the “do “window.localStorage.getItem…” call? Does it go into the it variable? It seems the function does not need a “return” line to return the fetched data…? It works, but I don’t understand how… 🙂

    Ruaridh - July 10, 2023 reply

    Hey Andreas,

    There should be a return after the “do as js” and i’ve updated the stack linked with the post to add this.

    The current code does however work, as the “do as js” puts the variable into ‘the result’ and if a function doesn’t return a value, the return value will be ‘the result’. I didn’t notice the lack of return when I made this and it’s always best practice to add a return statement instead of relying on it returning the result itself.

Join the conversation

*