SuperSwift

Inputs, Outputs & Shortcuts

Read input from Shortcuts or the share sheet and hand results back with RunContext.

Every app you write can take input and return a result through RunContext. The same code works however the app is started: run directly, from a shortcut, from the share sheet, or from a Home Screen link.

import SuperSwift

let run = RunContext.current
run.source  // .app, .shortcut, .shareSheet or .urlScheme
run.input   // what the caller passed in

RunContext.current is always available. When you run the app directly, source is .app and the input is empty, so you can test your code before connecting it to a shortcut.

Console output is not a result

print only writes to the console for debugging. A shortcut only receives what you pass to run.finish(with:), so adding or removing a print never changes what the shortcut receives.

Reading input

The input is an ordered list of items. Each item is text, a URL or a file. There are shortcuts for the most common cases:

PropertyTypeContents
input.isEmptyBoolWhether anything was passed in
input.textString?The first text item
input.texts[String]Every text item
input.urls[URL]URL items, plus text items that are exactly one URL
input.files[InputFile]Files, each with name, contentType (a UTType identifier such as public.png), data and text
input.jsonJSONValue?The first text item parsed as JSON
input.items[RunInputItem]Every item in its original order. Each item exposes text, url or file

JSON input

Shortcuts turns a dictionary or a list into JSON text when you pass it to the Input parameter, so input.json gives you the structure back. You can index into it with a key or a position, and read values with string, number, bool, array and object:

import SuperSwift

let json = RunContext.current.input.json
let bill = json?["bill"]?.number ?? 0
let firstTag = json?["tags"]?[0]?.string

Why JSONValue and not Codable?

The input's structure is only known at run time. Neither the Swift standard library nor Foundation has a public type for JSON of unknown shape, so RunContext uses JSONValue instead. It's a plain enum with the cases .string, .number, .bool, .array, .object and .null.

Returning a result

Call finish or fail to end the run. The first call decides the result. Code after it doesn't run, and any timers stop.

run.finish()                                  // done, no result
run.finish(with: .text("Saved"))              // text
run.finish(with: .json(.object([              // structured data
    "total": .number(46),
    "items": .array([.string("a"), .string("b")])
])))
run.finish(with: .url(URL(string: "https://superswift.app")!))
run.finish(with: .files([
    OutputFile(name: "report.csv", contentType: "public.comma-separated-values-text", text: "a,b\n1,2\n")
]))
run.fail("Bill must be positive")             // stop with an error

Other ways a run can end:

What happensResult
A script reaches its last lineSame as finish()
The user closes an app that has a user interfaceSame as finish()
fail(_:) is called, an error isn't caught, or the code doesn't compileThe run fails and the shortcut stops with the message
finish or fail is called a second timeIgnored, and a warning is printed to the console

Top-level control flow

Top-level code in a script can't contain if, switch or loops. Put that logic in a function and call it, as in the example below.

Using an app in Shortcuts

  1. In the Shortcuts app, add the Run App action.
  2. Choose the app to run. Apps that import SuperSwift are marked Accepts input and appear first. The action stores the app's identifier, so renaming the app doesn't break the shortcut.
  3. Optionally fill in Input (text, a number, a dictionary or a list) and Files.
  4. Use the action's output, Run Result. Tap it to choose Text, URL or Files. A JSON result is in Text, and Get Dictionary Value can read it directly.

Example: tip calculator

This script takes {"bill": 42, "percent": 18} and returns the tip and the total as JSON.

import SuperSwift

func calculate() {
    let run = RunContext.current
    guard let bill = run.input.json?["bill"]?.number else {
        run.fail("Pass a dictionary like {\"bill\": 42}.")
        return
    }
    let percent = run.input.json?["percent"]?.number ?? 15
    let tip = bill * percent / 100
    run.finish(with: .json(.object([
        "tip": .number(tip),
        "total": .number(bill + tip)
    ])))
}

calculate()

In Shortcuts, create a Dictionary with bill and percent, pass it to Run App as Input, then use Get Dictionary Value with the key total on Run Result → Text.

Running from the share sheet

Make a shortcut that receives input from the share sheet and passes it to your app:

  1. Create a shortcut and turn on Show in Share Sheet in its details.
  2. Add Run App and set Input to Shortcut Input.
  3. In your app, read run.input.urls for web pages, run.input.text for selected text, or run.input.files for images and documents.

The shortcut then appears in the share sheet of Safari and other apps.

Interactive apps

Apps with a user interface can return a result too. When a shortcut runs one, the app opens on screen. The shortcut waits until the app calls finish or the user closes it, then carries on with the result.

import SwiftUI
import SuperSwift

@main
struct DrinkPicker: App {
    var body: some Scene {
        WindowGroup { ContentView() }
    }
}

struct ContentView: View {
    @State private var drink = "Coffee"

    var body: some View {
        Form {
            Picker("Drink", selection: $drink) {
                Text("Coffee").tag("Coffee")
                Text("Tea").tag("Tea")
            }
            Button("Done") {
                RunContext.current.finish(with: .text(drink))
            }
        }
    }
}

On iOS 17 and 18, the system asks for confirmation before opening the app. On iOS 26 and later, it opens directly.

Limits

  • A Home Screen link (superswift://run/…) never carries input. Any web page or app can open a link like this, so accepting input there would let outside content inject data into your app.
  • Each run returns one kind of result: text, JSON, a URL or files.
  • A script that runs in the background from a shortcut has no timeout of its own, but iOS can stop background work that runs for too long. For long tasks, give the app a user interface so it runs in the foreground.

On this page