The Swift Package Manager (SPM) is a tool for managing the distribution of Swift code. It’s integrated with the Swift build system to automate the process of downloading, compiling, and linking dependencies. With SPM you can easily create a Swift Package, distribute it and also integrate swift packages into your projects.
Swift packages are reusable components of Swift, Objective-C, Objective-C++, C, or C++ code that developers can use in their projects. They bundle source files, binaries, and resources in a way that’s easy to use in your app’s project. They are more or less libraries that you use in your various iOS projects.
This article describes step by step, how you can create and distribute your own Swift Package easily and make it available to developers out there to use using the Swift Package Manager.
In this article, we will be creating a library called Loggr, a simple logging solution for your iOS projects. Let’s get started.
With your Xcode (11.0 and above) opened, select File > New > Swift Packages…
We give the package the name, Loggr and save it in a directory. You can tick the option “Create Git repository on my Mac” since we will be distributing it using Git later.
Once completed, Xcode opens with the file Package.swift
opened.
Configurations
The Package.swift file serves as the project manifest file, where configurations can be declared including other dependencies that your Swift Package depend on. In it, we can declare the minimum iOS version on which our library and projects depending on it should run. In this case, we choose iOS 12 as our minimum. Simply add platforms: [.iOS(.v12)]
below the name
field, as seen below.
You can also specify the Swift version to use, we keep that to default for this library.
If your library depends on other libraries, all you need to do is add the dependency to the dependencies
array and specify it under the targets
section. For example, say Loggr depends on Alamofire, below is how your dependency would be defined:
Since our Loggr library doesn’t depend on any library, we are going to leave the dependencies section empty instead.
Source Code
In the Sources
folder is where your source code would be. By default, Xcode has created a file called Loggr.swift
which will come in handy for us, as we will add all the needed code in there.
import Foundation import os.log public final class Loggr { public static func logDebug(_ msg: String) { #if DEBUG os_log(.debug, "[DEBUG]: %s", msg) #endif } public static func logInfo(_ msg: String) { #if DEBUG os_log(.debug, "[INFO]: %s", msg) #endif } public static func logWarning(_ msg: String) { #if DEBUG os_log(.fault, "[WARNING]: %s", msg) #endif } public static func logError(_ msg: String) { #if DEBUG os_log(.error, "[ERROR]: %s", msg) #endif } }
Here, we have declared a couple of static functions that could be called to log a message, depending on the kind of desired log level. Notice the class has been declared final to prevent other classes from extending it. Also, both the class and the functions have been declared public
to override the default internal
access type, so that these class and functions could be visible where needed.
Test classes
The swift package also comes with a generated Tests
folder where you can add your desired tests. For the case of this library, we are going to add a few trivial tests to the LoggrTests.swift
file.
import XCTest @testable import Loggr final class LoggrTests: XCTestCase { func testLogDebugSuccess() { let exp: XCTestExpectation = expectation(description: "Log Debug get called with no error") Loggr.logDebug("Debug Message") exp.fulfill() wait(for: [exp], timeout: 1.0) } func testLogInfoSuccess() { let exp: XCTestExpectation = expectation(description: "Log Info get called with no error") Loggr.logInfo("Info Message") exp.fulfill() wait(for: [exp], timeout: 1.0) } func testLogWarningSuccess() { let exp: XCTestExpectation = expectation(description: "Log Warning get called with no error") Loggr.logWarning("Warning Message") exp.fulfill() wait(for: [exp], timeout: 1.0) } func testLogErrorSuccess() { let exp: XCTestExpectation = expectation(description: "Log Error get called with no error") Loggr.logError("Error Message") exp.fulfill() wait(for: [exp], timeout: 1.0) } static var allTests = [ ("testLogDebugSuccess", testLogDebugSuccess), ("testLogInfoSuccess", testLogInfoSuccess), ("testLogWarningSuccess", testLogWarningSuccess), ("testLogErrorSuccess", testLogErrorSuccess), ] }
We run the tests and ensure they all pass. Next is to publish the library.
Publishing to Github
It’s time to publish the package. We do this by basically pushing to Github. We are going to do this by making use of the Version Control System provided by Xcode.
1. Create Remote Repository
If you didn’t tick the “Create Git repository on my Mac” option while creating the project, simply go to Source Control > Create Git Repositories…, then select Loggr as the project and click on the “Create” button.
Now that we have a local git repository created for the project, simply go to the Source Control Navigator, right-click the project name (Loggr) and select Create “Loggr” remote… A dialog pops up where you can fill some necessary details. Select your Github account from the Account
section. In case you don’t have your Github account set up already, simply select Add an account… and go through each step required to login to your Github account. Once an account is selected, fill in the description for your library, as the Repository Name
is prepopulated with your project name. Then specify the visibility of your project under the Visibility
section. Your repository can either be made public or private. Once all that is done, click on the Create button and wait while your repository is being created. Once that is done, the popup is automatically dismissed.
2. Commit and Tag
The next thing to do is to commit our code. Once that is done, it’s time to create a Tag
for the project which denotes specific release. This enables users to specify which version of the package they wish to use and SPM will fetch the right version for them.
Simply right-click the project name on the Source Control Navigator, select Tag “master”…, where “master” is the branch name. Specify the tag 1.0.0 to denote initial release of the package. The tag is specified using Semantic Versioning. You can also specify a message that goes with your release. Then click on the Create button.
3. Push
Finally, we simply push using the Xcode Source Control Push… option. Make sure the “Include tags” checkbox is checked.
And voila! We’re done! You can then find the newly created package repository on Github. Now we can go ahead and share the library with everyone and have them use it using Swift Package Manager. ?
To see how this library dependency (Loggr) is added to an Xcode project using the Swift Package Manager, check out this other article of mine, where I wrote a step by step process to add dependencies to your Xcode project using SPM.
Thank you for reading and going through this short journey with me to create a library using SPM. Cheers!