Now that Swift is available on many platforms, how can developers use testing frameworks outside of Xcode to their advantage? In this CMD+U Conference talk, Kyle Fuller discusses a variety of possible approaches.
My name is Kyle. I’m here to talk about testing without Xcode. You might think this is a bit strange. Why am I talking about this at a conference named CMD+U? While in Xcode you are certainly going to use command-U to do your tests, I’m sure most of you may be in situations where you want to run the test in small components, in CI, or even in Xcode Playground.
I will cover how testing frameworks work and how we can use them to test outside of Xcode since Swift is now available on other platforms. You might want to target them too. And therefore, it’s important to test on these platforms. Why? There are a few reasons you might be interested in testing outside of Xcode. And even if you’re not interested in them, I’m sure we can learn something about this topic because I will cover how Xcode test works internally and subjects like that.
Swift: Multiple Possibilities (0:56)
Since Swift is no longer tied to Apple platforms, we can actually use it to develop applications for other platforms. Swift can be used to develop UI applications, command line tools, web services, and libraries which work on a variety of platforms now.
Last week, someone even made a request to add support for the PlayStation 4 to Swift itself. This means you could potentially build video games for a PlayStation in Swift in the future. Android support is also available already. And people have been working towards actually being able to build Android apps in Swift. As a Swift developer, you have more possibilities than ever.
We can use the language we’re already familiar with to build all sorts of applications on a variety of platforms. And this also opens up the language for use by different types of developers. Not necessarily iOS or MacOS developers. We’re seeing a lot of people jumping over to Swift now, from web services and other kind of things.
For example, IBM has started building web frameworks and services in Swift already. And there’s various web frameworks evolving which actually have a substantial amount of backing and funding behind them. So there’s huge money going into this.
And if you’re building an open source library, depending on what it is, it might not be limited to app platform. If I were building some kind of text manipulation library or a testing framework itself, these are probably not specific to Apple platforms. They are not hard-coded to work on iOS. It is possible for other people to start utilizing your open source libraries in other environments now. So it’s probably a good idea to be able to test in them to make sure it actually works as intended on other platforms.
I’ve personally written dozens of Swift libraries before it was open source. And now, the majority of them have already been ported to these other platforms and being used. There’s huge demand for some of them.
For example, a library called Stencil, which is a template language. I initially built this for iOS in a MacOS application I built. But it’s actually very powerful to use on a web service and it’s probably more useful and that target audience is probably more likely to be web developers. You might be using Xcode for MacOS and iOS but I’ve also seen people build iOS applications from others they’re kind of more familiar with and prefer. It’s great to have full control over your editor in IDE.
Perhaps you don’t want to use Xcode and for a long time you might have been stuck with it. These days I personally don’t really use Xcode while developing certain applications because I’m not really targeting iOS or MacOS. More like command line tools and other things. So I use a different editor.
Who am I? (3:43)
Before we dive in any further, let me introduce myself. My name is Kyle and I currently work at a company called Apre that provides an AK design platform. I don’t really do much iOS day to day and outside of that I’ve really heavily contributed to the Swift scene. And also to the Objective-C scene.
I’m mostly known for all the projects I’ve contributed to or developed. According to CocoaPods I touched nearly half a million applications that use CocoaPods. That’s with over 23 million downloads. So as I mentioned before, a lot of my libraries are starting to be used in Linux when people are building web applications.
So, I’m going to demonstrate how we can start testing these outside of Xcode. In this talk, I’ll cover some of the patterns and techniques that other communities have started using to test their own languages platforms. It also covers some testing and how it’s currently done in Swift. And then I’ll finally move on to how we can do testing better in Swift.
Ruby and Aspect Tests (4:53)
Before we get started with Swift, let’s go over how other communities and languages have solved testing. Let’s take a look at Ruby, C, and C++.
Since Ruby is a dynamic language, there are some great testing libraries. Ruby’s Aspect is a very popular library and it’s popularized some design patterns for testing. Many other libraries these days are actually based off Aspect or similar libraries like this. Aspect is a behavior driven develop and testing framework, so your Aspect has to look very readable and as you can see on this slide. You can read this and understand what’s going on in this test without even knowing Ruby. One of the biggest differences between something Aspect and what some of the existing options we have in Objective-C and Swift are such as like Quick, Spectre, QE, XCTest. They all use XCTest. And to run your Aspect tests, you simply just call the Aspect command line tool and it runs your Aspect tests.
Testing in C/C++ (5:55)
And in contrast, in C, C++, there are some very simple solutions people use such as just asserting, so your process will just exit on a failure. So here we just create in the command line tool and inside our entry point to our command tool, this is the main function, we create in a user called kyle. And then we can just use assert to do some real basic testing like use the dot name because it’s Kyle.
Get more development news like this
And if it isn’t equal, the process will just exit. So it’s primitive, but it’s very simple to do because there’s no dependencies. And dependency management isn’t quite as easy in C, C++ as it is elsewhere. And the simplicity here is that I just create a command line tool. So I just build my test with Clang or DCC, the compiler. And then I just execute it as a command line tool. We can go a bit further into fabricating frameworks. For example, there’s a very popular framework called Catch for C++. And it looks like this. So, at the top there is hash define. Basically, it’s saying catch config main. So it says it’s forming catch because we’re including catch afterwards.
Then each should define the main entry point for application. Then after that, you use the tester case macro and a bunch of macros to do a test. And again, we’re just building a command line tool. And in most of the other communities I’ve shown, testing framework and runners are usually bundled together.
In our own community, most testing frameworks are built on top of XCTests. So you’re using XCTest’s design and runner which means you’re stuck with some of XCTest’s design decisions.
Testing in Swift (7:32)
So now we’ve seen how testing is done in other languages. Let’s talk about testing our Swift code. We have a variety of options we can use. We could do something simple like assert, which is built into the standard library. We can pull in XCTest or we can use some of the frameworks that sit on top of XCTest. Now there are also some new frameworks coming out I’ve made myself which allow you to test Swift without XCTest.
So it’s building command line tools similar to what we’ve seen in the previous examples of Ruby and C++. So assert is a command that allows you to assert something as true or it can just crash your app. And we can use assert to do some basic testing and crash and failure. So assert is just a simple function. You can parse in a statement. So person dot name equals equals Kyle. If that is false, if the name is not Kyle, the app will just crash. It won’t continue.
We can also provide comments. Like here I said person’s name should be Kyle so when you test or get this crash you can actually figure out why. So coming back to C++ example, we can actually do the same technique to do some really simple test and here I created simple structure for a person which has, it confirms to custom string convertible. Has description method. And I’m just set in the description method what is intended. And we can utilize the Swift compiler to build our simple Swift script and just run it directly. Just
Testing in a Playground (9:01)
And we can also use assert in a playground. I’m going to give you a quick demo using it. I know I said we weren’t testing outside Xcode, but we’re going to get started in a playground for simplicity.
So, I want to build a simple command line tool that’s basically a ledger, so it’s like the counting ledger. It stores our various account information. I want to write some code that takes a ledger - basically an array, or text because I want to use text files to store my ledger, but for now I’m just going to represent it to you in an array of lines.
The initial deposit is, for example, 10 euros. Perhaps I want to add a new entry such as the electricity bill. Let’s say that is -5 dollars. And we can have a paycheck so we get paid, which is +10. So let’s think about how we’re going to build this tool.
I want to have a class or structure to represent each individual ledger entry. So let’s go ahead and create that. And it’s going to have two properties, so we’re going to have a name which is going to be the title of the ledger entry, and then we’ll have a value. Which is going to the double, so it might be a negative or positive value.
And it’s currently complaining because we haven’t set initial values so I’m just going to set them to nil. So we can go about using assert to basically test this pretty simply. So we could for example say that paycheck equals entry name paycheck and value equals ten. But actually, I don’t want to give it individual priorities. I actually want to have to have an optional initialize which takes the string, the complete string, and parses it and then either it returns nil if it doesn’t have a value, it doesn’t have correct parameters, or returns the actual entry.
So instead I’ll need an entry, which has the complete string, so paycheck plus ten. So let’s go ahead and write in the initializers. Optional initializer takes entry which is a string. So that parses, or it doesn’t parse, but it compiles and doesn’t correct. So let’s use assert to do a quick assertion. So assert. We want to assert that the paycheck dot name is equal to paycheck. And now we run it. It’s an optional so we need to do that. So it says assertion failure because we haven’t actually implemented the initializer, so it’s going to be nil.
And it’s going to, especially unwrapped, this can crash. So let’s do the simplest thing to make this test fast. I’m going to just remove this and that. We’ll just set the name to be paycheck. And the value to be ten so this will compile and work. And it parses.
So now we’ve seen a very quick way we can use assertion checking. I’m going to move on with the slides and we’ll come back to this demo a little bit later to improve it and add some more tests. So assert testing. It’s great for small quick assertions. Again we have a package manager and various package managers you can choose.
Pulling in decent testing framework is not really a problem in Swift so we can easily rely on third party or other libraries that there are. You can follow the same style to build your own simple test for that in your specific application. Perhaps if that makes sense for you. I’ve had cases where it does for nearly elsewhere. You don’t necessarily need to use XCTest. And you can write your own simple comparisons to track test failures.
So, XCTest is an extremely powerful testing framework. But it does have some downsides. And some of these downsides is it’s very tightly coupled to Xcode and it gives an inability to customize the reporter which makes it difficult it in its own way.
This is great for having readable and there are also some bundled reporters such as Junit, HTML, and TAP. TAP is a standardized protocol on testing. It’s called Test Anywhere Protocol. And it basically a very simple format where it says okay or not okay. If it’s passing okay, or not okay it’s failing. And then it has the number of the test and then a short description the tools can just use. This is really great because a bunch of tools conform to this and can parse this out, so I could use this output and integrate into other tools.
There’s also HTML output which allows you to view the test’s results in a browser. This is really great because you can use continuous integration. So we can use xcpretty in our CO server which saves the tests or results as HTML files and then we can usually view it in our browser. You are going to have to troll through this huge log where it’s kind of difficult to read what’s going on.
How XCTest Works (16:36)
So, how does XCTest work? On MacOS XCTest works by a small command line tool and it takes the path to your unit to be tested. So it expects a bundle. Xcode provides a kind of convenience around XCTest and you can hit command-U to just run this. Xcode first builds your application target and then builds the test bundle and then invokes XCTest which has complete integration into Xcode’s UI. And shows you the output in the line gutters.
Now this is only possible because of the Objective-C runtime. On MacOS, XCTest and I heard some NS object, which means XCTest command line tool can dynamically load your classes and then it can introspect each of them and run each test. The Objective-C runtime allows us to find all those subclasses that XCTest carries along with all the selectors that begin with the string test. It allows us to light next to all the tests that it can detect.
When you’re writing pure Swift, you don’t necessarily have this Objective-C runtime. So it’s not actually possible to introspect XCTest subclasses and make test methods. When using XCTest without Objective-C, which is on Linux, Windows, etcetera, you’ll need to instruct XCTest all of your names and your selectors yourself. So basically you define the command line tool run app and you say at the bottom, I’m going to run XCTest with this class called person tests dot all tests. And then basically you have an extension on your XCTest subclass adding each string and selector to be run.
Unfortunately, this is kind of problematic. You can easily forget to inform XCTest main function about your test classes and methods. You might forget if you’re doing property TTD to do this. You might not forget if using TTD. However if you’re building on MacOS where this no Objective-C runtime, you could easily test it successfully on MacOS and when you go to use it on another platform, you forgot to put it in there so your test isn’t being run on that platform. And it’s very easy to make this mistake and you can actually see various projects from Apple where they’ve made this mistake and people have found a test that wasn’t being ran on various platforms because they forgot to put the string in. It’s something to think about.
And I don’t really find this whole thing too elegant and I think we can definitely do better. Before Swift was open source, I set off to make my own testing framework. I actually started Spectre much earlier back in 2014, like the week of WWDC. But I never really finished it and when quick testing framework came out, I abandoned the idea because thought well, we can just use that. I don’t need to solve this problem myself.
But years later I came back to it because I wanted to build a testing framework that has its own runner and that means it’s not tied to XCTest itself and it can be used with the upcoming open source Swift. Originally, no one expected Apple would actually port XCTest to work on other platforms, so it was a huge shock to most of us when we had XCTest on the open source Swift.
Spectre doesn’t require any kind of introspection that XCTest does, and therefore provides you this kind of nice syntax for building tests. Spectre is another behavior driven testing framework. It looks kind of similar to Aspect, I showed you earlier. And when I look at many BDD style frameworks, I’m always a little bothered by how they’re designed and how they work. Quite often they’ll provide a global it function which uses global state to know which describe or context we’re in to register and apply that statement to the actual context room. I though we can solve this better.
So what happens here is in describe, it takes a closure. And it actually parses itself like a context inside that, so you can see I used the hash zero, or dollar zero syntax to say the first argument in this closure, and I only call it on that so the it is properly inside the context.
Another thing you might find interesting here is the assertions I’m throwing. Spectre uses an error based model for reporting failures. So you can throw any kind of error and it will cause your test to fail. If your error conforms to a failure type, you can actually test or attach different metadata to that error so it can show in your reporter. And any other errors that don’t conform to that protocol also treat as error. It just won’t look as pretty in your output. So this means you can use try as much as you want in your test and Spectre will catch it and just stop the test if it fails. If there’s actually an error.
So this syntax is possible by providing an equatable method that takes the result of the expect function which is kind of like an expectation protocol. And it compares that against the other thing and it throws if that’s not equal.
Spectre comes with powerful reporting system, so there are a couple of built in reporters that I built myself, including the test I showed you earlier. And the default end report is pretty readable and so this is an example of me running Spectre against my two little tests I have. So basically it’s just the output. It describes a person and then there’s two tests. It has a name and returns the name as description. And they’re green because they’re passing.
One of my other error reports, my favorite one, is the dot reporter. Basically it means it just emits a dot for each test it runs. And if it’s not a success, it just put F, failure, and then it prints the actual failure out later. Often during it you don’t really care about this huge output of every test. You just care about how it fails. This is great for just getting just what you care about. And since the reporters’ infrastructure and spec traits actually moderate itself, basically just a protocol so you can input your own and it’s really great for integrating into other tools. ID, ECI, or any custom workflow.
Testing in Playground (22:39)
And Xcode playgrounds are great, but if you’re actually building code that you want to port to an application later in a playground, it’s very difficult to do TDD since there are not any testing frameworks that can be ran in a playground. So that leaves us the problem of we first write this code about tests and then we need to move it into the application and we have to write the test afterwards or we just have to rewrite it or I don’t know what you did.
So from the start I knew I wanted to solve this problem in Spectre so I build a second reporter that is a test and a test runner that can actually run inside a playground. So this means you can actually build code in a playground using TDD. Then you can copy over your test and implementation once you’re ready.
So let’s take a quick look at how that works. Let’s quickly close our ledger. All right, so let’s open my finder. Right. So here is a directory that I’ve pulled down. So this is the Spectre source itself, so I’ll just, and here is the fast road. So inside here you’ll see Spectre dot playground. So this is actually a playground. You can just double-click to run Spectre. And let me drag it over.
So here’s an actual example of Spectre running. So there’s a built in example like the one you saw in my screenshots previously. So I’m just going to ditch this and I want to copy over my ledger system and we’re going to improve on it now. So what we can do is we run it to make sure it works and let’s rewrite this assertion to actually use Spectre.
So at root, we can use the it to define a test case. So we can say it can parse a positive entry. So we’ve wrapped it in a test case. And now what it’ll do when we hit play. Looks like it’s an issue of Xcode now. And it’s locked up. Right, so there. It says can parse a positive entry. Unfortunately, since we’re using assert, it’s still going to crash when that fails, so let’s just change this to use Spectre’s own testing assertion.
So we can do try, expect, paycheck dot name equals equals paycheck. And now we hit play and that parses. So let’s add our next test because obviously our implementation is kind of dumb because it’s hard-coded this state. So let’s just copy that and let’s create another test. So can parse a negative entry. So let’s create electricity entry. And it’s going to cost us five euros, minus five. And electricity. Electric bill. So let’s say we expect the electric bill dot name to be electricity.
Now if we hit run, it’s going to say, there’s a little cross here, so it can not parse a negative entry. This test failed.
What we can do is improve our implementation. So what we’re going to do is we’re going to basically use foundation to parse a string. So first of all I’m going to import foundation. And then what I’ll do is basically I think what we’ll do is we’ll say if let range equals the entry that we’re given dot range of string, space, what we’ll do is we’ll then take the component there.
So we’ll do entry dot substring to index and then the range dot start index. So the range of the first space is going to be what we’re going to take. So now if we run. Or we’re going to return nil because it’s an option if we don’t have a space in the string. So now we should hit play. If this passes. Yep, so they both pass now. We didn’t actually test the double value here, so let’s add the test to both of these. Or the first one. So try expect paycheck dot and value in this case should be equal to ten. Let’s hit run.
Right, that’s correct. And let’s add the test for the negative case. So we’re going to expect the electricity bill value to be minus five. And we hit play. So it fails now. So what we need to do is actually make this a bit smarter. So what we can do is we can say the entry dot substring from index and range to end index. So we’ll take the first string after the space and what we’ll do is we’re going to turn this to a double. And since the double initializer from a string returns nil if it isn’t actually a double, so we’ll actually need to put this into value. So we can have value equals that.
Then we’ll need to change it a little bit so it’s self dot value is equal value. Hit play. It works, great. So let’s add another test. So both of these entries currently have a plus or minus. Let’s add a test because I want to ensure that if you don’t put a plus or minus, it still passes as a number. And this time what we’ll put is the initial deposit. And this is going to be, let’s say ten. It’s ten, no sign. We expect the name to equal initial deposit and the value to be ten. And we hit run. And we notice it fails.
Now what this is the case is because we are looking for the first space, so it’s actually going to treat this whole thing, it’s going to return nil because it’s treating this as the double, which obviously deposit ten isn’t actually a number. So we need to basically add an option to our range of string to say it say backwards search, that basically looks for the last space. This is plural, so options. Hit run. And they pass. So right, we’ve asserted it to allow so an entry parses itself. Let’s go back to my slides.
Right, so Spectre comes with its own test runner, so when you build your own tests as a target command line tool, Spectre will run at the end of all your code so you’re free to write the behavior, do anything you want. And your command line tool will accept various arguments so you can actually switch different reporters or provide your own. You can also provide filter options.
So it’s not shown here, but you can actually do dash dash filter and say I only want to run tests that match this certain string. It’s great for just running the test relative to what you’re changing right now if you want to speed up the process. Another one of the frameworks you can use is Quick. Now it’s one of the frameworks that sits on top of XCTest itself. So I’m going to quickly go over it since it has support on other platforms too.
So since Quick depends on XCTest, you’ll actually have to create a subclass of Quick test or QuickSpec which is actually a subclass of XCTest case. And then you need to override the spec method and then add your test. So it’s possible to use Quick on Linux and since its open source version of XCTest, you actually need to find the same kind of thing you did earlier for XCTest. Say these are the tests to run because there isn’t Objective-C runtime, so you need to wrestle them the same way. Much of you have heard of Cucumber.
So it’s another tool for running automatic tests in a plain language. It supports a language called Gherkin which is a very simple file and what’s great about this is it’s very readable and anyone can read it and help improve. It can be used to help improve communication between product owners and developers. It embraces coverage on your team and trust that you’ll doing the same thing.
So here’s a quick example. Here I can describe a feature called an array and I expect you can do various things with this array. For example, there’s a scenario where I can append to this array given I have an empty array and I add one to the array, I should have one item to the array. And I can perhaps have another one where I could ensure that a feature called filtering an array works.
So once I have an array with numbers one through five, if I filter those numbers for even, I filter the array for even numbers, then I should only have two items in the array. Now we haven’t actually written any code here. And we’ve completely defined how our test works in the Gherkin format. And there’s a library called Ploughman which Swift implementation of Cucumber test run for Gherkin. It’s not quite feature complete but it does the basics, so you can kind of use it.
And let’s quickly step through how that works. So we want to implement the Swift code behind the scenario we just saw. So basically we can just say using the given function we can parse it in a string that matches the given. And when it matches this string, it will run my closure. So here we say given I have an empty array, we basically set some global array variable to be an empty array. We can also build an x stat. So when, when was using one of them. So when I add a number to the array, so use the regex here, so to test any number in the string, and then it will just append that number to the array.
And another step in the test is we had then I should have x items in the array. So this is a validation step. So then I should have five items in the array. I should actually compare that for an error if it doesn’t work. Now what’s great about this is since the tests that we coupled from our implementation, we can actually share the feature files with perhaps the Android guys or other team. Different implementations to your code.
And once I have to find all my steps, I can actually build Ploughman and my steps together as a command line tool and then parse in the scenario files to run against. So I can change my scenarios without even rebuilding my app providing I’m using pre-defined steps. And you can reuse your steps pretty easily. You might want to build a bunch of new scenarios that already use steps you’ve already built. And since they’re already built, you don’t need to rebuild them. It’s pretty good for building tests like that.
Real World Example (34:51)
So I built a quick real world example. So this is actually a real example. So I built a web framework or web standard called Nest. Basically it has its own test for it. Nest is an interface on how service, web service and web applications work. So it defines how the programs work. And what the test sweep does is it ensures that any web server implementation is implemented correctly against an example application.
So to use the test sweep for Nest, you can take your web application, an example from the test sweep itself. Plug into your own web server that you’ve implemented, and then use Ploughman to run through the bunch of scenarios against it. So the sweep provides a scenario file from HP requests against the HP server you built.
So for example, I could say a scenario. I should get a response code when I make a get request slash hello. And I should have to enter it. Now because of the way these tests work, using matching, I can actually change parse and status codes here and change all these tests quite easily without changing our scenarios. Implementation. So you can build your web server implementation and the tests and then you can run your web server implementation. So it’s actually listing HP requests. Then I can run the test on command line, give it a path to my implementation. And all of the feature files. And it will just run all the tests against it.
It’s kind of great to ensure we have a correct implementation of our, coupling our tests to web server because this supports mobile web service. So now I’ve gone through a few tests and frameworks, let’s look at how we can go about building and running them.
Obviously, you can use Xcode as your build tool. You can also use Xcode’s XCTest integration, which is great for XCTest and Quick. But it is MacOS specific, so you’re probably going to use, or you can also use Xcode build to build and run your tests in the command line. So if you want a bit more power, you can use the Swift compiler directly. We can build our sources and perhaps any dependencies they have.
For example I could build a hello module. I can build my tests and then just run them as a command line tool. We can also utilize existing tools like Make which is basically a simple file for making use to define tests and we can actually define all our tests in here and then send it to what we did before and then we can run make test to just run them and then run our tests.
So running these commands itself can be a little cumbersome. And it adds substantial complexity to your codebase because you’re building scripts to build it and you don’t really want to imagine this on every application or library you build. So Apple released an open source package manager which kind of solved a lot of problems. The package manager is both a dependency manager and a build system itself. Unfortunately, the package manager isn’t available for Swift 2, and only Swift 3, so that means you can’t really use it on Swift 2 code bases.
We can place a packaged Swift file in the root of our repository and providing we have our sources in the sources directory, so it kind of expects the source code layout. It will automatically pick them up and be able to filter.
So here I’ve defined a package called Curassow. It has dependency on get repository of a certain version. When I run Swift build, what it will do is it will open package manger, it will download my dependencies, then it will build my source and dependencies together, and if I have a command line tool, it would build that so I can actually run it. We can also define test dependencies inside the package manger since test dependencies do work. Perhaps the inspector here. And the Swift package manager is capable of running our test too, so it expects all of your tests to be in a test directory. And currently it’s capable of running XCTest on MacOS and the ones without Objective-C runtime on other platforms.
On other platforms, you’re capable of creating a file called Linux Main dot Swift. Basically here you define the main runner for your test. So you have to tell XCTest how to, what classes and test methods you have as I showed earlier. Now it might be a bit deceiving, this file name where it says Linux, because it isn’t actually Linux specific. It was kind of built for Apple or people made requests to add support for other platforms to Swift so I’m assuming this name’s going to change before the final release of the package manager in Swift 3. But for now it’s called Linux Main.
Now unfortunately, Swift test only support XCTest. So this isn’t going to stay this way, the Swift team are planning to add support for custom testing frameworks. Which means you’ll be able to plug in your own tools. Spectre, Ploughman, test directly. But for now it’s not actually possible.
Because the Swift package manager isn’t released yet, it doesn’t have support for testing outside Xtest. I actually wanted to build a short term solution I’ve been using myself. So I could actually switch to use Swift package manager later when it’s a bit more mature. I want to use my third light. Without creating a bunch of scripts to download dependencies and build everything together. So I built swim.
It’s basically like a simple package manager and build system. And it supports Swift. So I had some kind of goals I want to try and solve. One of them was that it’s compatible with multiple Swift versions. So it should work with the current 2.21 stable and also development snapshot and any future release of Swift. Shouldn’t be tied to a specific version. Swim also allows you to test your Swift project with third-party testing frameworks. And swim is compatible with the Swift package manager format so any existing libraries are compatible.
One of the main things that I wanted to try and address was that it should be easy to install, which kind of means it doesn’t depend on Swift itself. So one of the goals is that it’s not written in Swift because otherwise you need Swift and a certain version to be able to build the package manager to then run it.
So basically I just built a simple Python application. You can use the Python tools to install it. So it has very little dependencies because Python is preinstalled on pretty much every platform. And swim accepts the exact same packaged Swift file that the Swift package manager expects, and the same source layout. It’s not quite feature complete, but it is compatible. And the main difference is that Swift test actually just expects a main Swift file in your test directory, so it basically just builds your test as a command line tool and runs it.
So that means you can plug in any test dependency or do any test how you want them yourself. Wait, you don’t have to use XCTest. Since Swift is constantly changing, you might find yourself wanting to switch versions and install more versions all the time. For this I’ve built a small command line tool called swiftenv. It’s a pretty simple tool just to manage your Swift versions and it supports all the platforms. So swiftenv install can basically install a Swift version. So you can just swiftenv install two two one and it will pull down a snapshot or fallback to building from source. You can also use swiftenv local command.
Say that I want to use a certain version of the Swift inside a certain directory. So perhaps in one library I want to use a development snapshot of 2016 and February 8th. And in a different project I’m using Swift 3.8, what’s in my lap. The local command means inside your current directory. You’ll just be running that certain version. And how this works is it saves a file in your directory called dot Swift dash version which just contains the version that you’re going to be running. So it’s advisable you actually commit this to your project, so anyone pulling it down will know what version to use and Swift development which version for them.
So basically as an example. Globally I have the Swift version two two one. And if I see the introduction with Stencil, which is a library, it has its own Swift version file which defines its using three. And then as soon as I seed into that, the Swift command owner changes. I’ve overheard swiftenv uses Xcode Swift versions and the binary snapshots Apple provides.
On Linux basically it just downloads them as a table and installs them itself. Swiftenv is pretty easy to use on MacOS. You can use homebrew or you can follow some of the instructions to build it on other platforms. Since swiftenv itself designed to build Swift, it can’t really depend on Swift because it would make it very hard to actually use and it would defeat the purpose of there being an install command. So it’s actually written in Bash. A really simple scripting language that’s everywhere, so it’s basically easy to use.
So if you’ve ever built a library, you might be interested against multiple versions of Swift. So I might want to build and run my library on Swift 2 and two two one and see how it, or perhaps my code base supports both Swift 2 and 3. So it would be important to ensure that these both work in our tests. We can use swiftenv Swift and command to build against Apple versions. So we can set an environment variable which defines which Swift version to use. So we can say I want to run this command with version two two and two three and three O. It’s very simple.
And if you’re familiar with Travis CI, it’s a continuous integration service allowing you to run your automatic tests. So using swiftenv you can actually install Swift of any version pretty simply on both Linux and OS 10 there. And it means you can easily use the open source version of Swift to test on Linux. Inside your Travis file you can have an install step which downloads swiftenv and the Swift version you’re using on the platform defined. On the top there it says OS.
So it says it’s going to test on both Linux and OS 10. So you can use this to test your existing libraries on multiple platforms. You can define your own command to be run when you actually want to run your test. You can either run Swift test or some script that you wrote or whatever you want to do. Here I’m just using the Swift package manager.
Travis makes it very easy for us to test against more Swift versions. We can just find the environment to use and it will run our install and script command for each of these environment flags. Swiftenv, once it sees the Swift version environment variable, will use that to override the version in the Swift version file in your repository. Temporary override. So that will allow you to easily test against other ones.
So obviously it’s very early for testing Swift to open source, but we’re seeing the tools you need start to emerge. The Swift package manager is constantly evolving and when it’s ready, it should offer us a solid way to build and test our code.
I urge you all to build your open source libraries with this in mind that they might not be specific to iOS and MacOS. You should start testing and supporting other platforms. Swift is not limited to MacOS and iOS, TVOS, and WatchOS any more.
I’ve published the sites that I’ve gone through here on the internet. You’ll be able to find them at the link above. I’d also like to point out that any time you see a hyperlink, it’s actually to a source like additional documentation and resources, and links and libraries. Thank you.
About the content
This talk was delivered live in July 2016 at CMD+U Conference. The video was transcribed by Realm and is published here with the permission of the conference organizers.