Article

Creating a Swift Package with Xcode

Create a Swift package to promote modularity and code reuse across your apps.

Overview

As you develop your app, you may discover parts of your code are reusable in your other apps. Refactoring this code into a Swift package reduces code duplication between projects and fosters reuse, improving maintainability as your apps grow over time. Xcode compiles the source code in Swift packages to match the app’s requirements in terms of SDK, architectures, and so on. It can compile packages for multiple platforms in the same build operation, removing the need to create a separate framework target for each platform.

Once you’ve organized your reusable code into a Swift package, you can decide whether to use it locally in a project, or publish it to a private Git repository. To take the idea of sharing code even further, you can become a part of the open source community and make your Swift package available to a global audience. To learn more about publishing a Swift Package, read Publishing a Swift Package with Xcode.

Create a Swift Package

A common use case for Swift packages involves adopting them for your existing shared component. To turn your existing component into a Swift package, you don’t need to create a new Swift package from scratch. Instead, keep your existing project and add a README.md and a Package.swift file inside the root directory of your library project to turn your library into a Swift package.

Use the sample Package.swift file below to get started and configure the Swift package to use your existing project structure. The amount of configuration depends on your shared component; you can configure minimum deployment targets, package targets, products, and the paths to your source and test files.

If you aren’t turning your existing shared component into a Swift package, you need to create a standalone Swift package. To create a new Swift package, open Xcode and select File > New > Swift Package. Choose a name and select a file location. Select “Create Git repository on my Mac” to put your package under version control. On completion, the Swift package opens in Xcode and looks very similar to a regular Xcode project.

Screenshot showing a newly created standalone Swift package named ExamplePackage. The Editor area shows the package's manifest file while the Navigator area shows the package's contents and the Utilities area displays the information about the package manifest.

Xcode generates all necessary files and folders as it creates a Swift package:

  • The README.md file resides at the root level of the package. It describes the functionality of your package.

  • The Package.swift file, or package manifest, describes the configuration for the Swift package. You can double-click it in Finder to open the package in Xcode. The manifest file uses Swift and API from the Swift Package Manager’s PackageDescription library. It defines the package’s name, products, targets, and dependencies on other packages.

  • Source files reside in a folder named Sources. A Swift package can contain several targets and, as a convention, each target’s code resides in its own subfolder.

  • Unit test targets reside in a folder named Tests, and, following the same convention as regular targets, each test target’s code resides in its own subfolder.

Configure Your Swift Package

Swift packages don’t use .xcproject or .xcworkspace but rely on the folder structure and use the Package.swift file for additional configuration. The following code listing shows a simple package manifest:

// swift-tools-version:5.1
import PackageDescription

let package = Package(
    name: "MyLibrary",
    platforms: [
        .macOS(.v10_13),
    ],
    products: [
        .library(name: "MyLibrary", targets: ["MyLibrary"]),
    ],
    dependencies: [
        .package(url: "https://url/of/another/package/named/Utility", from: "1.0.0"),
    ],
    targets: [
        .target(name: "MyLibrary", dependencies: ["Utility"]),
        .testTarget(name: "MyLibraryTests", dependencies: ["MyLibrary"]),
    ]
)

A Package.swift file needs to begin with the string // swift-tools-version:, followed by a version number such as // swift-tools-version:5.1.

The Swift tools version declares:

  • The version of the PackageDescription library

  • The minimum version of the Swift tools and Swift language compatibility version to process the manifest

  • The required minimum version of the Swift tools to use the package

Each version of Swift can introduce updates to the PackageDescription library, but the previous API version is available to packages which declare a prior tools version. This behavior allows you take advantage of new releases of Swift, the Swift tools, and the PackageDescription framework, without having to update your package manifest, or losing access to existing packages.

To learn more about the PackageDescription API, read the documentation for the Package class.

Add Your Code

To add source files to a Swift package, you can use workflows that you already know. For example, you can add a source file to a package by dragging it into the Project navigator, or by using the File > Add Files to [packageName] menu.

Screenshot showing a standalone Swift package with two added source files and two unit test files.

Make Your Swift Package Cross-Platform Compatible

While Swift packages are platform-independent by nature and include Linux as a target platform, your Swift packages can be platform-specific. Use conditional compilation blocks to handle platform-specific code and achieve cross-platform compatibility. The following example shows how to use conditional compilation blocks:

#if os(Linux)

// Code specific to Linux

#elseif os(macOS)

// Code specific to macOS

#endif

#if canImport(UIKit)

// Code specific to platforms where UIKit is available

#endif

In addition, you may need to define a minimum deployment target. Note how the package manifest below declares minimum deployment targets by passing them in as a value to the platforms parameter of the package initializer. However, passing minimum deployment targets to the initializer doesn’t restrict the package to the listed platforms.

// swift-tools-version:5.1
import PackageDescription

let package = Package(
    name: "MyPackage",
    platforms: [
        .macOS(.v10_13), .iOS(.v11), .tvOS(.v11),
    ],
    products: [
        // Products define the executables and libraries produced by a package, and make them visible to other packages.
        .library(
            name: "SharedCode", targets: ["SomeInternalCode", "SharedCode"])
        ],
        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: "SharedCode"),
            .target(
                name: "SomeInternalCode"),
            .testTarget(
                name: "SharedCodeTests",
                dependencies: ["SharedCode"]),
            .testTarget(
                name: "SomeInternalCodeTests",
                dependencies: ["SomeInternalCode"]),
    ]
)

Build Your Targets and Run Unit Tests

Xcode creates a scheme for each product in the Swift package’s manifest file. If your package contains multiple products, Xcode creates an additional scheme with the name [packageName]-Package to build all of them. Use this scheme to build all of the package’s targets and run all unit tests.

Select a scheme for the package’s build-and-run destination, and build it as you’d build an app target. Each source target usually has at least one corresponding test target.

Leverage the Package's Functionality Locally

To use your package locally, open your Xcode project or workspace. Drag and drop your Swift package’s folder into the Project navigator to add the package to your project. In your target’s Settings, select the General pane, scroll to the “Frameworks, Libraries, and Embedded Content” section, and click the + button. As your package manifest declares a library product, you can select the Swift package’s library product to link it, just like any other library or framework.

In your app’s code, you can now import the package’s targets as Swift modules and use their functionality in your app. For example, the package manifest above defines a target SharedCode that a developer can import as a Swift module by declaring import SharedCode.

Organize Your Code with Swift Packages

You can organize a larger code-base into Swift packages, instead of using a project setup that uses subprojects or Git submodules.

  1. Create a Swift package as part of your Xcode workspace by selecting File > New > Swift Package.

  2. Choose a project and a group, but don’t create a new Git repository if you app is already under version control and create the new Swift package.

  3. Move your code into the new package in the Project navigator.

  4. Open your app’s project settings, select your app target, and navigate to its General pane.

  5. Click the + button in the “Frameworks, Libraries, and Embedded Content” section and select the local package’s library to link it into your app target.

See Also

Code Sharing

Adding Package Dependencies to Your App

Integrate package dependencies to share code between projects, or leverage code written by other developers.

class Package

The configuration of a Swift package.

Publishing a Swift Package with Xcode

Publish your Swift package privately, or share it globally with other developers.

Building Apps that Use Swift Packages in Continuous Integration Workflows

Prepare apps to consume package dependencies within an existing continuous integration setup.