Notes on Swift package manager
What is it:
SPM automates the process of downloading, compiling, and linking dependencies.
Pretext:
Developing for apple devices requires you to use either Cocoapod Carthage or SPM. Which can be divided into 3 camps: The past, the present and the future. SPM being the future.
Folder structure:
[insert illustration here]
First impression using SPM:
SPM seems like a command-line tool for downloading projects that contain .swift files from github. It’s able to derive files based on “Sem-Ver” tagging of committed code. I think it also supports nested dependencies but I haven’t tested that part. And thats pretty much it. Which leaves a lot to be desired. Like support for binaries (.framework)? Which is really important when dealing with third-party libs. (binaries are a lot faster to compile that source code)
Export:
- Add an empty Package.Swift to your xcode project
import PackageDescription let package = Package( name: "Element" )
- keep all other .swift files you want to include in the package in a folder named Sources
- Upload the Sources folder and the Package.swift file to github
Import:
- Add a Package.swift to your xcode project with example:
import PackageDescription let package = Package( name: "Element", dependencies: [ .Package(url: "https://github.com/eonist/Element.git", majorVersion: 1)/*<--1.0.0..<2.0.0*/ ] )
Pick versions with this syntax: majorVersion: 0, minor: 4
For more specific version picking use: Version(0, 0, 0, prereleaseIdentifiers: ["alpha", "3"])
which would download 0.0.0-alpha.3
for ranges you can use syntax such as : Version(2, 0, 1) ..< Version(2, 1, 0)
- navigate to the folder in terminal
cd ~/Documents/dev/SomeProject
- in terminal execute
swift build
Excluding non-source files:
Use this property to exclude files and directories from the package sources.
let package = Package(
name: "Foo",
exclude: ["Sources/Fixtures", "Sources/readme.md", "Tests/FooTests/images"]
)
Create .xcodeproj file via terminal:
Each SPM-enabled project can be converted to an Xcode project:
cd to the respected folder
swift package generate-xcodeproj
SPM dependency resolving:
If two packages depend on different versions of a third package, the package manager tries to find a version of that package that both will find acceptable.
Drawbacks:
- Unable to target Commit ids. Only release tags are supported. Which makes it difficult to have a fast workflow when evolving your projects. Releases should be significant and not iterate on every new commit. If you have a lot of nested frameworks which you should because modularity is good and thats why we have dependency managers in the first place. Apple has no intention to support targeting commit ids according to their mailing-list on SPM. Carthage has support for targeting commit ids and even
"HEAD"
However pushing a release tag isn’t that much work but it is inconvenient. ⚠️️(this is not the case in SPM4)⚠️️ - Difficult to update dependencies once xcodeproj is generated ⚠️️(this is not the case in SPM4)⚠️️
Side notes:
- Target beta/alpha versions:
Version(0, 0, 0, prereleaseIdentifiers: ["alpha", "2"])
- The ‘Sources’ dir could also be ‘Source’, ‘src’ or ‘srcs’
- In terminal:
swift build --help
shows help options in terminal - In terminal:
swift package
shows all terminal commands available in SPM - In terminal:
swift build --version
printsApple Swift Package Manager - Swift 3.0.2 (swiftpm-11750)
- You should be able to target individual commit ids via: buildMetadataIdentifier in Version. Google it 🔑
- in terminal:
swift package init --type=library
Creates a SPM starter kit - In terminal:
swift package test
tests your SPM project - In terminal:
swift package init --type executable
makes an SPM starter kit that can be called from terminal - In terminal:
.build/debug/<Package-Name>
will execute your project if its executable and has been built - In terminal:
swift package init --type=library
creates a testable setup. Runswift test
after - In terminal:
swift package fetch
Swift package has commands to fetch the dependencies - In terminal:
swift package update
Swift package has commands to update the dependencies
Gotchas:
-
SPM forces you to organise your folder structure the “SPM way”, I didn’t like this at first.
Source/<project-name>/<file-name>.swift
but if you only have one ‘module’ which you should. Modularity and all that. Then github.com will not force you to jump through 2 levels to see your files. as github skips 1 level if a folder is inside another folder. So you just click sources and there is all your files. all in 1 click. NICE! 🔑 -
If you actively change your dependencies’ code then don’t link to binaries directly, instead include their source code as sub-project. When its code base becomes stable start using binaries aka (.framework).
-
The target in the SPM manifest is used when you include internal dependencies in the Source file see : this
Final word:
We are all DevOps now. There is no getting around this, if you want to code efficiently you have to: “master the art of DevOps”. Or descend into “dependency hell”. Package Dependency managers yet Are not easy to use yet, but one cannot live with out them.
Links:
basic swift info video Apples docs on SPM: SPM + CI Unit test advance spm
Todos:
- Figure out how SPM can build
importable frameworks
into an XCode app project ✅ - Figure out how you can bring Existing apps into the SPM framework fold 👈
- test hybrid framework if they rebuild on run or not
Targeting hash
- buildMetadataIdentifier: Optional build meta data for eg: timestamp, hash, etc.