Create a Swift Package and publish it to GitHub

To describe building and publishing your swift package in this post.

Introduction

Try to make Swift Package and publish it on GitHub.
My environment is as follows:

1
2
3
4
5
6
7
8
$ sw_vers
ProductName:	Mac OS X
ProductVersion:	10.15.7
BuildVersion:	19H524

$ xcodebuild -version
Xcode 12.4
Build version 12D4e

Creat Swift package project

In Xcode 12, User can create a swift package project on the one.

./images/figure_01.png

But currently requires some command line operations.
Previously, you had to use the following command.

1
swift package init --type library

Package.swift and dependency description

Swift Package require that describe the dependency of another swift package to Package.swift.
It seems that adding a dependency cannot be done from the Xcode menu, so Package.swift edit it directly.

The steps are the following two points.

  1. Describe the URL and version of the package

  2. Describe the package name in the Target you want to depend on

The source code below describes Package.swift the URL and version of the dependent package in the initial state of dependencies: [].

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "Teams",
    products: [
        // Products define the executables and libraries a package produces, and make them visible to other packages.
        .library(
            name: "Teams",
            targets: ["Teams"]),
    ],
    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 this package depends on.
        .target(
            name: "Teams",
            dependencies: []),
        .testTarget(
            name: "TeamsTests",
            dependencies: ["Teams"]),
    ]
)

For example, to add the HTTP library Alamofire to a dependency:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "Teams",
    products: [
        // Products define the executables and libraries a package produces, and make them visible to other packages.
        .library(
            name: "Teams",
            targets: ["Teams"]),
    ],
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        .package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.4.1")
    ],
    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 this package depends on.
        .target(
            name: "Teams",
            dependencies: []),
        .testTarget(
            name: "TeamsTests",
            dependencies: ["Teams"]),
    ]
)

Package.swift when you save, Xcode will go to get the package added to the dependency, and the dependency will be displayed as shown in the red frame in the figure below.

./images/figure_02.png

Now, write the package name in Target that you want to depend on next.
So let's check the package name of the added package.

In the case of Alamofire, It's a Swift Package Dependencies > Alamofire > Package.swift description as shown in the figure below.

./images/figure_03.png

Did you find package name?
Then, you could append your Package.swift.
Updated Package.swift is here:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "Teams",
    products: [
        // Products define the executables and libraries a package produces, and make them visible to other packages.
        .library(
            name: "Teams",
            targets: ["Teams"]),
    ],
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        .package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.4.1")
    ],
    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 this package depends on.
        .target(
            name: "Teams",
            dependencies: ["Alamofire"]),
        .testTarget(
            name: "TeamsTests",
            dependencies: ["Teams"]),
    ]
)

Create an Xcode project on the command line.

1
2
3
4
$ swift package generate-xcodeproj                                                                                                               [~/Documents/Projects/Personal/Teams 12:41]
generated: ./Teams.xcodeproj

$ open ./Teams.xcodeproj

Let's build it.

1
2
3
4
5
6
7
8
9
$ xcodebuild -project Teams.xcodeproj \
             -target example \
             -sdk iphonesimulator \
             -configuration Debug build

// 割愛: xcodebuildが出力するログ

$ ls build/Debug-iphonesimulator/                                                                                                                [~/Documents/Projects/Personal/Teams 12:51]
Alamofire.framework Teams.framework

If the build is successful, Teams.framework will be generated. And Deployment Target what about the settings of this framework? Let's look that is the red frame in the figure below.

./images/figure_04.png

Oh, it's a little old. Since the latest iOS is 14 series, 9 series will support 6 generations. This is too painful. Let's change it.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
// swift-tools-version:5.3
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "Teams",
    platforms: [.macOS(.v10_14),
                .iOS(.v14),
                .tvOS(.v14),
                .watchOS(.v5)],
    products: [
        // Products define the executables and libraries a package produces, and make them visible to other packages.
        .library(
            name: "Teams",
            targets: ["Teams"]),
    ],
    dependencies: [
        // Dependencies declare other packages that this package depends on.
        .package(url: "https://github.com/Alamofire/Alamofire.git", from: "5.4.1")
    ],
    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 this package depends on.
        .target(
            name: "Teams",
            dependencies: ["Alamofire"]),
        .testTarget(
            name: "TeamsTests",
            dependencies: ["Teams"]),
    ]
)

Once you've made your changes, recreate your Xcode project on the command line.

1
2
3
4
$ swift package generate-xcodeproj                                                                                                               [~/Documents/Projects/Personal/Teams 12:41]
generated: ./Teams.xcodeproj

$ open ./Teams.xcodeproj

If you check the Deployment Target, you can see that it has changed.

./images/figure_05.png

If you checked the results that added the dependencies, let's push the code to GitHub and actually incorporate it next.

Built into App project

Let's create an app project and incorporate your own Swift Pakcage. It doesn't matter which platform the app project uses, but I'm using tvOS (Apple TV). Also, select Swift UI to use it in the examples below.

Xcode has feature that add Swift Package. But since versioning isn't set in it, specify the branch as shown in the figure below.

./images/figure_06.png

When you create a Swift Package in Xcode, to fix a {package name}.swift in Sources/{package name}.
In the example, the package name is Teams, so it will be as follows.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
public struct Teams {
    public var text = "Hello, World!"
    
    public init(text: String) {
        self.text = text
    }
    
    public static func empty() -> Teams {
        return Teams(text: "")
    }
}

If you fixed it then push the code to GitHub.

Click File > Swift Packages > Update to Latest Package Versions to get the latest code in your app project.

./images/figure_07.png

Fix ContentView.swift in the app project.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
//
//  ContentView.swift
//

import SwiftUI
import Teams

struct ContentView: View {
    var body: some View {
        Text(Teams.init(text: "sample").text)
            .padding()
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

If app displayed 'sample' in, it's successful.

./images/figure_08.png

Conclusion

In this post, i introduced the flow that create Swift Package and publish it on GitHub. Next time, I' ll want to post an advanced edition that has relying on the C ++ library.

References