Lately, we started a new project for Moya organization, Harvey. The main goal of this project is to be fully compatible with Swift 4 & SPM. And because newest version of PackageDescription, V4, came out, we wanted to try it out.

In SPM’s PackageDescriptionV3 (the old version of a framework that has all the things needed for Package.swift), you’d have to do some workarounds if you wanted to use testing targets and testing dependencies alongside normal builds. These were mostly based on environment variables and checking if given variable was passed — this way script decided if it should do a normal or a test build. Examples of this config you can see on RxSwift or Quick repo.

As you can imagine this was rather tedious to do and a natural way of progressing was to allow more user-friendly API to split these dependencies in config file. And because that was a big issue not only for library maintainers, but also for projects that used SPM (e.g. on server side), we were pretty sure that PackageDescriptionV4 would have this feature built-in. Unfortunately, after reading the documentation, you could guess that it is not there yet.

But documentation is one thing and code is another (even at Apple). The code for supporting both test/normal targets with dependencies is there already!

Setup project

You need to have newest Xcode toolchain to support PackageDescriptionV4. When you have it, go to the directory you want to setup your package in. To create a basic setup you can use one of the swift package init commands:

swift package init --type library

(For more info about creating a package you can check this tutorial from raywenderlich.com.)

Package.swift

Let’s explore newest Package.swift. Say we want to use Quick and Nimble dependencies for testing target and Moya for normal target. You could use the following:

If you were using SPM before, you will notice new line at the top. This basically means that we are using newest tools version, including PackageDescriptionV4 that we want.

Now onto dependencies and targets. In comparison to CocoaPods, we have only one place to declare dependencies of your entire project. You can then select which of these dependencies you want to use for which target (even testTarget now!), though.

You can also notice that here we are using my own fork of Quick. This is the case because right now there is a problem with Quick on SPM and Swift 4. I’ve made a PR with the fix, but we have to wait until the core team reviews it and makes sure I didn’t break anything 😅

Generate xcodeproj

Now that we have created our package, it’s time to explore building your xcodeproj. SPM provides us with a great command line tool, swift package generate-xcodeproj . This command not only generates a project for us, but also builds our dependencies if these are not built yet. This way you could add both .xcodeproj and .build to .gitignore, because you can generate it after git pull both on your computer or CI. No more merge conflicts to resolve (at least with xcodeproj 😅)!

When you open the project, you will see many targets — don’t worry about them. You need to focus only on the targets you declared. In our case we would focus on:

Harvey-Package

HarveyPackageDescription

HarveyPackageTests

The first target, Harvey-Package, is our main target for the lib. So whenever we want to implement something in our library, we use this target for the build.

The second target, HarveyPackageDescription, is another cool thing from SPM. Basically it lets us check the syntax of Package.swift, use autocompletion and more. It is treated like a normal Swift file you would use in the development. When you switch to that target and hit build, it will provide all the answers you need right in your favourite IDE 😄

The last target, HarveyPackageTests, as the name says is our test target.

Development flow

Having the setup already in place, you may be wondering what development flow you could use while developing in SPM. This is what works for me right now:

Pull the changes from remote and generate project using swift build & swift package generate-xcodeproj . Implement features/fixes and push it to the remote. When in need of another dependency, add it to the Package.swift and regenerate the project. Repeat steps 1 and 2.

Rebuilding the project when needed makes sure your project is portable enough. If you find yourself having problems with building, try cleaning your package using swift package clean , remove your Package.resolved & .build directory and build your deps again (step 1).

Encountered problems

When I’ve developed with SPM, I encountered some problems that I didn’t have using other package manager - both on my machine and CI. To resolve them, I created a ruby gem, spm_utils, that helped me configure it the way I want. “Quick” list of what problems this gem should resolve:

Quick was not working even with my fork we used above. The problem is described here and here - basically it is kind of a SPM fault and there is an additional step to fix it after building the dependency.

I wanted all of my dependencies using Swift 3.2 or 4.

I wanted to ignore all the warnings from external dependencies

I wanted to clean the whole project/package/files using one command

If you are also interested in resolving any of these problems, check out the gem, here. You can find there installation process, setup and usage descriptions. You can also add new features you would like to see there 😉

Summary

I really like how it all looks right now. Besides few problems with setting up the project, I really felt happy using SPM. I have the control of everything I need, I don’t have problems with workspace/xcodeproj merge conflicts, everything is portable - everything I need. I see myself using only SPM in the future (if things will go the way I predict they will 😏).

Important links:

https://www.raywenderlich.com/148832/introduction-swift-package-manager - SPM tutorial by raywenderlich.com

https://github.com/sunshinejr/spm_utils - spm_utils gem

https://github.com/sunshinejr/Quick/tree/fix/spm_swift4 - my fork of Quick

That’s it!

Thank you so much for your time and I hope that it was worth it! You can find me on Twitter or GitHub.