Swift and the Command Line
Recently I’ve got a task to allow Electron-based desktop app to add events to macOS Calendar. There are not too many options to do that. I’ve made up a way to provide a macOS native layer to that JavaScript app.
I’ve written a macOS command line tool in Swift to add events and reminders using EventKit framework. This was truly great experience. Last time I made command line tools about 10 years ago in pure C.
A program importing NSFileHandle
to be able to write to stderr
, and C.stdlib
to return a non-zero exit status via abort()
function to denote failure. An exit status of 0
denotes success.
Electron framework allows web app to run shell commands. The program receives input data as command line arguments, the argument order is predefined with a frontend developers team. Usage example:
./myapp event "a title" 1497520000 1497529999
An event
(or reminder
) is a type of entity to create. A title. A start
and end
dates of event are in UNIX timestamp format.
The first argument that command line program receives is always a path where its executable file is situated. For this case the first argument is dropped and the result is assigned to a payload
variable.
Next, create an internal EventTask
struct with arguments mapped to Swift types. Initialize CalendarController
that internally initializes EKEventStore
, synchronoulsy requests access to macOS Calendar, and adds events.
The app is designed to throw Swift exceptions with detailed explanation of possible errors: wrong input, saving failed, no auth to Calendar given, or even if a confirmation dialog wasn’t accepted or denied in 60 seconds:
All event-creation code is performed in do
block. In case of any exception the catch
block will print error description to stderr
and exit with non-zero code. But the JavaScript team only observe if exit code is 0
or not.
I am very proud of the result. It seems like cross-platform apps are almost unviable without a some layer providing platform-specific solutions.
Next: how to reduce binary file size by 70%.