Create your own Swift Package and release it on Github.com

First step into successful modularisation and reusability 📦

Przemek Jablonski
7 min readAug 31, 2021
Cat sitting in the cardboard box
Your cat will love your new Package 🐱 Unsplash

Let’s step up our iOS coding game.

I bet you all have some place in your latest project that is independent from the business logic of your app. Whether it’s your latest extension collection, your Utils folder or even some code already accessible as separate target in your workspace. Have you ever thought about properly separating it as an isolated framework, making it reusable and distributing it as a Swift Package? It’s easy, fun and the benefits of it are huge.

Interested?

Let’s walk ourselves through the process of separating a piece of code from my personal project, creating Swift Package out of it, deploying it on Github.com and integrating it back to the app using the Swift Package Manager.

Sounds good? Let’s go! 🚀

This article is part of a series where we will take the Package created right here and marry it with the Github Actions CI. Stay tuned!

My Swift Package

My app has a dataset which is most conveniently displayed as a map. Map can be moved and rotated freely, but users don’t need to be tracked at all to use the app — so there is no need to ask them for the GPS location permission.

The problem is that it would be nice if the map was centered on approximate user location when it’s first launched though. It would feel intuitively wrong if the app launched from the United States territory would center on European continent by mistake etc.

So I have built a solution which is capable of fetching ballpark-level user location based on the OS locale and region settings. Yes, it has it’s logical caveats, but it serves the my purpose very well.

I have decided to name my piece Earendil, and it sits in my MapBasedApplication directory as below:

Earendil as part of MapBasedApplication project

I needed similar functionality at least once in the past, so I know there’s potential for reusability there. Let’s make a standalone Swift Package out of that!

Creating a blank Swift Package

Let’s open up the Xcode and create a new Swift Package using the ⌃⇧⌘N or File > New > Swift Package.... I will name it Earendil. Xcode will open up this newly created Swift Package for you automatically.

Default Swift Package directory, generated by Xcode

Notice it’s super simple structure. Your compilables go into /Sources, Unit Tests into /Tests and the Package settings are easy to understand and live inPackage.swift. Nice! Paste Earendil code where it belongs (/Sources) and let’s try to build it!

Package populated with copy-pasted Earendil contents

Building and Multi-Platform Support

Now, test if everything works correctly by building the Package itself (⌘B). At first, you might be confused by amount of build targets your Package can have. Xcode may even suggest weirdly looking target called My Mac at first.

Default build target for Swift Packages

That is because Swift Packages by default are designed to be multiplatform. No matter on which  Operating System your Package will be run on, it will integrate and work seamlessly, as if it never left the iOS-land.

Try it out yourself — selecting any non-iOS device will build your Package using different SDK than the iOS one. Pretty cool!

Long list of build targets for typical Swift Package

If your Package doesn’t want to compile though, check if the build target is valid. It should work if the build target is set to some iOS device (simulator or Any iOS Device), since this code was part of the iOS project before.

You always can disable multi-platform support using the SupportedPlatform in Package.swift file. However, I would strongly suggest making your code as much cross-platform as possible. Not only because it’s easy and usually there are no API barriers that prevent you from doing so, but also because it seems that Apple itself is going down this route too. Note that the new Apple tools are all multiplatform nowadays (SwiftUI, Combine, SPM, CoreML etc.).

Snippet from the Docs (SwiftUI)

Also, it would suck if you tried to build a multiplatform app in the future, and your own toolset prevented you from doing so. Been there, done that. 🙄

Double check the access levels

Few things to note, before we dive into the Package hosting. A side effect of modularising your app is that the internal access level keyword actually starts to work as intended now. So remember, all internal properties and classes (and those without explicit access level too) will NOT be available outside of your Package — it won’t be a part of your Package public API. You might want to think about that before the release.

I recommend splitting your Package into directories which suggest their contents access level, such as /API folder for public stuff and /Internals folder for anything marked as internal. This would also be good time to make sure that your Package public API is clear, concise and easy to use.

Package split into /API holding public and /Internals holding internal stuff

Get that code coverage high

Since your package will be deployed publicly on GitHub.com and will be cross platform, it makes sense to make sure that it actually works under all target platforms,  OSs and Xcode versions. This is why I recommend writing an extensive suite of unit tests, which can quickly check if every user will get fully operational framework, no matter their development setup.

You even have dummy test already there in the/Tests directory by default. Replace it with proper Test Cases and run them by going ⌘U.

Replaced dummy XCTestCase with proper Unit Test suite

Host your Package on GitHub.com

Ok, so we got out Package sorted out locally — let’s figure out how to release it. In order to make the Package easily accessible through Swift Package Manager, it needs to be hosted somewhere. So, create empty public repository on GitHub.com and git push our Earendil Package code there.

Earendil Package got it’s public repo 🌐

And that’s it really! Swift Package Manager supports integrating any proper git repository as a project dependency, so creating one on Github.com is basically the only thing you have to do to make it happen. Pretty cool!

I highly recommend setting up some semantic versioning, for future maintenance sake. You can set this up in Releases section on the right, under theDraft new Releasebutton. I will choose 1.0.0 as a first released version.

Releases section listing first Package tag, 1.0.0!

Fetch your Swift Package via Swift Package Manager

Let’s go back to our parent app. Since we have decided that the Earendil lives in the Github.com now, remove the leftover local files from the MapBasedApplication project. Then, integrate Earendil back into this app — this time properly, using the remote version of it.

To do so, open up the SPM dialogue in Xcode first (File > Swift Package > Add Package Dependency).

Inserting your Package URL at the top is enough for the Swift Package Manager to properly checkout the code and register it as a project dependency. Even cooler — if you’ve set up the Xcode’s Source Control tab to point to your Github profile, then your repos are visible instantly at the top without the need for the URL input at all. Neat!

Now, you should be seeing your new Swift Package as a remote package dependency, as above. You can even peek into the Package directory and see if everything looks as it is on Github.com.

And you are done, congrats! Take a look at the complete Earendil project here on Github. Thanks! 🚀

In the next article, we will learn how to marry our brand new Package with Github’s embedded CI service called Github Actions. Stay tuned!

--

--