Installing dependencies using Swift Package Manager

Important: Swift Package Manager does not work with iOS apps!

Feel free to skip this and move onto the Setup discussion.

Most of my days are spent in the wonderful world of .Net, Visual Studio, and the almighty Nuget Package Manager which is the C# no-hassle/couldn't-be-easier-to-use dependency manager. I'm emphasizing this for a reason. There's no silver bullet, any-dummy-could-do-it way of managing dependencies in Xcode.

Today I decided that I'd like to check out RxSwift for a side project I'm starting and I decided to use Apples own Swift Package Manager to manage my dependencies. I mean, its the future of dependency management, right? Maybe? In my opinion, SPM honestly needs a GUI interface like Nuget Package Manager to become widely adopted.... but that's neither here nor there.

Anyway, let's get to the point of this article... installing dependencies using Swift Package Manager.

Setup

Don't skip over this. I did and I wasted two hours because I made the assumption that by having Xcode installed that my tooling was properly setup, it wasn't.

Install the Swift toolchain from https://swift.org/download/, here's the direct download link if you're super lazy like me.

Double-check that your Toolchains are indeed installed at `/Library/Developer/Toolchains`

installed.png

Open terminal and execute the following command:

    
    $ export TOOLCHAINS=swift
    

Sweet, now we're ready to rock.

Installing our dependency into a new project

This may not apply to many of us, so I'll cover installing into an existing project below. But, to install into a new project you create a new folder with your would-be projects name. Then navigate to the folder using Terminal. Let's assume I created a folder called `MyExampleProject` on my desktop. We'd navigate to the folder by typing this into Terminal:

    
$ cd Desktop/MyExampleProject/

Next we'll init our project using the following command.

    
$ swift package init
	

If all has gone well, your new folder should look like this:

Screen Shot 2018-02-03 at 3.09.01 PM.png

Now, let's open up our Package.swift file to see what we've got.

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

import PackageDescription

let package = Package(
    name: "MyExampleProject",
    products: [
        // Products define the executables and libraries produced by a package, and make them visible to other packages.
        .library(
            name: "MyExampleProject",
            targets: ["MyExampleProject"]),
    ],
    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 which this package depends on.
        .target(
            name: "MyExampleProject",
            dependencies: []),
        .testTarget(
            name: "MyExampleProjectTests",
            dependencies: ["MyExampleProject"]),
    ]
)

In order to import the RxSwift package, we'll need to add a dependency line item, which will look something like this:

    
dependencies: [
    .package(url: "https://github.com/ReactiveX/RxSwift.git", .exact("4.1.1")),
],

The code above just points to the GitHub url and specifies exactly which version of the library we want. There's a couple of overloads for this package method available. You can take a look at the source code here to see your other options.

Next, we'll update the targets section so that we're targeting our project correctly.

    
targets: [
    .target(
        name: "MyExampleProject",
        dependencies: ["RxSwift", "RxCocoa"])
]

So, here we're targeting our project and defining dependencies of RxSwift and RxCocoa, which we're importing in the dependencies section.  

So here's what the final code should look like:

    
import PackageDescription

let package = Package(
    name: "MyExampleProject",
    products: [ 
        .library(
            name: "MyExampleProject",
            targets: ["MyExampleProject"]),
    ],
    dependencies: [
        .package(url: "https://github.com/ReactiveX/RxSwift.git", .exact("4.1.1")),
    ],
    targets: [
        .target(
            name: "MyExampleProject",
            dependencies: ["RxSwift", "RxCocoa"])
    ]
) 

Next, we'll just execute the following command:

    
$ swift package generate-xcodeproj

This process fetches all of the specified packages listed, installs them and generates an Xcode project. Your folder should now look like this:

If you open your project and build it, you should see a Dependencies folder with your dependencies, and you should also be able to access your dependencies without a problem.

rxswiftinstalled

Installing a Swift Package into an existing project

Alright, Just to be safe you should check all of your code into source control or make sure you have a backup of your project.

So, your project may look something like this:

Screen Shot 2018-02-03 at 3.34.47 PM.png

We'll navigate to the project using Terminal, just as before. Look up above for code reference.

Once we're at our folder location that contains our project, we'll execute the following command to initialize our Swift Package Manager for this project.

    
$ swift package init

Your directory should now look something like this:

Screen Shot 2018-02-03 at 3.36.04 PM.png

Next, just as before, we'll update our Package.swift file to point to our desired dependency, in this example, once again, we'll pull in RxSwift.

    
import PackageDescription

let package = Package(
    name: "MyExistingProject",
    products: [
        .library(
            name: "MyExistingProject",
            targets: ["MyExistingProject"]),
        ],
    dependencies: [
        .package(url: "https://github.com/ReactiveX/RxSwift.git", .exact("4.1.1")),
        ],
    targets: [
        .target(
            name: "MyExistingProject",
            dependencies: ["RxSwift", "RxCocoa"])
    ]
)

note that this file has changed slightly to accommodate our project name.

So, here's where I'm a bit unsure of the best way to do this so if you have any suggestions you get bonus points for correcting me here. My intuition says that we can use the command swift build to simply build our packages. However, this doesn't actually add RxSwift to our applications. By experimenting, I was able to get the RxSwift projects loaded by using the same swift package generate-xcodeproj command. So, yeah, back up your project files.

With that said, we'll once again execute the following command:

    
$ swift package generate-xcodeproj

Which, for me, installed the RxSwift project files into my project for me. My existing project now looks like this:

Screen Shot 2018-02-03 at 3.54.06 PM.png

The last thing you'll need to do is edit click ManageScheme in the top left and add your app scheme back into the project.  

Feel free to ask questions or offer corrections in the comments below. Thanks to @vzsg in the RxSwift Slack Community for his guidance.