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.
6 comments
Join the conversationFrançois Tarpin - July 3, 2023
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
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
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
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
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
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.