Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to call a Swift function in Rust?

I have a function written in Swift that I want to call from Rust. I've tried exposing it through Objective-C, however I continue to get errors from ld saying it can't find _foo. The Rust project is linked to the Swift project by compiling the Rust project as a staticlib.

foo.h

#pragma once

#include <stdint.h>

uint8_t foo_bridge(uint8_t);

foo.m

#import <Foundation/Foundation.h>
#import <Foo-Swift.h>

uint8_t foo_bridge(uint8_t byte) {
    return foo(byte);
}

bar.swift

public func foo(byte: UInt8) -> UInt8 {
    return byte * 2
}

src/lib.rs

extern "C" {
    pub fn foo_bridge(byte: u8) -> u8;
}

Bar-Bridging-Header.h

#import "foo.h"

Cargo.toml

[package]
name = "foo"
version = "0.1.0"

[lib]
name = "foo"
crate-type = ["staticlib"]
like image 414
XAMPPRocky Avatar asked Mar 20 '17 12:03

XAMPPRocky


1 Answers

The issue here is attempting to call a bare Swift function from Objective-C, which isn't supported.

If you check the Foo-Swift.h header you'll find that the foo() function isn't present, which indicates that the symbol for it, _foo, won't be available at runtime either.

The solution is to put the foo() function inside something that can be called from Objective-C, like a class deriving from NSObject:

class Foo: NSObject {
    class func foo(byte: UInt8) -> UInt8 {
        return byte * 2
    }
}

Once you do that, you'll find the Foo-Swift.h header now has an interface for it:

@interface Foo : NSObject
+ (uint8_t)fooWithByte:(uint8_t)byte;
- (nonnull instancetype)init OBJC_DESIGNATED_INITIALIZER;
@end

Since we know it's available from Objective-C now, we can call it like this:

uint8_t foo_bridge(uint8_t byte) {
    return [Foo fooWithByte:byte];
}
like image 101
steveatinfincia Avatar answered Nov 07 '22 20:11

steveatinfincia