Adding support for Swift Package Manager in existing CocoaPods library


Yesterday, I have updated my AHDownloadButton library to support Swift Package Manager and I wanted to share the steps that I needed to take.


Initial setup

To setup the initial folder structure and files needed to integrate SwiftPM we need to navigate to the root folder of our library (in my case AHDownloadButton) and run the following command:

swift package init

After successfully running the command you will see an output like this:

Creating library package: AHDownloadButton
Creating Package.swift
Creating Sources/
Creating Sources/AHDownloadButton/AHDownloadButton.swift
Creating Tests/
Creating Tests/AHDownloadButtonTests/
Creating Tests/AHDownloadButtonTests/AHDownloadButtonTests.swift

As you can see, SwiftPM create its own folder structure that we now have to adjust a bit to work with CocoaPods:

  1. First we need to remove the placeholder files and folders in AHDownloadButton/Sources.
  2. Then we need to move our library source folder with Assets and Classes subfolders into the new Sources folder.
  3. We can also remove AHDownloadButton/Tests since our tests reside inside the example project: AHDownloadButton/Example/Tests.
  4. Now we have to update the project file paths that we have changed, so that Cocoapods can work correctly. We need to open the project workspace, remove the invalid references to the source files and readd them again to the project.
  5. After that we need to update the path in the .podspec. In my example the updated source_files property will look like this: Sources/AHDownloadButton/Classes/**/*.

Now, the initial project cofiguration to support both SwiftPM and CocoaPods is done. The last step is to configure the Package.swift file.


Setting up Package.swift

The Package.swift file is a human-readable configuration file that is used by SwiftPM to build your library. It contains various properties that define your package. This file is created in the root folder of your project during initialization.
The Package.swift file for my project looks like this:

// swift-tools-version:5.0

import PackageDescription

let package = Package(
    name: "AHDownloadButton",
    platforms: [
        .iOS(.v8)
    ],
    products: [
        .library(
            name: "AHDownloadButton",
            targets: ["AHDownloadButton"]),
    ],
    targets: [
        .target(
            name: "AHDownloadButton",
            dependencies: [])
    ],
    swiftLanguageVersions: [
        .v5
    ]
)

The structure is very simple:

  1. The first line is a comment that tells the SwiftPM what is the minimum version of Swift needed for compiling the source code of this package.
  2. name, obviously, defines the name of the package.
  3. platforms defines platforms supported by the package.
  4. products defines a list of products that this package produces. In my case it’s the AHDownloadButton library.
  5. targets defines a list of targets that are part of this package. In my case I only have one target without dependencies. I removed the test target because tests are part of the example project.
  6. swiftLanguageVersions defines a list of Swift versions that this package supports.

After setting up the Package.swift file the only thing that is left is to commit the changes, add a SemVer tag and push the changes:

git commit -m 'Add support for SwiftPM'
git push
git tag 1.3.0
git push origin 1.3.0

The support for SwiftPM is now set up and the library can be tested in your project.


Conclusion

Setting up SwiftPM was much easier than I thought, mostly because my library did not have any kind of special requirements like external dependencies and similar.
I have gone a step further and added the library to the Swift Package Index. It’s a project by Dave Verwer that allows developers to explore different third party libraries that support SwiftPM. More info on how to add your library to Swift Package Index is available here.