Calling Methods from a C-Module in a Swift Project

Adonis Gaitatzis
4 min readOct 29, 2021

This article will show you how to call functions from a C module in Swift, with the module source written in GoLang.

The Module

The module can be written in any language, so long as it’s compiled into binary. To make things interesting, let’s say we have a GoLang program that will be compiled using cgo into an iOS module.

This GoLang code exports a sayHello() method, which returns the c-string hello :

package mainimport "C"func main() {
// main() won't be called, but it is required for compilation
}
//export sayHello
func sayHello() *C.char {
return C.CString("Hello")
}

This file and how to compile it as an iOS module are described in Compile GoLang as a Mobile Library, but what’s important for this tutorial is that the sayHello() method is exposed through the resulting sayhello.h.

We can therefore call this method from Swift with a little work.

The Procedure

The basic procedure here is to:

  1. Create an Xcode Project
  2. Add the module and header to the project
  3. Create an Objective-C bridging header
  4. Call the module’s functions

Let’s see it in action.

1. Create a New Xcode Project

The first step of course is to create a new Xcode project. Although this could be any sort of project, for this tutorial we will be creating a mobile app.

Create new App

2. Add the Module and Header To The Project

Drag and drop the module and the associated header (.h) file into the project folder, so that the module is available and the header can be found by the compiler.

Add module and header file

You can copy the references or not, whatever works for your project.

3. Create an Objective-C Bridging Header

Create a new Objective-C file, name it <project-name>-Bridging-Header.h

Create an Objective-C Bridging Header file

In your new header file, include the module header you will be interacting with:

#include "sayhello.h"

This will allow Swift to talk to a C header, through the Objective-C bridge.

4. Call the module’s functions

Let’s say we create a simple UI featuring a button which triggers the module’s function, and a label which displays the return value.

UI For Demo App

We can code this in the ContentView.swift like this:

import SwiftUIstruct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
@State var sayHelloResponse = "" var body: some View {
Button(action: onClick) {
Text("Say Hello")
}
Text("Response from module: \(sayHelloResponse)")
}
private func onClick() {
// do something here
}
}

Let’s look at how to call the sayHello() function in the onClick() method.

The sayHello() function returns a c-string. Since Swift doesn’t use c-strings natively, it could cause problems if the method doesn’t return properly or doesn’t terminate. Therefore, we must create a guard around it and exit the method if there’s a failure:

guard let cstr = sayHello() else { return }

Since Swift uses it’s own String type, we should convert cstr to a String. To do that, we need to get the c-string length using strlen(), encode as .utf8 , and free the variable for garbage collection when done:

let str = String(
bytesNoCopy: cstr,
length: strlen(cstr),
encoding: .utf8,
freeWhenDone: true
)

Finally, we can store the resulting str into our @State var sayHelloResponse variable. But just in case there was a problem converting cstr to a String , we need to include a fallback string, in this case "(Failed)" :

sayHelloResponse = str ?? "(Failed)"

Putting it all together, we get this method:

  private func onClick() {
guard
let cstr = sayHello() else { return }
let str = String(
bytesNoCopy: cstr,
length: strlen(cstr),
encoding: .utf8,
freeWhenDone: true
)
sayHelloResponse = str ?? "(Failed)"
// do something here
}

And when we run the program and click the “Say Hello” button, the Text label displays the response from the module:

The module works

That’s it! Now you have executed code from an imported module in your iOS project.

Further Reading

There’s another fantastic tutorial related to this one, but using gomobile instead of cgo: Calling Go code from Swift on iOS and vice versa with Gomobile

--

--

Adonis Gaitatzis

Is a technology and branding nerd who can synthesize any business need into a technology solution. Fun projects have included brain imaging tech & mesh routers.