This blog post is about how to reduce the number of dependencies in your project when using SPM in Xcode.
People often take SPM for granted because it's so easy to import new dependencies within the IDE, however, we can streamline the number of imports even when we have to import a particular Swift Package.
SPM is the best solution for iOS dependency management these days for multiple reasons:
Package.swift
. This makes the system more robust than CocoaPods, for example.I recently made a mistake where I automatically imported all the modules referenced by a Swift Package dependency, even those I don't need. This is the default behaviour when adding a Swift Package dependency. Many Packages can have multiple Products which each expose different APIs for different applications.
If you aren't sure if you need an import, check it and delete it. For example, a Package could include an Objective-C Module that adds an additional unnecessary import.
In my case, I imported a Swift Package that was exported via multiple nested libraries: OHHTTPStubs
and OHHTTPStubsSwift
in the above example.
Swift Packages are imported not just with the Git source code checkout, but also with one, or several Package Products. We don't see these in everyday development because they are checked out into Derived Data
.
Xcode uses Derived Data rather than the regular project directory because to improve convenience with source control. We no longer need to check in 3rd party source code into a repository, and can leverage Xcode's tight integration with Derived Data for lookup efficiency.
Each target should only import the libraries it uses.
Rules:
OHHTTPStubsSwift
is the equivalent Swift CocoaPods subspec that adds a nicer API wrapper over the ObjC API but already imports the ObjC API (OHHTTPStubs
).
I deleted the Package Products from the Host Target because I was only using it in UI Tests. I then only imported the OHHTTPStubsSwift
via Build Phases.
We can delete nested Swift Package dependencies via Build Phases or the Target General settings tab without deleting the Package itself. I have outlined an example above for Unit / UI Testing integration, but we can easily apply the same technique across a modular workspace or any project with multiple targets.
Deleting unnecessary dependencies is a well-known best practice to save your app's memory footprint.
Ps. If you want to really mock an API, don't use
OHHTTPStubs
. I would recommend using a Dependency Container as outlined in Pointfree's tutorial: Dependency Management made easy. Arguably you don't even need UI tests with such a testable Networking layer.