Making Your iOS Library Swift Package Manager-Compatible
I have my own Pod, LSExtensions, but it didn’t support Swift Package Manager (SPM). Nowadays, many development teams prefer using SPM over CocoaPods.
Therefore, I decided to make LSExtensions compatible with Swift Package Manager.
Swift Package Manager
My goal was to eliminate the necessity for the line in the Podfile of the project
pod 'LSExtensions'
According to the guide, I ran the following command under LSExtensions directory:
swift build
However, I encountered this error:
error: Could not find Package.swift in this directory or any of its parent directories.
This indicates that I need to create a Package.swift
file.
Package.swift
Package.swift
is similar to Podfile
or .podspec
for CocoaPods. To initiate it, I started writing the Package.swift
file following the document. While CocoaPods uses pod init
, Swift Package Manager supports initiation using:
swift package init
I appended the newly created files into the project to edit later.
Now I can run the build command:
Install Local Package
To test the installation before uploading the package to GitHub, I opened Swift Package Manager
and selected ‘Add Local’
Then, I could choose the local package.
I also had to select a project to add LSExtensions
before pressing Add Package.
Lastly, I pressed ‘Add Package’ again to confirm.
However, I couldn’t use methods contained in the library.
Because the SPM module should have source files under the Sources directory.
/// - sources: An explicit list of source files. If you provide a path to a directory,
/// the Swift Package Manager searches for valid source files recursively.
static func target(
...,
sources: [String]? = nil,
To specify another directory as the root, we need to set path
.
.target(
...,
path: "LSExtensions",
Without this option, Swift can’t find source.
My root directory has files not in Swift like .h, .m. Therefore, I had to specify exclude
also.
.target(
...,
path: "LSExtensions",
exclude: ["LSExtensions.h",
"Info.plist",
"Readme",
"Extensions/Objc"]),
Some of the extensions are using UIKit; therefore, the Package should depend on the framework. So I guessed dependencies
would solve the issue.
targets: [
.target(
...,
dependencies: ["UIKit"],
path: "..."),
Don’t forget to put dependencies
before the path
.
Opposite to my thinking, dependencies couldn’t solve the problem.
LSExtensions is only for iOS. So I had to specify that this library can be used only on iOS. See supported iOS versions.
platforms: [.iOS(.v13_0)],
Now I can build my project again!
Remove Local Package
I was starting to publish my package. Before that, I would remove the local package first.
Publish Package
To install the package based on a specific version, I uploaded the package with a new tag.
And I can see the latest version is automatically put in the manager.
Now building with the remote package has also succeeded!
If you found this post helpful, please give it a round of applause 👏. Explore more iOS-related content in my other posts.
For additional insights and updates, check out my LinkedIn profile. Thank you for your support!
Troubleshootings
No selected project
Source files for target xxx should be located under ‘Sources/xxx’
.target(
...,
path: "LSExtensions"),
contains mixed language source files; feature not supported
.target(
...,
path: "LSExtensions",
exclude: ["LSExtensions.h",
"Info.plist",
"Readme",
"Extensions/Objc"]),
no such module ‘UIKit’
targets: [
.target(
...,
dependencies: ["UIKit"]),
argument ‘dependencies’ must precede argument ‘path’
targets: [
.target(
...,
dependencies: ["UIKit"],
path: "..."),
product ‘UIKit’ required by package
platforms: [.iOS(.v13_0)],