The Swift Package manager

The Swift Package Manager, officially released alongside Swift 3.0, is a new way to create Swift libraries and apps on macOS and Linux. It helps you manage your dependencies and allows you to easily build, test and run your Swift code.

It’s important to note that as Swift 3 the Swift Package Manager only compiles for host platforms. In other words, for now you won’t be able to build or consume packages for for iOS, watchOS, or tvOS.

Let’s Go!!

Before starting, make sure you have Swift 3.0 or greater installed. Swift 3 is included with Xcode 8.0+, so if you have Xcode 8 or better, you are ready to start. You actually don’t even need Xcode to complete most of this tutorial; you can simply install Swift 3 from swift.org.

Open a new Terminal window and type swift package. You’ll see an overview of the commands. The main commands you will be using are:

  1. swift package init to create new packages
  2. swift package update to update the dependencies of a package
  3. swift package generate-xcodeproj to generate an Xcode project for your package

To learn about the Swift Package Manager, you will create a command-line app that uses a small library to print the emoji flag for any country. You will start by creating the executable package. These are packages that are meant to be command-line apps. Swift web apps also fall into this category.

Create a Flag executable package by running the following commands in the terminal:

mkdir Flag
cd Flag
swift package init --type executable

The current directory Flag is important when you run swift package init because it becomes the name of the generated package. You’ll see a few files and folders in the output that were created for you. Take some time to get familiar with the project structure:

  1. Package.swift has your package description, and it will also have your package’s dependencies.
  2. Sources/, as the name suggests, is where you will have all your Swift source files. A main.swift file has also been created for you. This will be the entry point for your application. For now, it prints hello, world to the Terminal.
  3. Tests/ will contain unit tests you can write using XCTest. You will write tests for your code soon!

Go back to your Terminal window and run the following:

swift build

This will build the package and create an executable at .build/debug/Flag. Execute the app by running:

.build/debug/Flag

You should see Hello, world! printed to the screen.

Congratulations: you’ve created and built your first Swift package!

Creating the Library

To do the actual work of generating an emoji flag for a country, you will create a library named FalcoFlags. You can then use this library in your Flag application.

Move outside of the Flag package and create a library package by typing the following commands into the terminal:

cd ..
mkdir FalcoFlags
cd FalcoFlags
swift package init --type library

This time instead of a main.swift, you’ll get an FalcoFlags.swift. This file, and any other file in the Sources/ folder will be imported with your library. In fact, the difference between a library and an executable is the existence of a main.swift.

You also get one example test this time. Run the tests with swift test. The Swift Package Manager will then compile your library and run your tests.

Open FalcoFlags.swift in your text editor and replace its contents with the following:

public struct Country {
public let code: String
public init(code: String) {
self.code = code.uppercased()
}
public var emojiFlag: String {
return "\u{1f1f5}\u{1f1f7}"
}
}

Here you implement a Country struct that can be initialized with an ISO country code. The emojiFlag property returns the flag for that code. For now, you’ll implement the minimum to allow you to write tests.

Also note that everything here is marked public, so that each member is visible to code that consumes the FalcoFlags module. :D

Now open FalcoFlagsTest.swift and replace the contents with the following:

Here you implement three tests. You create three different countries and then assert that they have the correct emoji flag.

Run your tests:

swift test

You should see that three tests executed, and three tests failed. It appears you still have some work to do! :]

Now that you have failing tests, it’s time to make them pass.

The way emoji flags work is actually pretty simple: Given a country code like AT, you need to convert each letter into a so-called regional indicator symbol. Those are, for example, 🇦 and 🇹. When you put those together, you get the emoji flag!

Switch over to FalcoFlags.swift and add the following method to the Country struct:

func regionalIndicatorSymbol(unicodeScalar: UnicodeScalar) -> UnicodeScalar? {
let uppercaseA = UnicodeScalar("A")!
let regionalIndicatorSymbolA = UnicodeScalar("\u{1f1e6}")!
let distance = unicodeScalar.value - uppercaseA.value
return UnicodeScalar(regionalIndicatorSymbolA.value + distance)
}

Here you take advantage of the fact that letters and regional indicator symbols are right next to each other in the Unicode table for values that are logically next to each other. So if A is 65, B is just 66 and if 🇦 is 127462, then 🇧 is just 127463. So to convert the letter P to a regional indicator symbol, you just need to get the distance between A and P, then add that distance to 🇵.

That was the hard part. Now that you have this method, the rest is easy! Replace the emojiFlag property with the following:

public var emojiFlag: String {
return code.unicodeScalars.map { String(regionalIndicatorSymbol(unicodeScalar: $0)!) } .joined()
}

You convert the country code to an array of each letter, then you convert each letter to its regional indicator symbol and join them back together. This gets you the flag!

Run the tests again and watch all three tests pass.

The next step of creating a Swift package is committing your code to Git and tagging it with a version. Since this is your first version, you will call it 1.0.0.

Execute the following commands to create your Git repo and tag it:

git init
git add .
git commit -m "Initial commit"
git tag 1.0.0

Creating the Executable

Now that you have your emoji flag library, you can add it as a dependency to the Flag executable package.

Navigate back to the Flag directory and open the Package.swift file. Its contents look like this:

import PackageDescription
let package = Package(
name: "Flag"
)

Every Swift package has a description like this. The most important of the parameters you’ll use will be the dependencies parameter. Replace the package description with the following:

let package = Package(
name: "Flag",
dependencies: [
.Package(url: "../FalcoFlags", "1.0.0")
]
)

Above, you state that the Flag package will have a single dependency with a URL of ../FalcoFlags and that the version should be 1.0.0.

The version should use semantic versioning. In a nutshell, this means that the version should look like MAJOR.MINOR.PATCH. The MAJOR version is for any backwards-incompatible changes; the MINOR version is for changes that are done in a backwards-compatible way; and the PATCH version is for bug fixes.

In almost all cases, you’ll want to automatically update to newer versions of the library with bug fixes or even minor improvements. Conveniently, the Swift package manager allows you to do this. Change your package description to the following:

let package = Package(
name: "Flag",
dependencies: [
.Package(url: "../FalcoFlags", majorVersion: 1)
]
)

The Swift Package Manger provides even more ways to precisely specify the exact version or versions of the library you want to update to.

Build the package:

swift build

You can see that the Swift Package Manager has carefully chosen version 1.0.0 based on your dependency requirements and installed it. Open the main.swift file and replace the contents with the following code:

import FalcoFlags
let arguments = CommandLine.arguments
if arguments.count != 2 {
print("USAGE: flag [iso country code]")
} else {
let code = arguments[1]
let country = Atlas.Country(code: code)
print(country.emojiFlag)
}

Here you import your library, then print the emoji flag for the first command line argument given. If no argument is given, you print a help message.

Build the app again and run it:

swift build
./.build/debug/Flag US

You should now see the United States flag in your Terminal window!

Once you are happy with your app it’s time to ship it. Build the app one last time, this time using the optimized release configuration:

swift build --configuration release

Now you can run the release version of your app like so:

./.build/release/Flag PR

You can now zip the ./.build/release/Flag file and share it with your, friends, family, or anyone, really! :]