| | | |

Swift package creation

Context

My iOS app (Photo Club Hub) and macOS app (Photo Club Hub HTML) are closely related, and thus share significant amounts of Swift files.

The two apps are in managed in two separate GitHub repositories so that contributors are spared any unnecessary complexity when working on either apps. It would however be better to factor out the shared code into a Swift package that contains the code used in both apps. At the time of writing this, the shared code is already organized into a subdirectory that occurs in both apps. But because there is a copy of that subdirectory in both projects, code changes in the shared code need to be manually duplicated. That is not efficient and needs solving.

Youtube

Packages (a.k.a. libraries) are a way to organize code that can be shared across projects.

This post is a summary of the steps mentioned in the Youtube video Creating a Swift Package in Xcode by Stewart Lynch. Note that the video is 4 years old, so may not use the latest features of the Swift Package Manager.

Simplifications

My summary assumes a simplifications compared to Lynch’s video. The assemblies / simplifications are:

  • you have a working GitHub account for version control
  • Xcode already knows about this GitHub account đź“˝
  • you want to include the package inside the main project
  • that you have a general plan which .swift files you want to put into the package
  • that you don’t need to put other files like assets in the package đź“˝

The items labelled with đź“˝ are covered in the video in case you need them.

Steps/ingredients

  1. choose a name for the package. In my case Photo Club Hub CoreData.
  2. file > new > Swift Package, and place it in same Xcode project manager “group” as your main app. In Lynch’s video the second app is created after making the package.
  3. edit the package.swift manifest:
    • platforms: [ .iOS(v18), .macOS(.v15) ]
    • the default is having 2 targets: the package and an extra one for tests/testing
  4. ensure that entry points of package (e.g. a struct) are made public
    • This is what the public privilege level is intended for: usage outside current project.
    • Public structs need explicit initializers – you can’t use memberwise initializers here.
  5. add import <mypackage> to files that access the package
  6. create local git repository for the package
    • associate package to a new remote on GitHub
    • add a tag to give the package a version number (for including proper versions)
    • when pushing the package, be sure to include tag(s) to handle version control at the package level
  7. add documentation to the public interfaces using /// style comments
    • this allows the Xcode quick help feature to show the API documentation

Example: scoping of my package

Both of my apps use Core Data for persistence. The persistence is fundamentally used as a kind of cache: the data in the database gets updated (in background) by retrieving JSON files over the netword.

  • The iOS app allows users to browse the data using a user interface build with SwiftUI user interface. The UI uses WebKit as an embedded browser for some of the UI.
  • The macOS app has a a more basic user interface (also SwiftUI) to browse the data. The WebKit part is omitted. Instead it has a feature to generate a static website that renders the JSON data. The static website (generated locally) uses Paul Hudson’s Ignite package.
Package content

So in my case, the envisaged package contains:

  • the data model of the Core Data database (implemented using SQLite)
  • classes (e.g. “Photographer”, “Organization”) to access the data Core Data
  • functionality to asynchronously load and merge the JSON data files into the database
Comparison to frontend/backend

So essentially the package makes the data available to the apps, regardless of what apps do with the data. Compared to web apps with their backend (database and server-side functionality) and frontend (user interface running on client devices), the package does the backend functionality, but its logic is included into the apps as a library/package. While the actual database is a distributed collection of JSON files.

Core Data versus Swift Data

Ultimately it would be nice if the package doesn’t just contain the shared code but would isolate the apps from the fact that Core Data (as opposed to Swift Data) is used. This would confine the transition from Core Data to Swift Data to internals of the package or to switching over to an alternative package. This is a bit tricky as Swift Data is (more or less) a wrapper around Core Data rather than being an independent successor.

This hiding of the storage technology could alternatively be implemented (package wise) as:

  • A new Swift Data version of the storage package, that replaces the original Core Data one.
  • A Swift Data-based package that is kept interface compatible with the original Core Data-based package.
  • An additional abstraction layer that hides whether Core Data or Swift Data is used.
    And uses the Core Data package where appropriate, but can alternatively use Swift Data.

Similar Posts

Leave a Reply

Your email address will not be published. Required fields are marked *