Store variables in config, access from script?

Just spent a day playing around with Scriptable and love it already. One thing though that I think would be useful is the ability to store some data outside of the script and then reference it from the script (think variables in Postman). That way things like API keys and other sensitive data could be kept out of the code making sharing it with others easier.

Apologies if this has been suggested before, or if you can already do it and I’ve not seen how!

Use Scriptable’s Keychain API to handle secrets: https://docs.scriptable.app/keychain

For general data storage, you can also use FileManager to read/write configuration files.

1 Like

Perfect, I knew I must have been missing something.

Thanks!

1 Like

Hi,

The simple way I found was a module which read and delete/write a file.
If you have a better idea, I take it.

module.exports.read = (input) => {
var fm = FileManager.iCloud()
var global = fm.readString(fm.documentsDirectory() +"/malAdd/global.txt").split("\n")
for (i = 0; i < global.length; i++) {
  var tmp = global[i].split("=")
  if (tmp[0] == input) {return tmp[1]}}}
module.exports.write = (input) => {
var fm = FileManager.iCloud()
var file = fm.readString(fm.documentsDirectory() + "/malAdd/global.txt")
var global = fm.readString(fm.documentsDirectory() +"/malAdd/global.txt").split("\n")
var output = input.split("=")
for (i = 0; i < global.length; i++) {
  var tmp = global[i].split("=")
  if (tmp[0] == output[0]) {fm.writeString(fm.documentsDirectory() + "/malAdd/global.txt",file.replace(global[i],input))}}}

With its globals variables file

remState=all
var2=toto
var3=7
remCal=Scriptable&co
tempYesterday=1701514800,-3,3
alert=bitcoin

Simpler is to read/write into a .json file so you don’t need to loop to find the value.

Even simpler is using the Keychain:

Keychain.set('myvar','sone value')
const value = Keychain.get('myvar')

Thanks a lot,

My mistake to no use json !!!

I’m not fan of Keychain to use it as global variables container because you don’t have (or I miss the way) a vision of the whole storage. And if your code is very long, it will not easy to find your key name, …

Hi,

With Supermamon remarks, I do a little script to manage the keychain; following it, my module alert.
About this module, I don’t understand why I have to use a function to avoid error (if I put directly the code in the module) on the await alert.

// If you want all your yet created keys:
//   + create the Keychain with key "42keys42"
//   + set its values (example for 3 keys) with "nameOfTheKey1&@#nameOfTheKey2&@#nameOfTheKey3""
const malAdd = importModule('/malAdd/alert')
title = "Keychain"
msg = "...is a secure storage for global variables."
inputs = [["Key name",""],["[Key value]",""]]
options = ["set","get","remove","contains"]
key = await malAdd.alert(title, msg, inputs, options)

alert = new Alert
alert.addAction("OK")
keys = Keychain.get("42keys42");keysList = keys.split("&@#");keysList.sort()
switch (key[0]) {
  case 0: if (key[1] == "") {alert.title = "Alert";alert.message = "Key name is required for this method.";alert.present()}
          else {Keychain.set(key[1],key[2]);
            if (Keychain.contains("42keys42")) {key[1] = Keychain.get("42keys42") + "&@#" + key[1]}
            Keychain.set("42keys42",key[1])}
  break;
  case 1: if (key[1] == "") {
              msg = "Key?"
              choice = await malAdd.alert("",msg,"",keysList)
              alert.title = "Value of " + keysList[choice] + ":";alert.message = Keychain.get(keysList[choice])}
          else {alert.title = "Value of " + key[1] + ":";alert.message = Keychain.get(key[1])}
          alert.present()
  break;
  case 2: if (key[1] == "") {
            title = "Warning";msg = "You will remove the selected key."
            choice = await malAdd.alert(title,msg,"",keysList)
            alert.title = "Are you sure?";alert.addCancelAction("Cancel")
            confirm = await alert.present()
            if (confirm != - 1) {
              Keychain.remove(keysList[choice])
              Keychain.set("42keys42",Keychain.get("42keys42").replace(keysList[choice] + "&@#","").replace("&@#" + keysList[choice], ""))}}
          else {Keychain.remove(key[1])
            Keychain.set("42keys42",Keychain.get("42keys42").replace(keysList[choice] + "&@#","").replace("&@#" + keysList[choice], ""))}
  break;
  case 3: if (key[1] == "") {
            keys = Keychain.get("42keys42");keysList = keys.split("&@#");keysList.sort()
            msg = "Keys"
            await malAdd.alert("",msg,"",keysList)}
          else {alert.message = Keychain.contains(key[1]).toString();alert.present()}
          break;}

And the module:


module.exports.alert = (title,msg,inputs,options) => {
async function alert(title,msg,inputs,options) {
  var alert = new Alert()
  if (title) {alert.title = title}
  if (msg) {alert.message = msg}
  if (inputs.length > 0) {for (input of inputs) {alert.addTextField(input[0],input[1])}}
  if (options.length >0) {for (option of options) {alert.addAction(option)}}
  
  let response = await alert.present();
  let out = []
  out[0] = response
  for (i=0;i<inputs.length;i++) {out[i + 1] = alert.textFieldValue(i)}
  return out}

let out2 = alert(title, msg, inputs, options)
return out2}