swift-package-manager

Exporting public C headers with SwiftPM


I have an open source Objective C framework, which I'd like to provide using Swift Package Manager as well.

Normally, I have public headers set in the Xcode project, which, when the framework is built, are copied under the framework bundle, and are discovered when linked to by Xcode.

I can't, however get it to work with SwiftPM.

I created a modulemap for my framework:

framework module LNPopupController {
    umbrella header "LNPopupController.h"
    export *
    module * { export * }
}

and I define the library like so in the Package.swift:

let package = Package(
    name: "LNPopupController",
    platforms: [
        .iOS(.v12),
        .macOS(.v10_15)
    ],
    products: [
        // Products define the executables and libraries a package produces, and make them visible to other packages.
        .library(
            name: "LNPopupController",
            type: .dynamic,
            targets: ["LNPopupController"]),
    ],
    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: "LNPopupController",
            dependencies: [],
            path: "LNPopupController",
            publicHeadersPath: ".",
            cSettings: [
                .headerSearchPath("."),
                .headerSearchPath("Private"),
            ]),
    ]
)

When added as a project dependency in Xcode, the framework compiles just fine, but when the dependent target attempts to import LNPopupController, an error is thrown: umbrella header 'LNPopupController.h' not found

Looking at the build folder, indeed, I see that Xcode has built a binary, but has not copied the public headers.

Any way to specify which headers are public, and make the build system copy them for import?


Solution

  • I eventually figured it out. This is what I did for LNPopupController:

    I created a include/LNPopupController/ sub-directory tree, and put soft links to all public headers there.

    In the package file, I added publicHeadersPath: "include".

    You can see the final result here: https://github.com/LeoNatan/LNPopupController/blob/master/Package.swift

    It might not be the most optimal way, but it works for all my ObjC projects which I have tried this method on.