This week I thought I'd write about the steps I go through when setting up a simple Swift project for iOS. This is as much for my own benefit as yours, as writing all this down helps solidify the process in my mind as well as giving me a handy place to look up the bits I often forget.

Obviously setup varies from project to project but there's a minimum foundation that is good build on. These instructions describe setting up a dummy project called Animals that includes package manager integration and a unit test framework that augments XCTest. It uses the Swift language and iOS, but a lot of what's covered can apply to any Mac or iOS project created through XCode. Anyway, enough senseless rambling, lets get to it...

Basic project setup

First open XCode and create a new project. I've given up fighting Apple's default project setup these days and stick to the default source tree layout provided by XCode as it makes integrating with other tools simpler, which means source files are kept in the same folder as the project they are built for rather than being scattered through sub folders.

You've probably done this a 1000 times but here's the steps:-

  • Create new iOS Single View application
  • Set the Product name to Animals
  • Make sure the language is set to Swift
  • I usually leave Devices set to Universal
  • Hit finish to create create project where you like

After that quick bit of setup, build and run what's been created and it should work.

Setup for testing

Apple provide a unit testing target out of the box. When adding things to a project its often easy to add them to the unit test target as well as the main project in order to get them tested. The problem with this approach is that it can hide subtle problems and its generally not a very clever way of doing things as it can bloat build times. Instead of putting all the files in both the application and test target we will expose the things we want to test from a Swift module.

We want to build a Swift Module when we build our application. This will then be imported into our test target.

  • Select the Animals project in Project Navigator
  • Select the Animals Application Target
  • Select Build Settings tab
  • Under Packaging set Defines Module to Yes
  • Note the Product Module Name will be the name of your output module Animals
  • Build and run tests to make sure everything is working, tests should pass
    • If you get link errors building test target make sure that Host Application is set correctly in the test targets General settings. Also Allow testing Host Application APIs should be checked.

Now we should now be able to write tests for anything that is exposed as public from the application. We actually need to test this and make sure that our Module works and that we can link to it from our test target. We'll do this by creating a class in the application with an associated test.

Create a new class file in the application called Animal. Make sure to only add the new file to your application target and NOT the test one. Declare the new class public and add a simple property that we can test for.

public class Animal: NSObject {
  public var species: String {
    return "Cat"
  }
}

Now lets write a test. Open the AnimalsTests folder in the Project Navigator and select the AnimalsTests.swift file that was created when you set the project up.

At the top of the file add an import statement for your module.

import Animals

Then remove the existing test functions as they create noise and add the following very simple test.

func testAnimal_isCat() {
    let animal = Animal()
    XCTAssertEqual(animal.species, "Cat");
}

Build and run the tests now and everything should pass. Any errors then start Googling and remember StackOverflow is your friend.

Git Repository

Now we have the bare minimum of stuff configured and working it's a good idea to setup some source control. I prefer Git. I won't go into the potential religious debate of why, I just do. Git comes as standard on the Mac command-line and integrates reasonably nicely with XCode. You may have already created a repository through the project setup wizard. If not I recommend using SourceTree to do it as its a great client.

When you setup up your repository, make sure you setup a .gitignore file. I use this Gist as a template to get me started.

With your repository in place. Add all the files to the index and commit them to the repository before making the next set of potential disastrous set of changes.

Package Management

After many years developing C/C++ software, finding out about Package Managers was something of a revelation. I used NuGet when working with C# and later came across Maven while working on a Java project. With Apple platforms there seems to be 2 main contenders, the hugely popular CocoaPods and the new kid on the block, Carthage. I've been using CocoaPod as it is widely adopted and is used by the libraries I want to use (I haven't tried Carthage yet).

Install CocoaPods

If you don't have CocoaPods installed then do it now. Here's the instructions CocoaPods - Getting Started.

Creating the Podfile

Create a text file called Podfile in the root of the source tree. This is used by the CocoaPods system to identify and locate the desired dependencies.

The first line of the file sets the platform for our project.

platform :ios, '8.0'

We're going to use a couple of libraries to improve our unit tests. Add the following beneath the platform line to include them in our test target builds.

target 'AnimalsTests' do
  use_frameworks!
  pod 'Quick'
  pod 'Nimble'
end

A word of warning, close your XCode project before the next bit! XCode often crashes while the pod tool does it's work.

In a terminal window, navigate to your project root (Where you created the Podfile). Then type pod install.

CocoaPods should now go off and fetch the files for the libraries we're requested. It will then modify your project file to include the necessary Pods and create a new Project for compiling the Pods into a framework or frameworks. This is why we checked everything into Git earlier, I've never had it go wrong, but there's always a first time.

When the pod command has completed you'll be told to close XCode and use the Animals.xcworkspace when developing the project from now on. That's because CocoaPods has now created a workspace to build your project and the libraries that is depends on. Open the workspace in XCode and if all has gone well it should build, run and test. There's more detailed instructions on Podfile setup here.

If everything is working we should check our set of changes into Git before we continue. There's a debate in the community as to whether or not to check the Pods folder into the repository. Personally I do, unless there's a restriction on repository size. Having the pods under revision control means I have access to the dependencies if something goes wrong with the Network or even worse the project is removed from the Internet.

Better Testing with Nimble and Quick

Our Podfile imports 2 projects, Quick and Nimble. Nimble adds fluent assertions which are a much nicer way of expressing assertions than the build in macros. Quick is BDD test framework, which uses closures to structure tests, its a bit confusing at first but leads to a very expressive way of writing tests.

Lets create a test with Nimble and Quick to make sure everything is definitely working properly.

  • Find your AnimalsTests group folder in the Project Navigator
  • Right click the folder and select New File...
  • Select the Cocoa Touch Class template and click next
  • Name the class AnimalSpec and make it a subclass of QuickSpec
  • Hit next and make sure that the file will be created in the AnimalsTests folder and only added to the AnimalsTests target

At the top of the AnimalSpec.swift file import our dependencies for the test class as follows in order to fix any compile errors..

import Foundation
import Nimble
import Quick
import Animals

Your project should still build and test without errors at this point.

Quick's documentation is fantastic and is a quick read so I'd recommend you go through it if you've not already done so. In the meantime here's a quick test that duplicates the test we wrote earlier using Nimble and Quick.

class AnimalSpec: QuickSpec {
  override func spec() {
    describe("an Animal") {
      it("is cat") {
        expect(Animal().species).to(equal("Cat"))
      }
    }
  }
}

When you run this test it should pass. It's worth making the tests fail and comparing the output between XCTest and Quick to see what the difference is particularly with how tests are named.

Quick's BDD framework along with Nimble's fluent assertions provides a really powerful base for building Unit Tests. If you don't like Quick's BDD style you could always use Nimble on its own to get the benefits of the improved assertion syntax inside an XCTestCase subclass, just remove Quick from the Pod to drop the dependency.

So now we have a project with CocoaPods integration and a shiny new BDD test framework. Cleanup the tests by removing the default AnimalsTests.swift class file from the project and then commit what you have to Git.

Conclusion

And that's it. At this point we've created a new XCode project with source control, we have CocoaPods integration for Package Management and added the Quick and Nimble frameworks for testing. This provides a nice foundation for any project to build off. I'd love to automate the setup to be honest with you, but haven't quite figured out how to integrate a Podfile into a custom XCode project template, something to tackle one day in the future I think.

If you have any suggestions for improving this including suggestions for tools or frameworks, please let me know in the comments below.

 

Share this: