Creating Swift Packages in Xcode

Here’s my quick guide to creating Swift packages with Xcode.

Last updated: Nov 10, 2022

Creating A Local Package

I’m starting from a small Xcode project with some Core Data helper code collected into a sub-folder:

Sample Project Files

My plan is to extract the Core Data code into a Swift package so I can reuse it between projects:

  1. From the Xcode menu File > New > Swift Package...:

    Create package location

    Name the package and create it in the top-level folder (with the xcodeproj file). Add it to the project and make the group the top-level folder.

  2. You should now have a new Swift package at the top-level of your Xcode project:

    File navigator showing package

  3. Copy the source files into the Sources\CoreDataHelp folder. Delete the existing CoreDataHelp.swift template file:

    File navigator showing package

  4. If you have unit test files, copy them into the Tests\CoreDataHelpTests folder.

    Note: I deleted the CoreDataHelpTests.swift, XCTestManifests.swift and LinuxMain.swift files as they are only needed to run your tests on Linux. For more details see Keeping XCtest in sync on Linux by Ole Begemann.

  5. You’ll also want to write something meaningful in the README.md file.

Swift Package Manifest

A Swift package is made up of a collection of source files and a manifest file (Package.swift). The manifest defines the products (libraries or executables) built by the package, any dependencies on other packages, and the build targets including any test targets.

For a simple package, you probably will not need to change the default manifest. I added a minimum deployment version for iOS:

platforms: [
  .iOS(.v12)
],

I recommend watching the WWDC sessions for further details. The full manifest file for reference (the swift tools version will depend on the version of Xcode used to create the package):

// swift-tools-version:5.1
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
  name: "CoreDataHelp",
  platforms: [
    .iOS(.v12)
  ],
  products: [
    // Products define the executables and libraries produced by a package, and make them visible to other packages.
    .library(
        name: "CoreDataHelp",
        targets: ["CoreDataHelp"]),
  ],
  dependencies: [
    // Dependencies declare other packages that this package depends on.
    // .package(url: /* package url */, from: "1.0.0"),
  ],
  targets: [
    // Targets are the basic building blocks of a package. A target can define a module or a test suite.
    // Targets can depend on other targets in this package, and on products in packages which this package depends on.
    .target(
        name: "CoreDataHelp",
        dependencies: []),
    .testTarget(
        name: "CoreDataHelpTests",
        dependencies: ["CoreDataHelp"]),
  ]
)

Versioning and Publishing

To share the package, we need to extract it from the existing project and create a remote source control repository:

  1. Hold the Option (⌥) key and drag the package folder out of the project into a new directory:

    Package files

    You can delete the source files from the original project at this point as we will add them back with a package dependency later.

  2. Double-click the Package.swift manifest file to open it in Xcode.

  3. Create a local Git repository for the package. From the Xcode menu Source Control > New Git Repositories....

  4. Create a remote repository for the package. If you’re using GitHub or Bitbucket you can do this directly in Xcode from the source control navigator (next to the file navigator in the left panel):

    Source control navigator

    Right-click on the top-level package and use New "PackageName" Remote.... Use Add Existing Remote if you created the remote repository outside of Xcode:

    Source control menu

    To use Xcode to create the remote repository you need to have added your GitHub or Bitbucket account to Xcode (see Xcode source control accounts). You can then chose to make the repository public or private:

    Create remote repo

  5. Push the package to the remote repository using the Xcode menu Source Control > Push....

  6. You version the package by tagging it in source control. In the Xcode source control navigator, right-click on the top-level package and choose Tag "main".... Use semantic versioning to add a version number to the current commit:

    Add tag

  7. Push the tag to the remote repository, Source Control > Push..., make sure to select the option to “Include tags”

    Push tag

At this point, we’ve published our Swift Package. If you update the package, remember to increase the version number when pushing the changes to the remote repository.

Using the Package

To use the package, add it as a dependency to an Xcode project:

  1. From the Xcode menu File > Swift Packages > Add Package Dependency.... Xcode shows the repositories visible from any GitHub or Bitbucket accounts you have added. You can also directly enter the URL for the package:

    Find package

  2. You can change the package dependency rules. By default, Xcode uses up to the next major version when updating dependencies. See the WWDC session for more details on how Xcode resolves package dependencies:

    Find package

  3. Add the product(s) in the package to the appropriate targets:

    Dependency options

    If you have additional targets you can add the package library to them later.

  4. You should see the package dependency listed in the file navigator:

    Swift package dependencies

  5. If you want to remove the package dependency, you can do it from the project settings:

    Swift package dependencies

  6. You should now be able to import the library into your Swift projects and playgrounds.

Adding Resources

Xcode 12 added the ability to add resources to a package. See this later post for details:

Learning More

I recommend watching the two WWDC 2019 sessions on the Swift Package Manager. The first session covers using Swift packages, the second covers how to create and share your own:

Dave Verwer’s Swift Package Index is a place to find and share Swift packages: