Working with other apps
Many add-ons show something from another app: notes from Notes, the queue from Music, today's events from Calendar. On the Mac that usually means scripting the app. Here is how to do it so it's safe, quick and clear to the person using it.
Run fixed scripts
Apps like Notes, Music, Reminders and Finder can be scripted with JavaScript for Automation or AppleScript, through /usr/bin/osascript. Keep each script as fixed text in your add-on, and pass anything that changes, like a note's id or a count, as an argument. Never paste a value into the script's text: a note named with a quote and a command could otherwise change what runs.
/// Brings Notes forward on one note. argv[0] is the note's id.
let openNote = """
function run(argv) {
const Notes = Application("Notes");
Notes.activate();
Notes.show(Notes.notes.byId(argv[0]));
}
"""
let process = Process()
process.executableURL = URL(fileURLWithPath: "/usr/bin/osascript")
process.arguments = ["-l", "JavaScript", "-e", openNote, note.id] // the id is an argument, not text in the scriptReturn what you read as JSON (JSON.stringify in JavaScript) and decode it in Swift. Dates come back with milliseconds, which .iso8601 alone doesn't accept, so decode them with an ISO8601DateFormatter that has .withFractionalSeconds.
The Automation prompt
The first time your add-on scripts an app, macOS asks whether Ledge may control it, because your add-on runs inside Ledge. Until the person answers, the script waits; if they say no, every later run fails at once with error -1743.
- Show a loading state first, and give the script a deadline, so a prompt nobody has seen yet can't hang your add-on.
- Treat
-1743as its own case and say what to do:Allow Ledge to control Notes in System Settings → Privacy & Security → Automation.
- Try again a few seconds after the first failure, since the answer often comes while the first run is waiting.
guard process.terminationStatus == 0 else {
if errors.contains("-1743") { throw NotesError.notAllowed } // the person said no, or hasn't been asked again
throw NotesError.failed
}Keeping up with changes
Most apps can't tell a script that something changed, so read again on a timer. Every 30 seconds is plenty for a list someone glances at, and read again straight after your add-on changes something itself. Ask for only what you show: the newest five titles rather than every note's body.
Other people's data
What you read belongs to the person using your add-on. Keep it on their Mac, show only what the add-on is for, and don't write it to your log: log that a read failed, not what it returned. Say on your settings page what you read and that it stays on the Mac.