How to wrap a C library in a swift package
There are a few great C/C++ libraries out there that you might want to use in your Swift application. Most of the time you'll find a wrapper already, but not all of the time. Or maybe you want something that is carefully tailored to your project needs.
Chris Eidhof from objc.io has a wrapper around the cmark library: CommonMark-Swift. Let's take that as an example and see how this can be achieved.
Create the library package
mkdir ~/CommonMark
cd ~/CommonMark
swift package init --type library
Before we can actually use the cmark library it needs to be installed. I use brew:
brew install cmark
Now let's edit the Package.swift
// swift-tools-version:5.3
import PackageDescription
let package = Package(
    name: "CommonMark",
    platforms: [
        .macOS("11")
    ],
    products: [
        .library(name: "CommonMark", targets: ["CommonMark"]),
        .library(name: "Ccmark", targets: ["Ccmark"])
    ],
    dependencies: [
    ],
    targets: [
        .target(name: "CommonMark", dependencies: [
            "Ccmark"
        ]),
        .systemLibrary(
            name: "Ccmark",
            pkgConfig: "libcmark",
            providers: [
                .brew(["commonmark"])
            ]),
        .testTarget(
            name: "CommonMarkTests",
            dependencies: ["CommonMark"]),
    ]
)
So we're building two products:
- CommonMark
- Ccmark
In the target section you can see that Ccmark is the name that we'll use for libcmark. The leading uppercase C seems to be a standard.
For this to work we need to create a directory Ccmark below our Sources directory and create the file: module.modulemap with the following content:
module Ccmark [system] {
    header "/usr/local/include/cmark.h"
    link "libcmark"
    export *
}
This tells the system where to find the header for libcmark and the library itself. The [system] attribute tells the compiler that cmark.h is a system header and more warnings will be ignored.
Voila! That's it. We can now use the cmark library in our Swift code.
Create a Swift API
Chris created another target: CommonMark that gives the user a nicer API to work with. Let's build a minimal version of that.
Edit Sources/CommonMark/CommonMark.swift like this:
import Foundation
import Ccmark // this wraps libcmark
public class Node {
    let node: OpaquePointer
    public init(_ node: OpaquePointer) {
        self.node = node
    }
    
    public init?(markdown: String) {
        guard let node = cmark_parse_document(markdown, markdown.utf8.count, CMARK_OPT_DEFAULT) else {
            return nil
        }
        self.node = node
    }
    deinit {
        guard type == CMARK_NODE_DOCUMENT else { return }
        cmark_node_free(node)
    }
    public var type: cmark_node_type {
        return cmark_node_get_type(node)
    }
    public var typeString: String {
        return String(cString: cmark_node_get_type_string(node))
    }
    public var children: [Node] {
        var result: [Node] = []
        
      	// cmark_node_first_child can return nil
        var child = cmark_node_first_child(node)
        while let unwrapped = child {
            result.append(Node(unwrapped))
            child = cmark_node_next(child)
        }
        return result
    }
}
This is pretty straight forward. The class Node encapsulates a pointer to the cmark node type. It gets initialized in the failable initializer through the call to the function cmark_parse_document. For this to work you need to import Ccmark.
If you want to read the documentation for libcmark you can open the man-page with
man 3 cmark. The3opens the library documentation as opposed to the implicit1which would open the cmark commands man page.
This will not compile yet, because of the testExample test. Let's create a useful test:
import XCTest
@testable import CommonMark
final class CommonMarkTests: XCTestCase {
    func testCaption() {
        let markdown = "# Caption"
        let node = Node(markdown: markdown)!
        let heading = node.children.first
        XCTAssertEqual(heading?.typeString, "heading")
    }
}
Include the library with your app
If you want to include the dylib  with your app bundle you can create a Frameworks subdirectory below Contents and copy the library there. You can tell your app where it can find the library with the following command:
install_name_tool -change /usr/local/opt/cmark/lib/libcmark.0.29.0.dylib "@executable_path/../Frameworks/libcmark.dylib" ./<YourApp>.app/Contents/MacOS/Scratched
You can find out the the standard path of the library with the following command:
otool -L /usr/local/lib/libcmark.dylib