Create your own command line tools in Swift
Tags: swift, command, tools, ios
Published: 2020-12-15 17:25:31

---
title: Create your own command line tools in Swift
published: true
description: Getting started with Swift for command line and argument parser. 
tags: Swift, Command, Tools, iOS
//cover_image: https://direct_url_to_image.jpg
---

Swift is certainly a great programming language for developing apps made for the Apple ecosystem. But as you know, Swift, as a language, is not Apple specific. You can use Swift in many other areas such as backend (with Vapor). Swift runs on Linux and Windows too.
Today I would like to present a quick "Getting started" post about how to make your own command line tools with Swift.

As a developer there is many tasks that I'm sure you have already made your own tools for, to automate them.
Cropping images, scrapping stuff from the internet, generating all kind of files or config stuff... You name it.
Today I'll try to demonstrate how to create your own tool using Swift, the Swift package manager, and the argument parser.

## Building a command line tool using the Swift Package Manager

I find very useful to have an image comparison tool on my computer. I have a lot of UITests on my apps and while the tests are running, they automatically take screenshots of the screen. There are many use-cases such as upload them to iTunes Connect, but I also use it to compare screens over time and over versions of my apps. If I see any unexpected difference between my "Base" screenshots and the one taken while testing, I know something is wrong with my UI.

### So today we'll be building an image comparison command line tool

Let's start by creating the project :
```
$ mkdir img_cmp
$ cd img_cmp
$ swift package init --type executable
```
Nothing crazy here; I create an `img_cmp` directory for my project and initilize the project.
A folder structure hase been created for me :
```
Creating executable package: img_cmp
Creating Package.swift
Creating README.md
Creating .gitignore
Creating Sources/
Creating Sources/img_cmp/main.swift
Creating Tests/
Creating Tests/LinuxMain.swift
Creating Tests/img_cmpTests/
Creating Tests/img_cmpTests/img_cmpTests.swift
Creating Tests/img_cmpTests/XCTestManifests.swift
```
Awesome, now you can build and run the project with command line :
```
swift build
swift run
```

If you're on a macOS machine and have XCode installed, you can double click on Packaged.swift, it will open the project.
Otherwise you can simply open Sources/img_cmp/main.swift in you favorite text editor.

Now we are going to import the Argument Parser. This is a Swift package made by Apple to get simple user input via arguments while running the program.

In your Package.swift file, you will add the dependancy as follow :
```
let package = Package(
    name: "img_cmp",
    dependencies: [
        .package(url: "https://github.com/apple/swift-argument-parser", from: "0.3.0"),
    ],
    targets: [
        .target(
            name: "img_cmp",
            dependencies: [
                .product(name: "ArgumentParser", package: "swift-argument-parser"),
            ]),
        .testTarget(
            name: "img_cmpTests",
            dependencies: ["img_cmp"]),
    ]
)
```

Now you have added the Argument Parser framework. If you are on XCode, it will automatically update the dependancy package and resolve the dependancy versions.

In the main.swift, we can now import the framework :
```
import ArgumentParser
```

### Creating the program

We need to start by creating a parsable command. Nothing simpler: make a struct that conforms to `ParsableCommand`, a protocol defined in the Argument Parser framework.
Here I'll call it `ImgCmp`.
After that, we'll call the static `main` function, that will take care of running the program for me.

```
struct ImgCmp: ParsableCommand {

}
ImgCmp.main()
```

Great, now to make our program do something, I will implement the function `run` in our struct (also defined in the protocol `ParsableCommand`). This will be the method used to execute the command.

```
struct ImgCmp: ParsableCommand {
    func run() throws { }
}
ImgCmp.main()
```

Notice this command can throw error, so the OS can catch the exit status of your program.

### Get all the arguments !

Alright now we want the user to input two images to compare one to another. So we'll need (at least) 2 arguments.
The ArgumentParser let use input 3 type of arguments :

- Arguments
That's a required input needed for the program to run.

- Options
An option is an extra value that the user can input for a specific behaviour.

- Flag
A Flag is a simple option that the user can add or not.

Example :
```
nano -L /path/to/file --tabsize=1
```
Here we use the command `nano` which is used to edit a file.
`-L` is a flag (Don't add newlines to the ends of files).
`/path/to/file` is an argument (The file to edit).
`--tabsize=1` is an option (Set the size (width) of a tab to cols columns).

To parse argument, the framework makes everything for us.
It's based on property wrappers - that you might have used with SwiftUI and it's pretty simple :
Every argument is an attribute of the struct (here `ImgCmp`) and the propery wrapper takes parameters to define and specify the argument.
Let's start with our two main arguments :
```
@Argument(help: "The reference image.")
var base: String
    
@Argument(help: "The image to compare.")
var image: String
```

Now if you run the command without two arguments it should say:
```
$ img_cmp swift run img_cmp
Error: Missing expected argument '<base>'

USAGE: img-cmp <base> <image>

ARGUMENTS:
  <base>                  The reference image. 
  <image>                 The image to compare. 

OPTIONS:
  -h, --help              Show help information.
```
But add some strings and you're good :
```
$ img_cmp swift run img_cmp /Users/me/Desktop/a.png /Users/me/Desktop/b.png
```
Now for me, this is not enough. I want the user to be able to specify a tolerance for comparing. Say if A is 99% B, it's okay for me.
For that we'll add an option
```
@Option(name: [.customLong("tolerance"), .customShort("t")], help: "The tolerance to consider an image identical as a value from 0 to 1.\n 0 is strictly identical.")
    var tolerence: Float?
```
With the Option property wrapper, you can specify a long and a short name (here "tolerance" and "t").
Now we can use execute :
```
$ img_cmp swift run img_cmp /Users/me/Desktop/a.png /Users/me/Desktop/b.png -t 0.01
```
Let's also add a verbose flag. If the user would like to display more information and non blocking messages. A basic "-v" will work for me.
```
@Flag(name: [.customLong("verbose"), .customShort("v")], help: "Show logs, information and non blocking messages.")
var verbose = false
```

Back the to run function : to exit a program, just throw an ExitCode (.success, .failure...). Just add an exit statement to the function : `throw ExitCode.success`

###There you go!
Now I wont (unless you ask for it in the comment) write about the program's body because it's kind of out of subject (and in real life, you would probably use imagemagick ^^), but here how your code should look like so far:

```
import ArgumentParser

struct ImgCmp: ParsableCommand {
    
    @Argument(help: "The reference image.")
    var base: String
    
    @Argument(help: "The image to compare.")
    var image: String
    
    @Flag(name: [.customLong("verbose"), .customShort("v")], help: "Show logs, information and non blocking messages.")
    var verbose = false
    
    @Option(name: [.customLong("tolerance"), .customShort("t")], help: "The tolerance to consider an image identical, as a value from 0 to 1.\n 0 is stricly identical.")
    var tolerance: Float?

    func run() throws {
        // here you would compare the images, log stuff and return the right status code.
        throw ExitCode.success
    }
}

ImgCmp.main()
```

I hope you enjoyed this simple post. Don't hestitate to ask any question in the comment section, I'll be glad to help :).

Happy coding!