But some of my best friends are C

So I have a goal for a Swift mastodon bot: post an generated image per dayish while running on a Linux (explicitly Ubuntu) box.

So if I was going to run tipsy robot on MacOS I’d be done by now. There are a lot of really useful types for working with image information (UIImage, CGImage, CVBuffer, Metal). None of these work on Linux.

There are libraries for creating noise in GameKit, also not available on Linux.

If I was interested in using other people libraries, I would also be done by now. I am clearly not the first one to run into this problem. In fact I learned a lot from looking at a package that I might ultimately use, the HackingWithSwift GD wrapper.

But since this is more of a research project I’m going to see about writing it all myself.

Before I can write a file, I need to have the buffer of random pixel info to put into it. My favorite noise function all are written in C. What is a Swift programer to do?

It turns out it is pretty easy to either:

Embed the Code

I followed the tutorial here:

to make make the following almost empty sample project

To flesh it out a little the steps to add C to an existing project are:

  1. File > NewFile, Choose C type
  2. Name the file what you want. DO NOT include .c in the same.
  3. DO tick the box let Xcode make the header file.
  4. If is the first C file added, X-code will ask if you want to add a bridging header. Say yes.
  5. 3 new files will be in the Xcode project
    • your_name.c
    • your_name.h
    • ProjectName-Bridging-Header.h
  6. Add your_name.h to the bridging header.
  7. Add code, call it directly from a .swift file, celebrate.

Trouble shooting

Build input file cannot be found: 'File/Path/ProjectName-Bridging-Header.h'. Did you forget to declare this file as an output of a script phase or custom build rule which produces it?

If you make the Bridging-Header by hand XCode can’t always find it. Try moving it to the root of the project. If that works you can either leave it and move on … OR

  1. open Project Settings
  2. Select Target, if necessary
  3. Open Build Settings
  4. Filter on “Bridging” and a setting with the name “Objective-C Bridging Header” comes up.
  5. Make sure the text in this field is the correct path relative to your project’s root. So either ProjectName-Bridging-Header.h if you want it in the root, or ProjectName/ProjectName-Bridging-Header.h if the project directory is where you want it.

There is a function that is defined twice. Did you swap the content for the .c and the .h files when you pasted the code in if bringing it form somewhere else? Ask me how I know. I mean don’t. Really.

A little more about the Bridging Header’s

The bridging header is where XCode looks for the functions you want to make available to the rest of the project. You can just add all the function, or have separate headers for functions that only the C/Objective-C can use and ones to expose to Swift. It does NOT appear that C++ headers are directly usable (yet?), but C++ can be wrapped in Objective-C or C for use.

Put Code in a Package

My preferred approach is to put the C code into a package, paired with some Swift code to make accessing it easier.

There is an example project, NoiseMaker, that compiles but doesn’t actually do anything useful yet.

The default behavior of the swift compiler is to look for the headers in a folder called include. This is changeable? sort of? but honestly looks like a real pain so I’m just going to go with the Swifty Way.

References

Directory Structure

->Sources
 ->C
  ->include
---- my_name.h
--- my_name.c
 ->Swift
--- MyWrapper.swift

Package File

// swift-tools-version: 5.7
// The swift-tools-version declares the minimum version of Swift required to build this package.

import PackageDescription

let package = Package(
    name: "NoiseMaker",
    products: [
        // Products define the executables and libraries a package produces, and make them visible to other packages.
        .library(
            name: "NoiseMaker",
            targets: ["CNoiseMaker","NoiseMaker"]),
    ],
    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: "CNoiseMaker",
            path: "Sources/C"
            ),
        .target(
            name: "NoiseMaker",
            dependencies: ["CNoiseMaker"],
            path: "Sources/Swift"
        )
    ]
)