Starting with Xcode 12, Apple has matured the Swift Package Manger to a degree where it makes sense to add support for Swift packages in your libraries. There are still some stumbling blocks on the trail that have no obvious solution. So I reckon I want to share with you how I got around them when I recently added SPM support to DTCoreText, DTFoundation and Kvitto.
Before SwiftPM, my general approach for a library would be to have all the library code in a “Core” subfolder, with a “Source” folder containing code being collected and a resource folder for all kinds of resources, such as asset directories or XIB files.
A little history
For the first 7 iOS versions, the product of this product could only be a static library, Apple only introduced the ability to create dynamic frames for Objective-C starting with iOS 8. With Swift it was the other way around: you could only have dynamic frames with Swift code. For the first 4 versions of Swift, ABI (Application Binary Interface) was too much in motion to allow a static connected product. With Swift 5, in 201
Oh boy, I remember all the hacking we had to do to produce a “fake” framework that was actually a fat static library (with disks for all supported processors) and all public headers. We do this so that someone who uses our library can easily let it into their project and get all visible interfaces. In Objective-C, you must have the header files available for public functions and classes found in the library. These `.framework` bundles provided a nice encapsulation of it, so it was almost like handling a single package and adding a third-party frame to your app.
Dynamic frames – in fact, on the device – actually contain no headers anymore as they become useless after compilation. The biggest advantage of first-party dynamic frameworks is that Apple can have APIs and codes shared between all apps installed on the device. The only UIKit framework – installed as part of iOS – is accessed and dynamically connected to all installed iOS apps. Only a single instance is present in RAM at any one time. Custom frames cannot be shared between multiple apps because all apps are in their own little sandbox. Each iOS app that contains DTCoreText, for example, must have its unique copy of it in the app package. If an app has many third-party frames, the process of loading all the frames into memory and dynamic linking can reduce the launch of the app.
Swift Never Had Headers
With the innovations that were included, Swift also gave the module concept to Xcode. The Swift Programming Language website offers this definition of modules.
ONE module is a single device with code distribution – a framework or program that is built and sent as a single device and can be imported by another module with Swift’s import word. Each building target (such as an app package or framework) in Xcode is treated as a separate one module in Swift.
When you import a module into your code, Xcode somehow magically knows everything about the public interfaces contained in it, without having to have its own top file. I do not know how it works, but I’m glad it does!
It was the problem of discovering and integrating third-party libraries into your code base that Cocoapods was invented to solve. The first public release of it was almost exactly 9 years ago, in September 2011. With the default settings – without using frames – Cocoapods would compile the third-party code and merge it with your own, resulting in a single monolithic app binary. And of course, it would clear all those Objective-C headlines for you. If you added
use_frameworks! to your Podfile, then the strategy will change to create a framework / module per pod / library. And that would be the requirement for when using external libraries written in Swift, or so I thought …
I have always used it in apps I work with that use Cocoapods for addiction. Imagine that I walk over to a client about the disadvantages of dynamic frameworks, and try to convince him of the benefits of Swift Package Manager. Imagine my surprise when we inspected the app’s package, only to find a single framework in there. All the third-party code he had ended up with merged with the app binary, my library – written in Swift and integrated via git submodule and Xcode subproject – which resulted in the only dynamic framework in his app.
By default, CocoaPods had always done what we know to be the smarter choice: if third-party code is available, to merge the object code into the app binary. Of course, closed-source frames, which are only available as dynamic frameworks, leave you without this option. Personally, I try to avoid these, as the devil avoids holy water.
Oh, and I would also be the first to admit that I could never warm up to Carthage. I’ve never looked at it. As far as I understand, the difference in approach versus CocoaPods is that Carthage only needs a repo URL to add a component, while CocoaPods needs a Podspec and will generate an Xcode workspace for you where all dependencies are set up in a Pods project. I think it may be this work area of witchcraft that may put some people off Cocoapods.
Resourceful Swift packages
Prior to the current version 5.3 of SPM, the two major remaining pain points have been a lack of resource management and no support for the distribution of binary files as packages. They have now been improved, and the best part is that Swift packages now have the correct integration in Xcode 12.
Another major advantage that CocoaPods had over other addiction managers was the existence of the “trunk”, a centralized warehouse of available pods. There you can search and find libraries that will meet certain needs for you. Another important aspect would be that in order for a version to be released on the CocoaPods case, you would have to “tweak” your pod specification which would validate the syntax and ensure that the library builds without errors or warnings.
Apple (and the SwiftPM open source community) have been working on polishing the tool itself. But the central depot with validation aspect of parcel handling was not filled. Until Dave Verver went and established the Swift Package Index. In his own words:
Swift Package Index is a package search engine that supports Swift Package Manager.
But this site is not just a search tool. Choosing the right addictions is about more than just finding the code that does what you need. Are the libraries you choose well maintained? How long have they been under development? Are they well tested? It is difficult to choose high quality packages The Swift Package Index helps you make better addiction decisions.
Dave launched the SwiftPM library in the autumn of 2019, which in June 2020 was rebuilt as the Swift Package Index that we use today.
It was this implementation of a central index, with a focus on package quality, that pushed me over the edge to finally start embracing SPM. With CocoaPods, it has been tedious to set up a CI server to continue building your libraries for each change to ensure that nothing breaks. On the other hand, SPI builds your package with Swift versions 4.0, 5.0, 5.1, 5.2, 5.3 for iOS, macOS Intel, macOS ARM, Linux, tvOS and watchOS and will then appear on the package page where it worked.
This page provides a very nice overview where developers can get an idea of the quality of this library. And for us project owners, it provides an incentive to try to maximize the number of green ticks you see.
SPI still tracks 5.3 as “beta”, although Xcode 12 has become gold a month ago. The reason is that Apple has accelerated Xcode 12 and the final support for building universal apps that can also run on Apple Silicon, will be in Xcode 12.2 – available later this year.
I also like how SPI tracks both the latest stable release (via tag on master) as well as the progress of the development branch. I wanted these buildings to come earlier, ideally right after pushing the changes to the GitHub repo, but sometimes it can take a long time before the buildings are planned. Also a way to try a failed build again would be very nice, as we are used to from Travis-CI or GitLab-CI.
At this point, I wanted to go into the things I have learned so far from adding SPM to any of my libraries, but I’m still struggling with SPI about some of the coveted hooks. This article, too, has already shown itself longer than I wanted it, that I will do so in the next one.
Let me know if it’s of interest to you, by giving me a tweet. Are you considering adding SPM yourself? Which part did you delete?
Also published on Medium.