Swift Package Manager Logo

Implementing Swift Package Manager –Example 3

This entry is part 9 of 14 in the series Swift Package Manager

Simple Nested Libraries, With the Top One Being Dynamic

In this example, the PackageConsumer utility simply consumes Package_C, Version 1.0.0, which is a dynamic library, that, in turn, consumes Package_A Version 1.0.0 (a static library).

Here is the GitHub link to the PackageConsumer project Version 3.0.0. You can check out that version, and reproduce the results here.

Fig. 1: The Hierarchy

The reason for the red line and the “NO” symbol, is because there are some issues that can arise, when running in Xcode, due to the Hardened Runtime setting (more on that, below).

Here is the source code for the Package_C.swift file, Version 1.0.0:

import Foundation
import Package_A

public struct Package_C: PackageProtocol {
    public let indent: Int
    public let text: String
    public init(indent inIndent: Int = 0) {
        indent = inIndent
        let prefix = String(repeating: "\t", count: inIndent)
        text =  "\(prefix)Package_C, Version: 1.0.0\n" + Package_A(indent: inIndent + 1).text
    }
}

Here is the source for the Package_C Package.swift file:

import PackageDescription

let package = Package(
    name: "Package_C",
    platforms: [
        .iOS(.v11),
        .tvOS(.v11),
        .macOS(.v10_14),
        .watchOS(.v5)
    ],
    products: [
        .library(
            name: "Package-C",
            type: .dynamic,
            targets: ["Package_C"])
    ],
    dependencies: [
        .package(name: "Package_A", url: "git@github.com:LittleGreenViper/SPMArticle-Package_A.git", from: "1.0.0")
    ],
    targets: [
        .target(
            name: "Package_C",
            dependencies: [
                .product(name: "Package-A", package: "Package_A")
            ]
        ),
        .testTarget(
            name: "Package_CTests",
            dependencies: [
                "Package_C"
            ]
        )
    ]
)

Note the “type: .dynamic“, in the products list. That tells the build system to create the library as a dylib/framework.

Here is the source code for Package_A, version 1.0.0:

import Foundation

public protocol PackageProtocol {
    var text: String { get }
    var indent: Int { get }
}

public struct Package_A: PackageProtocol {
    public let indent: Int
    public let text: String
    public init(indent inIndent: Int = 0) {
        indent = inIndent
        let prefix = String(repeating: "\t", count: inIndent)
        text =  "\(prefix)Package_A, Version: 1.0.0"
    }
}

Here is the source for the PackageConsumer Version 3.0.0 file:

import Foundation
import Package_C

struct PackageConsumer {
    let text: String
    init(text intext: String = "PackageConsumer, Version 3.0.0") {
        text = intext + "\n" + Package_C(indent: 1).text
    }
}

Running that, gives us something like this in the console:

PackageConsumer, Version 3.0.0
	Package_C, Version: 1.0.0
		Package_A, Version: 1.0.0

An Important Note About Running This In Xcode

Because this imports a dynamic library, the library needs to be embedded, which means that it also needs to be signed.

If we have Hardened Runtime on, then the execution will fail:

Fig. 2: Hardened Runtime Failure

If we have it off, the execution will work:

Fig. 3: Hardened Runtime Off

This does not happen when we use swift build. I suspect that’s because swift build does not enable Hardened Runtime by default.