Testing in Swift

Posted on Sun 10 June 2018 in Swift • Tagged with swift

We want to be able to write tests in Swift, and to do this we will use the XCTest framework.

We'll start by writing a function to test, something we haven't actually done yet.

(All the code here is available from [Github](

// add-function.swift  [Swift 4.1, Xcode 9.3.1, 2018-06-10]
//
// Trivial demonstration of function syntax


func add(a: Int, b: Int) -> Int {
    return a + b
}

func saybye() {
    print("Bye!")
}


print("2 + 3 = \(add(a: 2, b: 3))")
print("2 + -2 = \(add(a: -2, b: 2))")
saybye()

There's not a huge amount to say about this, but in case it isn't clear:

  • The keyword func introduces a function definition.
  • Arguments for the function are declared as name:type.
  • The return type for the function is declared after the -> symbol.
  • If there is no value to be returned, the -> can be omitted, as in the second function, which also takes no arguments.
  • The function body is enclosed in braces.
  • We're using the \(expression) syntax to embed a calculated value in a string (with automatic type co-ercion).
  • When passing arguments to the function, we have to name them, but (despite this) they have to be passed in the declared order (you're not writing Python yet!)1

If we run this, the output is as expected:

$ ./add-function
2 + 3 = 5
2 + -2 = 0
Bye!

Naming the arguments when calling the function here seems to serve no useful purpose: there's nothing special about a and b, and requiring them to be named merely makes it harder to remember how to invoke the function. Swift allows us to declare the arguments with an _ prefix (then a space), and if we do so, the arguments do not need to be (and indeed, are not allowed to be) named when calling the function.

So this code is functionally equivalent:

// add-function-no-labels.swift
//
// Trivial demonstration of function syntax with anonymous arguments

func add(_ a: Int, _ b: Int) -> Int {
    return a + b
}

func saybye() {
    print("Bye!")
}

print("2 + 3 = \(add(2, 3))")
print("2 + -2 = \(add(-2, 2))")
saybye()

However, the point of this post wasn't really functions, but rather testing. And for that, we need to learn a little about packaging in Swift.

XCTest, as the name implies, comes from Xcode, and expects your Swift sources, tests, and outputs all to have a very specific structure. This carries over to working with Swift from the command line. By far the easiest way to proceed (outside Xcode) is to use the swift package command Apple supplies with the Xcode command-line tools. To do this, we first need to create a directory to create our package, then use the swift package command to initialize the structure of that package, like this:

$ mkdir adder
$ cd adder
$ swift package init --type library
Creating library package: adder
Creating Package.swift
Creating README.md
Creating .gitignore
Creating Sources/
Creating Sources/adder/adder.swift
Creating Tests/
Creating Tests/LinuxMain.swift
Creating Tests/adderTests/
Creating Tests/adderTests/adderTests.swift
Creating Tests/adderTests/XCTestManifests.swift

$ ls -R
Package.swift   README.md   Sources     Tests

./Sources:
adder

./Sources/adder:
adder.swift

./Tests:
LinuxMain.swift adderTests

./Tests/adderTests:
XCTestManifests.swift   adderTests.swift

Points to note:
  • The packager has created some helper code for Linux too.
  • It's also created a some trivial code to test and a single test for it.
  • We've specified that we're building a library here, but we could have said we want an executable instead.

We could run the auto-generated test for the auto-generated code its tests for its code by saying:

$ swift test

but, we'll replace the code and test before doing so.

For the code, we'll simply use the add function from above:

// adder.swift  [Swift 4.1, Xcode 9.3.1, 2018-06-10]
//
// Simple integer add function for testing

func add(a: Int, b: Int) -> Int {
    return a + b
}

using this to replace the "Hello, World"-ish that the packager generated for us.

import XCTest
@testable import adder

class adderTests: XCTestCase {
    func testAddSmallIntegers() {
        XCTAssertEqual(adder.add(a: 2, b:3), 5)
    }

    func testAddInverses() {
        XCTAssertEqual(adder.add(a: 2, b:-2), 0)
        XCTAssertEqual(adder.add(a: 10000000, b:-10000000), 0)
    }
}

Points to note:

  • We need the @testable before the import of our module to make it available to the test framework.2
  • We subclass XCTestCase class from XCTest to form a test class, (following the usual x-unit pattern)
  • We use the XCAssertEqual function, from XCTest for our assertions.
  • The generated test code made the test class final, which prevents it from being subclassed. I'm not sure what the benefit of that is, so I've omitted it, but it would do no harm
  • We need to prefix our add function with the namespace qualifier adder. when calling it in our tests. I am currently confused as to why this is the case, as everything I've read seems to suggest we shouldn't need to. We don't seem to need to prefix functions from Foundation with Foundation. when we call them, so either the way we've defined or imported the library here is suboptimal, or there's a name clash I'm unaware of, or there's something else I've misunderstood. We'll come back to this (and I'll update this post) when I've understood more about Swift packaging and namespaces.
  • The generated test code also include the lines:
static var allTests = [
    ("testExample", testExample),
]

It looks as if that is necessary only for Linux (see here), so again, I've omitted it here.

This is what happens if we run the tests:

$ swift test
Compile Swift Module 'adderTests' (2 sources)
Test Suite 'All tests' started at 2018-06-09 13:03:53.957
Test Suite 'adderPackageTests.xctest' started at 2018-06-09 13:03:53.957
Test Suite 'adderTests' started at 2018-06-09 13:03:53.957
Test Case '-[adderTests.adderTests testAddInverses]' started.
Test Case '-[adderTests.adderTests testAddInverses]' passed (0.110 seconds).
Test Case '-[adderTests.adderTests testAddSmallIntegers]' started.
Test Case '-[adderTests.adderTests testAddSmallIntegers]' passed (0.000 seconds).
Test Suite 'adderTests' passed at 2018-06-09 13:03:54.067.
     Executed 2 tests, with 0 failures (0 unexpected) in 0.110 (0.111) seconds
Test Suite 'adderPackageTests.xctest' passed at 2018-06-09 13:03:54.068.
     Executed 2 tests, with 0 failures (0 unexpected) in 0.110 (0.111) seconds
Test Suite 'All tests' passed at 2018-06-09 13:03:54.068.
     Executed 2 tests, with 0 failures (0 unexpected) in 0.110 (0.111) seconds

Happily, both tests pass.

Before concluding, we'll just show what happens if we have create a failing test, for example by changing the first test to expect 0 instead of 5 for the addition of 2 and 3.

$ swift test
Compile Swift Module 'adderTests' (2 sources)
Linking ./.build/x86_64-apple-macosx10.10/debug/adderPackageTests.xctest/Contents/MacOS/adderPackageTests
Test Suite 'All tests' started at 2018-06-09 13:13:56.386
Test Suite 'adderPackageTests.xctest' started at 2018-06-09 13:13:56.386
Test Suite 'adderTests' started at 2018-06-09 13:13:56.386
Test Case '-[adderTests.adderTests testAddInverses]' started.
Test Case '-[adderTests.adderTests testAddInverses]' passed (0.114 seconds).
Test Case '-[adderTests.adderTests testAddSmallIntegers]' started.
/Users/njr/lang/swift/cli-swift-examples/adder/Tests/adderTests/adderTests.swift:6: error: -[adderTests.adderTests testAddSmallIntegers] : XCTAssertEqual failed: ("5") is not equal to ("0") - 
Test Case '-[adderTests.adderTests testAddSmallIntegers]' failed (0.002 seconds).
Test Suite 'adderTests' failed at 2018-06-09 13:13:56.502.
     Executed 2 tests, with 1 failure (0 unexpected) in 0.116 (0.116) seconds
Test Suite 'adderPackageTests.xctest' failed at 2018-06-09 13:13:56.502.
     Executed 2 tests, with 1 failure (0 unexpected) in 0.116 (0.116) seconds
Test Suite 'All tests' failed at 2018-06-09 13:13:56.502.
     Executed 2 tests, with 1 failure (0 unexpected) in 0.116 (0.117) seconds
$

The output is a little but busy, but we can see that the crucial information about the failure is as follows (in which I've broken the single-line error message to enhance readability):

/Users/njr/lang/swift/cli-swift-examples/adder/Tests/adderTests/adderTests.swift:6:
error: -[adderTests.adderTests testAddSmallIntegers] : XCTAssertEqual failed:
("5") is not equal to ("0")

I'm slightly surprised at the way the failure is reporting the failure—making it look as though the values are strings—but the problem is clear.

That's probably enough for one post.


  1. Swift's approach to function and argument names is clearly strongly influence by Objective C, which was itself strongly indludenced by Smalltalk. In a previous post we saw an example of a method for percent-encoding a string, which we called as path.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlPathAllowed). This style of naming functions as verbs and arguments as parts of sentences explaining the qualification that the provide to the verb is idomatic, and went even further in Objective-C, where the "function" (actaually a message) would actually be called addPercentEncoding:withAllowedCharacters

  2. I'm not sure, at this point, what @testtable actually does, and have been a bit surprised at the paucity of information on it. I suspect it's not needed in Xcode, where most people develop Swift. But I can confirm that the tests will not run without this. 


Synchronous HTTP GET in Swift

Posted on Sat 09 June 2018 in Swift • Tagged with swift

In the last few posts, we've seen how to print, get arguments from the command line, and how to get lines of keyboard input and implement a REPL (read, evaluate, print loop).

The particular command-line tools I want to build are for interacting with MicroDB, an online distributed tag-based database that I'm developing.

In order to interact with MicroDB, we're going to need to be able to perform various HTTP operations---specifically GET, PUT, POST and DELETE. So I wanted to explore how to do those in Swift. The code below illustrates one of many ways to perform an HTTP GET in Swift, based on information from these four web resources.

// get-synchronous.swift  [Swift 4.1, Xcode 9.3.1, 2018-06-09]
//
// Method 1 for getting a URL in Swift

// Simple, Synchronous URL get.
//
// Based on https://www.hackingwithswift.com/example-code/strings/how-to-load-a-string-from-a-website-url
//   and https://developer.apple.com/documentation/foundation/nsstring/1411946-addingpercentencoding
//   and https://developer.apple.com/documentation/foundation/characterset
//   and https://stackoverflow.com/questions/32064754/how-to-use-stringbyaddingpercentencodingwithallowedcharacters-for-a-url-in-swi


import Foundation

let path = "njr.radcliffe0.com/album:solid air (john martyn)/Title"
let enc = path.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlPathAllowed)
let urlString = "https://" + enc!  // don't %-encode "https://"

if let url = URL(string: urlString) {
    do {
        let contents = try String(contentsOf: url)
        print(contents)
    } catch {
        print("*** Contents could not be loaded from" + urlString + ".")
        }
} else {
    print("*** Bad URL:" + urlString + ".")
}

Let's go through it:

MicroDB. In MicroDB, information is stored as an association between a user, a unique (meaningful) identifer, a tag name and a value. The user is identified by a domain. I have an instance of MicroDB running at http://njr.radcliffe0.com, and information about John Martyn's album Solid Air stored on the identifier album:solid air (john martyn). One of the bits of information I have about the album is its name, which is stored in a tag called Title. So if I perform an HTTP GET to https://njr.radcliffe0.com with the path album:solid air (john martyn)/solid air, I should get back the title of the album. If we try this with curl, indeed we see that (percent-encoding spaces as %20):

$ curl 'https://njr.radcliffe0.com/album:solid%20air%20(john%20martyn)/Title'
Solid Air

Percent Encoding. In Swift (at least with our chosen way of fetching a URL), we also have to percent encode the URL path ourselves. The method addPercentEncoding on strings can do this, but we need to specify which characters not to encode by passing an arugument to the method. The argument name is withAllowedCharacters and the various standard character sets are available in Foundation as documented on this page. urlPathAllowed is the list we need for a URL path.

Optionals. We then for the full URL by prepending the percent-encoded path with https:// using simple string concatenation (+ in Swift). But notice the exclamation mark ! on the end of enc. That's because addingPercentEncoding returns a Swift optional, i.e. a value that might be nil. The documentation specifically says this:

Returns the encoded string, or nil if the transformation is not possible.

(I'm not sure in exactly what circumstances the transformation wouldn't be possible, but presumably there are, or might at some point, be some.)

Unwrapping Optionals By following variable with !, we are telling Swift that even though its contents could be nil, in this case they are not. This is called "unwrapping an optional". In this case, we know the value will not be nil because we're giving it string can obviously be percent-encoded (all the characters are ASCII!), so it's fine to force it. If we did turn out to be wrong, however, this would cause the Swift executable to crash. Here's a simple interaction with the Swift REPL that illustrates that:

$ swift
Welcome to Apple Swift version 4.1 (swiftlang-902.0.48 clang-902.0.39.1). Type :help for assistance.
   1> let a:Int? = nil
a: Int? = nil
2> a!
Fatal error: Unexpectedly found nil while unwrapping an Optional value
2018-06-02 21:52:44.040840+0100 repl_swift[22405:1982301] Fatal error: Unexpectedly found nil while unwrapping an Optional value
Execution interrupted. Enter code to recover and continue.
Enter LLDB commands to investigate (type :help for assistance.)
Process 22405 stopped
* thread #1, queue = 'com.apple.main-thread', stop reason = EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
    frame #0: 0x00000001011d5c50 libswiftCore.dylib`function signature specialization <Arg[2] = Dead, Arg[3] = Dead> of Swift._fatalErrorMessage(Swift.StaticString, Swift.StaticString, file: Swift.StaticString, line: Swift.UInt, flags: Swift.UInt32) -> Swift.Never + 96
libswiftCore.dylib`function signature specialization <Arg[2] = Dead, Arg[3] = Dead> of Swift._fatalErrorMessage(Swift.StaticString, Swift.StaticString, file: Swift.StaticString, line: Swift.UInt, flags: Swift.UInt32) -> Swift.Never:
->  0x1011d5c50 <+96>: ud2
    0x1011d5c52 <+98>: nopw   %cs:(%rax,%rax)

libswiftCore.dylib`function signature specialization <Arg[0] = Owned To Guaranteed and Exploded, Arg[1] = Exploded> of Swift.String._compareASCII(Swift.String) -> Swift.Int:
    0x1011d5c60 <+0>:  pushq  %rbp
    0x1011d5c61 <+1>:  movq   %rsp, %rbp
Target 0: (repl_swift) stopped.
3> ^D

Here we made the variable a be an optional integer by specifying its type as Int? and assigned its value as nil. We then try to forcibly unwrap it using a!, and as we see a crash ensues.

The if...else statement. Like the while statement we saw previously, the if...else statment should look fairly familiar if you know any C-like language. Again, the condition does not require parentheses, and again this one is using a let---assignment of a constant. The if is protecting against the possibility the that URL object that we create with with URL(string:urlString is nil, which it will be if the urlString is not a valid URL.

The do...try...catch statement. Assuming the URL is valid, we try to fetch it, and we do this in a do...ctry...atch block, which is Swift's way of allowing us to handle exceptions.1 We'll get such an exception if the URL can't be fetched.

Performing the HTTP GET. In this case, we attempt a synchronous HTTP GET simply by initializing a string using contentsOf: url as an initializer. This causes Swift to (try to) get the URL contents. Notice the try which indicates that this is the statment inside the do...catch block that might throw an exception.

That's it!


  1. In this case, we're catching any exception that might be thrown, but we could also be more specific and catch particular kinds of exceptions; I imagine we'll see this in later posts. 


A Simple REPL (Read, Evaluate, Print Loop) in Swift

Posted on Sun 03 June 2018 in Swift • Tagged with swift

We looked in the last post at how to get arguments from the command line. We will also be interested in how to build a REPL---a Read, Evaluate, Print Loop that reads input from the keyboard and responds until some exit condition is met, and then exits. program is often called a REPL—a Read, Evaluate, Print Loop. This fundamentally requires us to be able to read lines from the keyboard and process them.

Here's some Swift that does that (based on information in this post).

// readline.swift  [Swift 4.1; Xcode 9.3.1; 2018-05-27]
//
// Implement a trivial REPL.
//

print("Type something: ", terminator: "")
while let line = readLine() {
    print("(Just to show I got it) you typed '\(line)'")
    print("Type something: ", terminator: "")
}
print("\nBye!")

Points to note:

  • The print statement includes the extra (named) argument terminator, which specifies what should be added to the string we give. By default, this is a newline (\n), but here we want Type something: to act as a prompt, so we don't want a trailing newline. By specifying the empty string terminator, we avoid this.
  • The while condition {statements} construct will be fairly familiar to anyone who's ever seen a C-style language, but the condition may look slightly unfamiliar.
    • First, no parantheses are require around the condition, as they would be in C and friends.
    • readLine() is a function call that returns what Swift calls an optional String, which is to say, it either returns a string (whatever the user types) or a null value (nil), if the user terminates the input with the end-of-file character (^D on Unix and Linux; ^Z on Windows, though I don't know how relevant that is in a Swift context).
    • let assigns a name (in this case line) to a constant value, i.e. one that cannot be changed after assignment.
    • So the construction while let line = readLine() is a condition that will be satisfied whenever the value is non-null even if the input is empty.
    • As a strongly, statically-typed language, Swift usually requires nulls to be handled in one of a few well-defined ways, including protecting against them as shown. We'll see other ways in the future.
  • The only notable thing in the body of the while loop is the use of '\(line)' in the string to be printed. Strings can include arbitrary expressions by using \(...) syntax. In this case, we are just grabbing the value of the variable. The result of such expressions (perhaps surprisingly) does not have to be String: whatever is produced will be cast to a string.
  • In total, then, this program simply repeatedly asks the user for input, and echoes it back, until input is terminated, either by breaking out (e.g. with ^C), or by ending input with the ^D end-of-file character.

Here's what happens if we compile it and interact with it:

$ ./readline
Type something: Hello
(Just to show I got it) you typed 'Hello'
Type something:
(Just to show I got it) you typed ''
Type something: (That was just a carriage return)
(Just to show I got it) you typed '(That was just a carriage return)'
Type something: Next up will be end-of-file
(Just to show I got it) you typed 'Next up will be end-of-file'
Type something: ^D
Bye!
$

Command-Line Arguments (argv and argc) in Swift

Posted on Sun 03 June 2018 in Swift • Tagged with swift

There are two main styles of command-line tool. One of them takes its inputs on the command line, does something, and exits. The other starts, reads input from the keyboard and responds until some exit condition is met, and then exits. This second style of program is often called a REPL—a Read, Evaluate, Print Loop.

We're going to start with the first of these, and the fundamental requirement for writing such a tool is getting access to those command-line argument (argv, and perhaps argc, in C-terms).

Here's a trivial Swift program that just shows what the arguments were, and counts them, based in information in this post

// argv.swift
//
// Collect, echo and count command-line arguments
// [Swift 4.1; Xcode 9.3.1; 2018-06-03]
//
// https://stackoverflow.com/questions/24029633/how-do-you-access-command-line-arguments-in-swift
//


print(CommandLine.arguments)
print(CommandLine.arguments.count)

Here's what happens in a few cases after compilation:

$ ./argv
["./argv"]
1
$ ./argv one
["./argv", "one"]
2
$ ./argv one 2
["./argv", "one", "2"]
3
$ ./argv one && echo done
["./argv", "one"]
2
done

About the points to note are:

  • CommandLine.arguments either is or acts like an array of Strings. (the note on ARGC in this article suggests it might be array-like, but it doesn't make any different at least for simple uses.)
  • We can use the .count property to access the number of arguments
  • As with argv in C, the command itself is the first element in the array
  • the last example illustrates that, as expected, the shell only passes the intended arguments to the command

Hello, World in Swift

Posted on Sun 03 June 2018 in Swift • Tagged with swift

Of course, we start with printing. Here is "Hello, World!" in Swift. It couldn't be much simpler.

// hello.swift  [Swift 4.1; Xcode 9.3.1; 2018-05-27]
//
// Just say "Hello".
//

print("Hello, World!")

The //, of course, introduce comments, print is the print function, and "Hello, World!" is the string to be printed. We don't need a semicolon (or anything else) to terminate the line.

We can run this various ways, but the main one is to compile it and then run it. Assuming you have Xcode installed (you might also need Xcode's command-line tools; I'm not sure), you can compile the code with

$ swiftc hello.swift

(assuming you've stored this in hello.swift) which produces the executable file swift. And running that:

$ ./hello
Hello, World!

as expected, prints Hello, World! to stdout.


Command-Line Interfaces in Swift

Posted on Sun 03 June 2018 in Swift • Tagged with swift

This is a new blog about my journey learning Swift by writing command-line tools (in fact, probably just one or two command line tools). I'm writing it mostly as reference for myself, but also in the hope that it might help someone else.

It is Sunday 3rd June 2018 and I have written command-line tools in many languages for over 30 years. I have played with Objective-C, writing a few unreleased iOS apps, and a few months ago I did a little iOS development in Swift, but not much.

This time, I want to start without all the GUI stuff and just concentrate on the fundamentals of Swift the language, rather than iOS and Mac OS GUI development. I might come to that later.

At the time of writing (one day before WWDC 2018), Swift is at 4.1. My plan is to translate a couple of command-line apps I already have in Python (my main daily language), and in this blog, I plan to show little experiments that I need in order to know how to perform the translation. In doing so, we'll probably develop a small generic command-line interface, but no promises.

I won't be using Xcode (the IDE), per-se, but rather editing files, as usual, in Emacs and just dumping the code into the blog. I'll just be compiling with swiftc.

I make no promises that I'll come back and update the code as necessary if and when changes to the language and or the libraries necessitate it, but I will clearly mark what version of Swift and Xcode are used used on each post, and try hard to update that if I update the code.

The posts certainly aren't intended as an introduction to programming, but they might serve as a useful conversion series for anyone coming from another (probably imperative) language.

Below is a non-exhaustive list of basic tasks I'll probably try to cover over the coming weeks and months. Notice that most of these are capabilities, rather than language features, though we'll obviously eun across language features along the way.

  • Compiling Swift
  • "Hello, World!" (printing)
  • Getting arguments from the command line
  • Reading from lines the keyboard and building a REPL
  • Reading and writing files (text, binary)
  • HTTP GET, PUT, POST, DELETE (and probably not PATCH)
  • Writing tests
  • Manipulating strings (walking strings, concatenation, stripping, substrings etc.)
  • Regular expression matching
  • Text encoding (and representation)
  • Exception handling
  • Option parsing (a special case of handling command-line arguments)

Code

The code featured in the blog will be available on Github at github.com/njr0/cli-swift-examples

Feedback

I'll undoubtedly get stuff wrong, and (maybe) this blog will occasionally be read by more experienced Swift developers (hi @clatter_llvm). Do ping me on Micro.Blog (@njr) or Twitter (@njr0) when I get stuff wrong, am unidomatic or there's a better way. Or file an issue or pull request on Github.