-
This project allows the use of Kotlin with the Scriptable iOS app. It is not meant to be better or easier, I just love using Kotlin.
-
This is made possible utilizing Dukat to generate external declarations which provide type information from the iOS Scriptable Types project.
-
Once you have written your own scripts you can use the Scriptable app to run them on your iOS device.
-
Uses esbuild to bundle each script into its own file, only including dependencies that are actually used by the script.
-
To be able to efficiently create your own scripts you will need a basic understanding of Kotlin.
Warning
This is a new project and is still in development. Let me know if you run into any issues or have any questions.
The root build.gradle.kts file implements the main plugin, which is responsible for generating the projects used for each Scriptable script.
-
The project is configured using the scriptable extension:
-
plugins { id("scriptable-main") } scriptable { // ... }-
- Returns the value of the specified environment variable, or throws an error if the variable is not defined.
- This is a shortcut for
providers.environmentVariable(name).get(). - If the environment variable has only been set since your IDE has been open, you may need to restart your IDE for the change to take effect.
-
environment("ICLOUD_SCRIPTABLE_DIRECTORY")
-
- Returns the value of the specified Gradle property, or throws an error if the property is not defined.
- This is a shortcut for
providers.gradleProperty(name).get(). -
properties("icloud.drive.path")
-
- Required.
- You should have iCloud Drive setup on your PC, and this should be the path to the Scriptable folder in your iCloud Drive.
- This is where the script files will be copied to when you run the "sync" task.
-
iCloudScriptableDirectory.set(file(environment("ICLOUD_SCRIPTABLE_DIRECTORY"))) // or iCloudScriptableDirectory.set(file(properties("icloud.drive.path"))) // or iCloudScriptableDirectory.set(file("path/to/icloud/drive/Scriptable"))
-
- Not required.
- Defaults to
true - If set to
true, the scripts will be minified when they are bundled, helping keep the file size to a minimum. -
defaultMinifyScripts.set(false)
-
defaultIcon [ScriptIcon]
- Not required.
- Defaults to
ScriptIcon.Desktop - This is the value that will be implemented with
ScriptIcon.Default -
defaultIcon.set(ScriptIcon.Desktop)
-
defaultColor [ScriptColor]
- Not required.
- Defaults to
ScriptColor.DeepGray - This is the value that will be implemented with
ScriptColor.Default -
defaultColor.set(ScriptColor.DeepGray)
-
- Adds a scriptable to the project, automatically creating non-existent projects within the scripts project.
- Scripts removed from this will not be deleted automatically, but will not be included in the project build any longer.
- name:
- Required.
- The name of the scriptable. This is the name for the script as shown in the Scriptable app.
- module:
- Not required.
- Defaults to name.toKebabCase() (e.g.
"My Script"becomes"my-script").
- icon:
- Not required.
- Defaults to
ScriptIcon.Default, which uses the project default defined above. - Acceptable values are:
- ScriptIcon enum. (e.g.
ScriptIcon.Desktop,ScriptIcon.UserShield) - ScriptIcon enum name. (e.g.
"Desktop","UserShield") - String value shown in the Scriptable app. (e.g.
"desktop","user-shield")
- ScriptIcon enum. (e.g.
- color:
- Not required.
- Defaults to
ScriptColor.Default, which uses the project default defined above. - Acceptable values are:
- ScriptColor enum. (e.g.
ScriptColor.DeepGray,ScriptColor.DeepBlue) - ScriptColor enum name. (e.g.
"DeepGray","DeepBlue") - String value shown in the Scriptable app. (e.g.
"deep-gray","deep-blue")
- ScriptColor enum. (e.g.
- name:
-
// You can use any combination of the accepted icon / color formats. include("My Script", "my-script", ScriptIcon.Desktop, ScriptColor.DeepGray) // or include("My Script", "my-script", "Desktop", "DeepGray") // or include("My Script", "my-script", "desktop", "deep-gray") // or include("My Script", "my-script", "Desktop", ScriptColor.DeepGray) // or include("My Script", "my-script", "desktop", ScriptColor.DeepGray) // or include("My Script", "my-script", ScriptIcon.Desktop, "DeepGray") // Or leave them off to use your project defaults include("My Script", "my-script")
-
- Shortcut for include described above, automatically using
name.toKebabCase()for the module name.
- Shortcut for include described above, automatically using
-
-
scriptable { iCloudScriptableDirectory.set(file(environment("SCRIPTABLE_ICLOUD_PATH"))) iCloudScriptableCacheDirectory.set(file(environment("SCRIPTABLE_DATA_PATH"))) defaultMinifyScripts.set(false) defaultIcon.set(ScriptIcon.AddressCard) defaultColor.set(ScriptColor.DeepPurple) include("ShowTableExample", "show-table-example", ScriptIcon.Table, ScriptColor.DeepGreen) include("show-alert-example", color = ScriptColor.DeepOrange) }
-
-
-
[!IMPORTANT]
- You need to have iCloud Drive setup on your PC, and you need to have the Scriptable app installed on your iOS device.
- Once you've done that, you need to set up the project configuration.
- If you want to use your system environment for the iCloud Path, you will need to restart your IDE for the change to take effect, unless you happened to have it saved already.
-
The main plugin
- Responsible for generating the projects used for each Scriptable script.
- To add a script, you need to add it to the project configuration.
- To remove a script, you need to first remove it from the configuration, and then manually delete the files. If you don't remove it from the configuration, it will repopulate to the default new script setup.
-
The initialize task
- Triggers evaluation of your configuration and applies any changes you have made.
- This should be triggered automatically, but if you need to manually trigger it you can run the
initializetask. - This task will automatically run before the
synctask. - Only scripts that have been added, changed, or removed will be effected by this task.
- This task will not delete any files, only create or update them.
-
The sync task
- Builds the project, processing it into javascript code that can be run by the Scriptable app.
- Packages the scripts into their own files, only including dependencies that are actually used by the script.
- Copies the scripts to the iCloud Scriptable directory, so you can run/test them directly on you iOS device.
- Only processes scripts that have been effected by changes you have made since the last sync.
- This can be applied per script in the gradle menu, or in the root project to sync all scripts.
-
- Is where the declarations for the Scriptable API are stored. The ios-scriptable-types project is used to generate these declarations.
- This project is automatically included in the scripts project and the library project.
- None of this will be compiled into your script, it will use the declarations to provide type information for the Scriptable API.
-
The library project
- Is where you can create your own libraries to be used by your scripts.
- The scripts will automatically have access to this library, and will only include what they need from it when they are bundled.
-
The scripts project
- Is where you can create your own scripts.
- A project will be created in this directory for each script you add to the project configuration.
- The scripts will automatically have access to the scriptable project and the library project, and will only include what they need from them when they are bundled.
The examples will include the Kotlin version and Javascript equivalent. Not as a comparison of better or worse, but to show what it is doing.
You can create your own Kotlin DSL style builders.
You can create your own Kotlin DSL style builders.
-
TableBuilder.kt is a simple example of a builder that can be used to create a table for use in a Scriptable script.
-
fun main() { val table = buildTable { row { isHeader() text("Example Title", "example subtitle") } } table.present() }
-
-
The Javascript equivalent:
-
const table = new UITable() const headerRow = new UITableRow() headerRow.isHeader = true headerRow.addText("Example Title", "example subtitle") table.addRow(headerRow) await table.present()
-
- AlertBuilder.kt
-
fun main() { val alert = buildAlert( "Kotlin Alert", "This is an alert from Kotlin!" ) { input("username", "Username") secureInput("password", "Password") action("Login") cancel() } alert.present { when (this) { is AlertResult.Fulfilled -> { if (wasCancelled) { QuickLook.present("Alert was cancelled") return@present } val username = inputs["username"] val password = inputs["password"] QuickLook.present("Username: $username\nPassword: $password") } is AlertResult.Rejected -> QuickLook.present("Alert was rejected with message: $message") } } }
-
- The Javascript equivalent:
-
const alert = new Alert() alert.title = "Kotlin Alert" alert.message = "This is an alert from Kotlin!" alert.addTextField("Username") alert.addSecureTextField("Password") alert.addAction("Login") alert.addCancelAction("Cancel") const result = await alert.presentAlert() if (result === -1) { QuickLook.present("Alert was cancelled") } else { const username = alert.textFieldValue(0) const password = alert.textFieldValue(1) QuickLook.present(`Username: ${username}\nPassword: ${password}`) }
-
- The result:

-
- If you don't already have it, you will need
esbuildfrom npm.npm install --save-exact --save-dev esbuildNewly installing this may require IDE restart for gradle to recognize it. - The
gradle.propertiesfile in each script project is updated automatically, changes made to it will be overwritten automatically. Make changes in the rootbuild.gradle.ktsfile.
- If you don't already have it, you will need
