Preparing Xcode for the Advent Of Code 2020 in Swift

Preparing Xcode for the Advent Of Code 2020 in Swift

ยท

7 min read

This year's Advent of Code is around the corner and I was thinking of ways to organize my work environment for the different daily challenges.
This is just my take on this. I wanted to take advantage of the fantastic resources made recently available by Apple like the Swift Package Manager and the Argument Parser.
Of course SwiftUI could be a choice too ๐Ÿค”๐Ÿ™ƒ, but since the challenge are based on speed rather than UI, using executable scripts seems a better choice.

The Goal

My goal is to use Xcode to develop and also to be able to quickly use the terminal to launch my challenges for each day like this:

aoc5.png Last year I did the challenges in the Xcode Swift playgrounds. (till day 15: Advent-of-Code-2019)

I did encounter some problems though.
Playgrounds are much slower in executing the code on their main page, because they are doing quite a few checks in the background and while it is fun to use them for try out some code, they do not offer the true Swift speed.
This year I wanna try something different with the Swift package manager. This allows me to have an executable package and to pack all my files in it.
Also I could eventually make a library with the most used functions and import it separately.
Sounds like something new and exciting for me, so I can learn more about packages as well!

Creating an executable package

The way to do it is to create a new directory, call it like AdventOfCode2020

mkdir AdventOfCode2020

and cd to it:

// your path will look different though
cd /Volumes/iOS/AdventOfCode2020

Next you need to init the package:

swift package init --type executable

This will create the necessary files, your folder structure will look like this:

โžœ tree
.
โ”œโ”€โ”€ Package.swift
โ”œโ”€โ”€ README.md
โ”œโ”€โ”€ Sources
โ”‚   โ””โ”€โ”€ AdventOfCode2020
โ”‚       โ””โ”€โ”€ main.swift
โ””โ”€โ”€ Tests
    โ”œโ”€โ”€ AdventOfCode2020Tests
    โ”‚   โ”œโ”€โ”€ AdventOfCode2020Tests.swift
    โ”‚   โ””โ”€โ”€ XCTestManifests.swift
    โ””โ”€โ”€ LinuxMain.swift

4 directories, 6 files

Now build:

swift build

And run!

swift run AdventOfCode2020
// Hello, world!

This is the output of our executable because in our package we have a main.swift file. This file is special, it is the only one file that is called automatically by the system and executed first. Also it allows to put commands on the top level! this are the contents of the main.swift file, a famous one liner: print("Hello, world!")

When I do run AdventOfCode2020 I execute the main swift file in the AdventOfCode2020 folder (under sources).

And I can do testing as well with swift tests

Creating folders for each day

It will be nice to just be free to execute the code for any day passing the arguments so I will create a Day1 folder containing another main.swift file to be executed like:

swift run Day1

My tree will look like this now:

โžœ tree
.
โ”œโ”€โ”€ LICENSE
โ”œโ”€โ”€ Package.swift
โ”œโ”€โ”€ README.md
โ”œโ”€โ”€ Sources
โ”‚   โ”œโ”€โ”€ AdventOfCode2020
โ”‚   โ”‚   โ””โ”€โ”€ main.swift
โ”‚   โ””โ”€โ”€ Day1
โ”‚       โ””โ”€โ”€ main.swift
โ””โ”€โ”€ Tests
    โ”œโ”€โ”€ AdventOfCode2020Tests
    โ”‚   โ”œโ”€โ”€ AdventOfCode2020Tests.swift
    โ”‚   โ””โ”€โ”€ XCTestManifests.swift
    โ”œโ”€โ”€ Day1Tests
    โ”‚   โ”œโ”€โ”€ Day1Tests.swift
    โ”‚   โ””โ”€โ”€ XCTestManifests.swift
    โ””โ”€โ”€ LinuxMain.swift

6 directories, 10 files

And to allow this I will need to modify the Package manifest file:

// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "AdventOfCode2020",
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        // .package(url: /* package url */, from: "1.0.0"),
    ],
    targets: [
        // Targets are the basic building blocks of a package. A target can define a module or a test suite.
        // Targets can depend on other targets in this package, and on products in packages this package depends on.
        .target(
            name: "AdventOfCode2020",
            dependencies: []),
        .testTarget(
            name: "AdventOfCode2020Tests",
            dependencies: ["AdventOfCode2020"]),
    .target(
        name: "Day1",
        dependencies: []),
    .testTarget(
        name: "Day1Tests",
        dependencies: ["AdventOfCode2020"]),
    ]
)

[...]

If I click on the package file, it opens in Xcode and there I can just easily select which target I want to run, as you see in the top left corner.

aoc1.png

My script can receive arguments as input.

Parse Input Arguments

import ArgumentParser

// Define our parser.
struct Day1: ParsableCommand {
  // Declare expected launch argument(s).
  @Option(help: "Specify an Integer.")
  var input: Int

  func run() throws {
    print("Running Day1 Challenge with input \(input)")
  }
}

// Run the parser.
Hello.main()

I need to import the Argument Parser package and add it as a dependency as per Apple documentation:

let package = Package(
    name: "AdventOfCode2020",
    dependencies: [
        .package(url: "https://github.com/apple/swift-argument-parser.git", from: "0.3.0"),
    ],
    targets: [
        .target(
            name: "AdventOfCode2020",
            dependencies: [.product(name: "ArgumentParser", package: "swift-argument-parser")]),

[...]

Now I run the target I get correctly a prompt, saying that my usage is not correct and I am expecting an Int!

aoc2.png

I will add this clicking on my target and add it in arguments

aoc3.png

This is effectively the same as writing in Terminal:

swift run Day1 --input 42

aoc4.png

So the environment is now set up ๐Ÿ˜€

Sources

Advent Of Code 2020: https://adventofcode.com
Swift Package Manager: https://docs.swift.org/package-manager/
Five stars blog : fivestars.blog
Argument Parser: https://github.com/apple/swift-argument-parser/