MacOS/IOS swift项目调用Rust库

940 阅读4分钟

本文主题是IOS使用Rust库。其实C/C++库操作类似,本文前半部分我将描述怎么把Rust library编译为静态/动态连接库,后半部分是怎么使用这个库。

同样的,Rust编译的库同样适用于其他平台的项目比如Android、MSVC等。

一、准备工作

我假设你已经有Xcode和Rust环境,那么还需要:

  • 目标平台Rust工具链

  • C/C++ 头文件生成器

  • IOS库编译工具

    Rust工具链安装

    rustup target add x86_64-apple-ios aarch64-apple-ios
    // 其他工具链,可以根据需求安装
    rustup target list
    

    三方工具安装

    cargo install lipo
    cargo install cbindgen
    

二、制作FFI静态库

为什么要静态链接?IOS对依赖库限制比较多,其他平台需要解决依赖关系,但如果你的App不是对构建物大小有限制的话这就是不是问题,当然这也不是本篇讨论的重点。

下面我们以开源项目google-authenticator-rust为例子。

1.编写导出C风格函数

github.com/hanskorg/go…

 // 返回C风格字符串
 #[cfg(feature = "clib")]
 #[no_mangle]
 pub extern "C" fn create_secret(len: u8) -> *const c_char {
     CString::new(GA_AUTH.create_secret(len))
         .expect("can't make secret.")
         .into_raw()
 }

#[no_mangle] 控制编译器 不要破坏C函数名,确保外部能通过 create_secret 调用到这个函数。 #[cfg(feature = "clib")] 条件编译参数,编译库时通过增加features参数确定是否编译为C/C++库。

2.编写Rust内存释放相关代码

github.com/hanskorg/go…

/// # Safety
/// A function that can be used for free returnd to C string
/// `str`, the string which be passed to outside
#[cfg(feature = "clib")]
#[no_mangle]
pub unsafe extern "C" fn free_str(str: *mut c_char) {
    unsafe {
        let _ = CString::from_raw(str);
    }
}
3.导出C/C++ Header

这个需要注意的是,默认导出的C Header file, 需要C++ header,需要配置cbindgen.tmol

导出头文件到 src/authenticator.h

cbindgen ./ -l c --output src/authenticator.h
4. 编译静态库

配置Cargo.toml

#修改[lib]
[lib]
name="authenticator"
crate-type = ["staticlib", "cdylib"] #同时编译静态和动态库
required-features = ["with-qrcode","clib"]

编译目标平台lib

# 这里需要说明的是,feature 可以根据需要增减targets, 案例中增加x86完全是为了在x86平台调试
cargo lipo --all-features --release --targets x86_64-apple-ios aarch64-apple-ios
# 编译结果文件目录如下,`.a`就是静态库
target
├── aarch64-apple-ios
│   └── build
│      ├── libauthenticator.a
│      └── libauthenticator.dylib
│
├── universal
│   └── release
└── x86_64-apple-ios
    └── release
        ├── libauthenticator.a
        └── libauthenticator.dylib

当然可以把x86和arm64两个平台库合并为一个库,合并也是测试目的,并非必要步骤,仅为x86平台调试作用。

lipo -create target/x86_64-apple-ios/release/authenticator.lib \
             target/aarch64-apple-ios/release/libauthenticator.a \
             -output libauthenticator.a

至此已经编译好库文件,如果你是Apple生态项目大拿或者只需要知道怎么编译Rust库就可以忽略以下内容。 下面我们将通过一个MacOS App案例介绍怎么使用静态库。

三、IOS/Mac Project 使用C库

这个案例以Swift项目为例,一步一步介绍如何使用C library

1. 添加C Library到项目

可以拖动刚才编译好的libauthenticator.a到项目,在General->Frameworks,Libraries...->Add Other添加。 添加libresolv.tdb到项目,可以直接在 General->Frameworks,Libraries...中搜索到。 xcode-project-add-lib.png

2. 添加C Header到项目

xcode Add Files To '{project}' xcode-project-add-header.png

3. 编写Objective-C Bridge Header

xcode File -> New -> File..., 选择Header File并命名为 Authenticator-Bridging-Header.h。

xcode-project-add-ocbridege.png

4. 配置项目路径

xcode TARGETS Authenticator, Build Setting

Objective-C Bridging Header增加 $(PROJECT_DIR)/Authenticator/Authenticator-Bridging-Header.h

Search Paths -> Header Search Paths 增加 $(PROJECT_DIR)/Authenticator

Search Paths -> Library Search Paths 增加 $(PROJECT_DIR)/Authenticator

修改为如下,请注意修改自己的目录 xcode-project-add-searchpaths.png

5. OC Bridging Header 引入C header

Authenticator-Bridging-Header.h

#ifndef Authenticator_Bridging_Header_h
#define Authenticator_Bridging_Header_h

#define DEFINE_QRCODE
#import "authenticator.h"

#endif /* Authenticator_Bridging_Header_h */

5. 编写swift调用类

注意这里有个要点,Rust返回的字符串需要归还Rust释放内存,其他语言同理。

free_str(UnsafeMutablePointer(mutating: encodeCode))

Authenticator.swift


import Foundation

class Authenticator {

    func getCode(secret:String) -> String {
        let encodeCode = get_code(secret, UInt64(NSDate().timeIntervalSince1970 / 30))
        let code = String(cString: encodeCode!)
        free_str(UnsafeMutablePointer(mutating: encodeCode))
        return code;
    }
}

至此,Swift项目调用Rust库已经配置完成。如果配置过程中遇到问题或者其他有疑惑的地方可以参照项目


更多参照

原文发布于: notes.icool.io/rust-ios-ma…