Alexito's World

A world of coding šŸ’», by Alejandro Martinez

The state of Swift scripting, writing the script

This is the second part of my posts about the sate of scripting with Swift. In the previous post I wrote about what I expect from a language toolset when writing scripts and where Swift still needs some improvements. In this post I want to do a quick overview of the libraries that I used to write the script.

The examples of this posts are taken from the READMEs of each library. I recommend you to read them for more examples.

Arguments

One of the things that a script needs is the ability to process the launch arguments. Foundation lets you access those arguments quite easily with the Process.arguments array, but usually you want to use some library that gives you a structured access to them instead of having to scan the array and process it yourself in every new script.

There are many libraries that offer a way of doing it, but Commander is my preferred option. The thing that makes it different is that it allows you to describe your commands with their arguments and options. Even better, it allows you to write the code for each command in its own closure which gives you an easy way to structure your scripts.

command( Option("name", "world"), Option("count", 1, description: "The number of times to print.") ) { name, count in for _ in 0..<count { print("Hello (name)") } }

It even letā€™s you give descriptions to commands and arguments that are automatically exposed to the usual --help command.

$ hello --help Usage:
$ hello
Options: --name --count - The number of times to print.

This way of structuring the script in commands is really nice, but if your script is way simpler you can just use any other library to just read the arguments when you need them.

Interactive

Swiftline is a nice library that has multiple features. One of them is parsing the arguments, but as I said I prefer to use Commander for that.

The feature that Iā€™m most interested in is the ability to turn the scripts in something more interactive in a quick and easy way.

let age = ask("How old are you?", type: Int.self)

Asking for user input on its own is not that difficult, but this library offers a set of methods for the most common cases of input. Swiftline will keep asking the user for correct input until it validates the type that you are expecting. It even allows you to add custom validation or asking the user to confirm the input.

How old are you? None You must enter a valid Integer. ? Error You must enter a valid Integer. ? 5 5

But it doesnā€™t only ask for an arbitrary input, you can also ask the user to pick one option from a specific set of choices that you offer.

let choice = choose("Whats your favorite programming language? ", choices: "Swift", "Objective C", "Ruby", "Python", "Java :S")

  1. Swift
  2. Objective C
  3. Ruby
  4. Python
  5. Java :S Whats your favorite programming language?

And letā€™s not forget about the input that every script asks for the user. Yes or No?

let choice = agree("Are you sure you want to rm -rf / ?")
Are you sure you want to rm -rf / ? What! Please enter "yes" or "no". Are you sure you want to rm -rf / ? Wait Please enter "yes" or "no". Are you sure you want to rm -rf / ? No
You entered false

## Colors

I donā€™t tend to use colors in the output of my scripts as usually techy users will have a custom palette on the terminal and who knows how readable will it be. But for display errors in red I usually do it. Swiftline allows you to just call a couple of short methods in any string to give it a color.

print("Red String".f.Red)

JSON

As I already stated on my analysis of Swift JSON libraries I use Decodable in my projects. But usually in scripts I donā€™t need to decode JSON into my model, I just need an easy access to some keys of the JSON so I usually use SwiftyJSON for that.

let name = json["name"].stringValue

I like it because visually is just like accessing a dictionary with a simple method that determines the type that I'm expecting.

Foundation

Even with all the nice libraries that are out there I still end up using Foundation for quite a bunch of things. For example, Iā€™m sure that are libraries to read and write files but I found myself quite comfortable with just using NSFileManager or even the convenience methods that some types have like string.writeToURL.

Even for network calls NSURLSession is more than enough. Although to use it in a script there are some tweaks that are needed, mainly making it synchronous using a semaphore.

No ABI

The funny part about the history of this script was that I was writing it when the Swift team announced that the ABI was postponed after Swift 3.

Iā€™m happy with that delay, as I prefer to have a good and stable language (in this case the ABI) before rushing it to quickly just to make marketing happy.

But this simple script was one of those cases where I would have needed ABI compatibility. I wanted to deploy the script in a linux server, but Swift 2 doesnā€™t have linux compatibility so I had to port the script to Swift 3, except that without ABI compatibility means that I need to wait for all the libraries to be updated for the upcoming version of Swift. :(

Conclusions

After all the characters Iā€™ve written I still firmly believe that Swift will be a great language for scripting. I donā€™t have complains about the language itself or the libraries, as you have seen in this post. The main concerns are about the tools around them. But we donā€™t have to forget that is still a young language and I have high hopes in the evolution of it.

If you liked this article please consider supporting me