
This video is only available to subscribers. Start a subscription today to get access to this and 478 other videos.
Scripting in Swift with Marathon - Part 1
Thanks to John Sundell for joining me on this episode!
Continued in Part 2
Episode Links
- Swift by Sundell - John's excellent blog on Swift
- GitHub - JohnSundell/Marathon: Marathon makes it easy to write, run and manage your Swift scripts 🏃
- Mint · GitHub - A utility for installing Swift packages
- JohnSundell/Files · GitHub - A Swift package for working with the file system
- JohnSundell/ShellOut· GitHub - Easily run shell commands from a Swift script or command line tool
Installing Marathon
I followed the recommended instructions, which involves installing Mint first (😂):
$ brew install mint
Now that Mint is installed, use it to install Marathon:
$ mint run JohnSundell/Marathon
Writing Your first script
$ marathon create AddMigration
This creates an Xcode project in Marathon's support folder, creates the Swift package structure and links in the swift file for us to edit.
Any time we want to edit this file, we have to do so using Marathon:
$ marathon edit AddMigration
Reading Command Line Arguments
One of the first things we have to do is read command line arguments. Luckily, Foundation already has us covered there:
import Foundation
print(CommandLine.arguments)
This will print out a single argument, which is the full path to the script being run.
If you want to provide some arguments when debugging in Xcode, you can do so by editing the Xcode scheme.
Running Marathon Scripts
Running the scripts also is done via Marathon:
$ marathon run AddMigration <your> <arguments>
Working with Files
We want to work with the filesystem in this example, so we'll pull in John's library for this, helpfully called "Files". We'll need to tell Marathon keep track of the dependencies that we might want to use. We can do this in multiple ways.
The first way is to add the package as a known dependency with Marathon:
$ marathon add https://github.com/JohnSundell/Files.git
(Later when we want to update Marathon's copy of this, we can run
marathon update ...
)
This adds a cached copy of the dependency in Marathon's support folder, so we can now just import it in our script:
import Foundation
import Files
let folder = Folder.current
print("Your running from \(folder.name)")
If you run this with Xcode, the output will look something like this:
You're running from Debug
Program ended with exit code: 0
The folder name is just the single name, if you want the full path, you can use folder.path
:
You're running from /Users/ben/Library/Developer/Xcode/DerivedData/example-ccknwhxumzpmtsgmrdeepmzbsfnf/Build/Products/Debug/
Program ended with exit code: 0
To create or retrieve a folder, you can use methods on the Folder
type:
let folder = Folder.current
let subfolder = try folder.createSubfolderIfNeeded(withName: "MySubfolder")
This would create the folder if it didn't exist already
Exiting from scripts with errors
See how Xcode noted that the program ended with exit code 0? This is a Unix convention. "0" means successful, anything non-zero is an error. You can use this to communicate error codes, but it is also useful for chaining together commands:
first_run_this && then_run_that
If the first command returns a non-zero exit code, then the 2nd script will not run.
We want to honor the Unix convention of returning a non-zero value if we encounter an error.
if CommandLine.arguments.count < 2 {
print("Usage: marathon run myScript <arg>")
exit(1)
}
If we try to run this script and don't provide an argument, then the CommandLine.arguments
array will only contain 1 value: the path to the script. We then print out a helpful message and exit with the value "1", indicating an error.