Making Your iOS Library Swift Package Manager-Compatible

Lee young-jun
5 min readDec 28, 2023

--

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)],

error: reference to member ‘v9_0’ cannot be resolved without a contextual type

iosversion

References

--

--

No responses yet