147

Is there a way to call C routines from Swift?

A lot of iOS / Apple libraries are C only and I'd still like to be able to call those.

For example, I'd like to be able to call the objc runtime libraries from swift.

In particular, how do you bridge iOS C headers?

Bijan
  • 25,559
  • 8
  • 79
  • 71
Blaze
  • 1,530
  • 2
  • 15
  • 24

6 Answers6

114

Yes, you can of course interact with Apple's C libraries. Here is explained how.

Basically, the C types, C pointers, etc., are translated into Swift objects, for example a C int in Swift is a CInt.

I've built a tiny example, for another question, which can be used as a little explanation, on how to bridge between C and Swift:

main.swift

import Foundation

var output: CInt = 0
getInput(&output)

println(output)

UserInput.c

#include <stdio.h>

void getInput(int *output) {
    scanf("%i", output);
}

cliinput-Bridging-Header.h

void getInput(int *output);

Here is the original answer.

Adrian Mole
  • 49,934
  • 160
  • 51
  • 83
Leandros
  • 16,805
  • 9
  • 69
  • 108
  • 1
    I am new to ios/swift. I would like to use logging c-function from #include in swift files. Anyone? – Dmitry Konovalov Nov 01 '14 at 00:30
  • 1
    is it compatible with C++, .cpp files?? – Carlos.V Nov 12 '16 at 03:29
  • 1
    To use C++ files directly, you should create an objective-C wrapper. You'll want the implementation (which should reside by itself in a .mm file) to contain Objective-C++ code (which is just Objective-C and C++ in one file), and the interface (which should be in its own .h header file) should contain pure Objective-C code, so you'll have to convert C++ types to Objective-C types in the implementation, to expose them to Swift. Then you can import that header with an objective-c bridging header. – William T Froggard Dec 06 '16 at 01:23
  • 2
    _“you can of course interact with Apples C libraries”_  Incorrect.  You can interact with __any__ C libraries, not at all limited limited to only Apple's. – Slipp D. Thompson Mar 20 '17 at 08:32
  • Updated documentation link https://developer.apple.com/documentation/swift/imported_c_and_objective-c_apis – MechEthan Jun 11 '20 at 23:13
9

The compiler converts C API to Swift just like it does for Objective-C.

import Cocoa

let frame = CGRect(x: 10, y: 10, width: 100, height: 100)

import Darwin

for _ in 1..10 {
    println(rand() % 100)
}

See Interacting with Objective-C APIs in the docs.

rickster
  • 124,678
  • 26
  • 272
  • 326
  • 1
    Thanks for the example, and you're right, I can see how that might work. However it doesn't really answer my question, which was how to call apple C libraries. But it's definitely closest. – Blaze Jun 02 '14 at 23:27
  • Look elsewhere in that document. For example, CoreFoundation-style "objects" (`CFTypeRef`) are converted to Swift objects. Most of the ObjCRuntime.h functions aren't meaningful to Swift, though. – rickster Jun 02 '14 at 23:29
  • "Most of the ObjCRuntime.h functions aren't meaningful to Swift, though" Why do you say that? I think I need to find some way import a header and bridge it. That seems awkward, but I suppose it's the way to go. – Blaze Jun 02 '14 at 23:34
7

Just in case you're as new to XCode as me and want to try the snippets posted in Leandro's answer:

  1. File->New->Project
  2. choose Command Line Tool as a project preset and name the project "cliinput"
  3. right-click in the project navigator (the blue panel on the left) and choose "New File..."
  4. In the drop down dialog name the file "UserInput". Uncheck the box "Also create a header file". Once you click "Next" you will be asked if XCode should create the Bridging-Header.h file for you. Choose "Yes".
  5. Copy & paste the code from Leandro's answer above. Once you click on the play button it should compile and run in the terminal, which in xcode is built-in in the bottom panel. If you enter a number in the terminal, a number will be returned.
Community
  • 1
  • 1
lukas83
  • 441
  • 5
  • 9
4

This post also has a good explanation regarding how to do this using clang's module support.

It's framed in terms of how to do this for the CommonCrypto project, but in general it should work for any other C library you want to use from within Swift.

I briefly experimented with doing this for zlib. I created a new iOS framework project and created a directory zlib, containing a module.modulemap file with the following:

module zlib [system] [extern_c] {
    header "/Applications/Xcode.app/Contents/Developer/Platforms/iPhoneSimulator.platform/Developer/SDKs/iPhoneSimulator.sdk/usr/include/zlib.h"
    export *
}

Then under Targets -> Link Binary With Libraries I selected add items and added libz.tbd.

You may want to build at this point.

I was then able to write the following code:

import zlib

public class Zlib {
    public class func zlibCompileFlags() -> UInt {
        return zlib.zlibCompileFlags()
    }
}

You don't have to put the zlib library name in front, except in the above case I named the Swift class func the same as the C function, and without the qualification the Swift func ends up being called repeatedly until the application halts.

Community
  • 1
  • 1
Julian
  • 2,837
  • 17
  • 15
3

In the case of c++, there is this error that pops up:

  "_getInput", referenced from: 

You need a c++ header file too. Add c-linkage to your function, then include the header file in the bridge-header:

Swift 3

UserInput.h

#ifndef USERINPUT_H
#define USERINPUT_H    

#ifdef __cplusplus
extern "C"{
#endif

getInput(int *output);

#ifdef __cplusplus
}
#endif

UserInput.c

#include <stdio.h>

void getInput(int *output) {
    scanf("%i", output);
}    

main.swift

import Foundation
var output: CInt = 0
getInput(&output)
print(output)

cliinput-Bridging-Header.h

#include "UserInput.h"

Here is the original video explaining this

John James
  • 83
  • 6
  • if it's not work, try to add `__OBJC` check to your Bridging-Header, eg `#ifdef __OBJC @import UIKit; #endif` – Chris Yim Aug 31 '19 at 02:50
1

It appears to be a rather different ball 'o wax when dealing with pointers. Here's what I have so far for calling the C POSIX read system call:

enum FileReadableStreamError : Error {
case failedOnRead
}

// Some help from: http://stackoverflow.com/questions/38983277/how-to-get-bytes-out-of-an-unsafemutablerawpointer
// and https://gist.github.com/kirsteins/6d6e96380db677169831
override func readBytes(size:UInt32) throws -> [UInt8]? {
    guard let unsafeMutableRawPointer = malloc(Int(size)) else {
        return nil
    }

    let numberBytesRead = read(fd, unsafeMutableRawPointer, Int(size))

    if numberBytesRead < 0 {
        free(unsafeMutableRawPointer)
        throw FileReadableStreamError.failedOnRead
    }

    if numberBytesRead == 0 {
        free(unsafeMutableRawPointer)
        return nil
    }

    let unsafeBufferPointer = UnsafeBufferPointer(start: unsafeMutableRawPointer.assumingMemoryBound(to: UInt8.self), count: numberBytesRead)

    let results = Array<UInt8>(unsafeBufferPointer)
    free(unsafeMutableRawPointer)

    return results
}
Chris Prince
  • 7,288
  • 2
  • 48
  • 66