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:
swift package init
to create new packagesswift package update
to update the dependencies of a packageswift 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:
Package.swift
has your package description, and it will also have your package’s dependencies.Sources/
, as the name suggests, is where you will have all your Swift source files. Amain.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.Tests/
will contain unit tests you can write usingXCTest
. 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: Stringpublic 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 PackageDescriptionlet 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 FalcoFlagslet arguments = CommandLine.argumentsif 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! :]