Modern modular apps with Xcode 11 and Swift Package Manager
Tags: Xcode 11, Swift Package Manager, Xcode, Engineering, Swift

NOTE: Xcode 12 improves Swift Package Manager support for creating modular apps, so I’ve written a follow up post with how to use it.

Xcode 11 beta 1 is out, and it is extremely exciting! Until now, the best way to manage dependencies was with Cocoapods or Carthage, but today that changes.

Xcode 11 and Swift Package Manager make it ridiculously easy to make modular apps; Xcode 11 automatically resolves dependencies when you build, and even gives useful error messages!

I’ll show you how easy it is.

Step 1

In Xcode 11, create a new Workspace from File > New > Workspace, name it AmazingApp and save it somewhere sensible:

Step 2

Create a new Project from File > New > Project. Choose Single View App, name it AmazingApp and click Next:

Step 3

Choose AmazingApp from the “Add to:” drop down, make sure the right location to store it is selected and click create:

Step 4

That’s your app and workspace created. Lets add a module!

From the bottom left of the workspace, click the + button and choose “New Swift Package”:

Name it FantasticFeature, make sure it’s being stored in the right place and click Create:

Step 5

Now you’ve created your module, you need to add it as a framework to your app so you can use it. Click on the AmazingApp project in the navigator on the left, choose your target (also called AmazingApp) and scroll down to Frameworks, Libraries, and Embedded Content:

Click the +, find your module’s name (FantasticFeature) and click “Add”:

Step 6

That’s it! You’ve added a new module and you can import FantasticFeature into any file in your main target. But it gets better! Let’s add Alamofire as a dependency to the FantasticFeature:

Find the Package.swift file for your module and open it:

In the root Dependencies array, add Alamofire as a dependency:

.package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.0.0-beta.5")

Build your app and you’ll see Alamofire automatically get resolved as a dependency in the workspace 😍:

Step 7

You need to declare which target in the FantasticFeature uses Alamofire, so do that by adding .product(name: "Alamofire") to the array of dependencies specifically for the FantasticFeature target:

Step 8

If you build the app you’ll get no errors, hooray! But if you try to import Alamofire into a file in the FantasticFeature target (like FantasticFeature.swift ) you’ll get an actually useful error saying “Compiling for iOS 8.0, but module ‘Alamofire’ has a minimum deployment target of iOS 10.0”.

Easy fix! Open up Package.swift again in your FantasticFeature module, and declare which platforms your module supports with platforms: [.iOS(.v13)] :

Save the file, build your app, and the error is gone! You can now use Alamofire in your FantasticFeature module, and you can import your FantasticFeature module into your main app.

Step 9

One last piece of the puzzle to figure out is how to create another module that can be imported into your FantasticFeature module. Thankfully that’s easy too!

Create a new Swift Package called Common, and declare it as a dependency for FantasticFeature:

And that’s it! Modern modular app architecture using nothing but Swift Package Manager built directly into Xcode 11. Not bad for a first beta.

Read my follow up post for how to go even further with Xcode 12 to use SPM to manage your modules completely.