Command Line Swift
In this series we will explore the world of making command line apps in Swift. We'll see how to leverage Swift Package Manager to bring in useful dependencies, how to parse command line arguments, and how to work with files and sub processes.
Length: about 2 hours.
I use a collection of command line scripts that help me in the production of NSScreencast episodes. Most are written in Ruby, which is a language I know and love, and some are written in bash, which is... a language. I sometimes wonder what these scripts would look like if I were to write them in Swift. So in this new series I'm going to explore rewriting a script that I use to encode videos using Swift. In this episode we'll bring in the excellent Swift Argument Parser library and use it to give us a clean and consistent command line interface to start with.
Working with files and folders with FileManager is cumbersome. Instead we'll lean on the excellent Files package from John Sundell.
In this episode we will launch other programs as sub processes and show how to capture its output.
I always like it when command line programs offer colored output, which helps to discern between various stages of output, highlight errors and other important information. This is done with ANSI color codes, which are a somewhat archaic technique, but we can implement a nice wrapper to make it easy to work with in Swift.
When dealing with long running tasks it would be nice to be able to gather output as the task runs and not hang until the entire process is done. In this episode we will extract some useful information out of ffprobe, so we can get the total number of frames in a video file, and then kick off ffmpeg to encode the video. We'll use the Subprocess package to provide a simpler interface over gathering output from a pipe as it is sent, rather than waiting for the end.
Now that we have some streaming output from the ffmpeg process, we can take this and create a custom progress bar. Since this could useful in other utilities we can create it as a separate Swift package and import it using a local file reference.