Skip to content

Latest commit

 

History

History
338 lines (267 loc) · 9.55 KB

RUST.MD

File metadata and controls

338 lines (267 loc) · 9.55 KB

Tesseract

Prerequisites

Following rust targets must be installed:

rustup +nightly target add aarch64-apple-ios aarch64-apple-ios-sim x86_64-apple-ios

Adding Rust to your iOS App

Create a new iOS application or open your existing project.

Copy build script

Copy Xcode build script from Examples/Rust/xcode_build_step.sh into your project (in this guide we assume that it is copied to the sources root folder).

Modify your target settings in the Xcode

  • Add Run Script phase to your target
  • Move it before Build Sources phase
  • Change script to exec bash ${SRCROOT}/xcode_build_step.sh CApp app. Where CApp is a name for the exported Swift module and app is a name of the rust library target in the Cargo.toml.

Swift Package

  • Add Package Dependency to the project: https://github.com/tessetact-one/Tesseract.swift.
  • Add TesseractTransportsClient library dependency for application target.

Create a rust library

  • cargo new app --lib
  • Add the dependencies:
[dependencies]
tesseract-swift = { version = "0.5", features = ["client"] }
tesseract-one = { version = "0.5", features = ["client"] }
tesseract-protocol-test = { version = "0.5", features = ["client"] }

[build-dependencies]
cbindgen = "0.26"
  • specify the type of library
[lib]
crate-type = ["staticlib"]
  • create build.rs file for C headers generation
extern crate cbindgen;

use std::env;
use std::path::Path;

fn main() {
    let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let profile = env::var("PROFILE").unwrap();
    let name = env::var("CARGO_PKG_NAME").unwrap();
    let header_path = Path::new(&crate_dir)
        .join("target")
        .join(&profile)
        .join("include")
        .join(format!("{}.h", name));

    cbindgen::generate(&crate_dir)
        .expect("Unable to generate bindings")
        .write_to_file(&header_path);
}
  • Create cbindgen.toml file with this contents
include_version = true
autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
tab_width = 2
language = "C"
documentation_style = "doxy"
pragma_once = true
cpp_compat = true
after_includes = "@import CTesseractShared;"

[enum]
prefix_with_name = true

[parse]
parse_deps = true
include = ["tesseract-swift", "tesseract-swift-utils", "tesseract-swift-transport"]

[export]
exclude = [
  "CFutureString", "SyncPtr_Void", "ClientTransport", "Void", 
  "CError", "CString", "CStringRef", "CAnyDropPtr"
]

Now you can export C functions, initialize Tesseract and start calling the Wallet.

Initialize App and Tesseract in Rust

Create application initialization function in Rust. Initialize Tesseract in it

use tesseract_one;
use tesseract_protocol_test;

use tesseract_swift::error::TesseractSwiftError;
use tesseract_swift::client::transport::*;
use tesseract_swift::utils::{error::CError, ptr::SyncPtr, Void};

use std::mem::ManuallyDrop;
use std::sync::Arc;

// Your application context. Put all needed data here.
// Rust only structure
struct AppContext {
    service: Arc<dyn tesseract_one::client::Service<Protocol = tesseract_protocol_test::Test>>,
}

// Type-erased App Context pointer for Swift.
#[repr(C)]
pub struct AppContextPtr(SyncPtr<Void>);

// Helper methods for pointer
impl AppContextPtr {
    fn new(ctx: AppContext) -> Self {
        Self(SyncPtr::new(ctx).as_void())
    }

    unsafe fn unowned(&self) -> &AppContext {
        self.0.as_typed_ref().unwrap()
    }

    unsafe fn owned(&mut self) -> AppContext {
        self.0.take_typed()
    }
}

// App init method
#[no_mangle]
pub unsafe extern "C" fn app_init(
    alerts: AlertsDelegate, transport: ClientTransport,
    value: &mut ManuallyDrop<AppContextPtr>, 
    error: &mut ManuallyDrop<CError>
) -> bool {
  TesseractSwiftError::context(|| {
        let tesseract = Tesseract::new(TransportDelegate::arc(alerts))
                .transport(transport);

        let service = tesseract.service(tesseract_protocol_test::Test::Protocol);

        let context = AppContext { service };

        Ok(AppContextPtr::new(context))
    }).response(value, error)
}

Call initialization function in Swift

import TesseractTransportsClient
import CApp

let rustApp = try! TResult<AppContextPtr>.wrap { value, error in
    app_init(alerts.toCore(), IPCTransportIOS().toCore(), value, error)
}.get()

Usage

The rest of Tesseract APIs stay exacly the same everywhere. Please, consider to go through the docs in our Tesseract shared Core repo.

Adding Rust to your iOS Wallet

  1. Create a new iOS application or open your existing project.
  2. Add Action Extension target to the project.
  3. Configure Action Extension target: Wallet Docs

Copy build script

Copy Xcode build script from Examples/Rust/xcode_build_step.sh into your project (in this guide we assume that it is copied to the sources root folder).

Modify your targets settings in the Xcode

  • Add Run Script phase to your Wallet and Extension targets
  • Move it before Build Sources phase
  • Change script to exec bash ${SRCROOT}/xcode_build_step.sh CWallet wallet. Where CWallet is a name for the exported Swift module and wallet is a name of the rust library target in the Cargo.toml.

Swift Package

  • Add Package Dependency to the project: https://github.com/tessetact-one/Tesseract.swift.
  • Add TesseractTransportsService library dependency for Extension target.

Create a rust library

  • cargo new wallet --lib
  • Add the dependencies:
[dependencies]
tesseract-swift = { version = "0.5", features = ["service"] }
tesseract-one = { version = "0.5", features = ["service"] }
tesseract-protocol-test = { version = "0.5", features = ["service"] }

[build-dependencies]
cbindgen = "0.26"
  • specify the type of library
[lib]
crate-type = ["staticlib"]
  • create build.rs file for C headers generation
extern crate cbindgen;

use std::env;
use std::path::Path;

fn main() {
    let crate_dir = env::var("CARGO_MANIFEST_DIR").unwrap();
    let profile = env::var("PROFILE").unwrap();
    let name = env::var("CARGO_PKG_NAME").unwrap();
    let header_path = Path::new(&crate_dir)
        .join("target")
        .join(&profile)
        .join("include")
        .join(format!("{}.h", name));

    cbindgen::generate(&crate_dir)
        .expect("Unable to generate bindings")
        .write_to_file(&header_path);
}
  • Create cbindgen.toml file with this contents
include_version = true
autogen_warning = "/* Warning, this file is autogenerated by cbindgen. Don't modify this manually. */"
tab_width = 2
language = "C"
documentation_style = "doxy"
pragma_once = true
cpp_compat = true
after_includes = "@import CTesseractShared;"

[enum]
prefix_with_name = true

[parse]
parse_deps = true
include = ["tesseract-swift", "tesseract-swift-utils", "tesseract-swift-transport"]

[export]
exclude = [
  "CFutureBool", "SyncPtr_Void", "ServiceBoundTransport", "ServiceTransport",
  "ServiceTransportProcessor", "Void", "CError", "CString", "CStringRef", 
  "CAnyDropPtr"
]

Now you can export C functions, initialize Tesseract and wait accept request.

Initialize Extension and Tesseract in Rust

Create Extension initialization function in Rust. Initialize Tesseract in it

use tesseract_one::service::Tesseract;

use tesseract_swift::error::TesseractSwiftError;
use tesseract_swift::service::transport::ServiceTransport;
use tesseract_swift::utils::{error::CError, ptr::SyncPtr, Void};

use std::mem::ManuallyDrop;
use std::sync::Arc;

// Your Extension application context. Put all needed data here.
// Rust only structure
struct ExtensionContext {
    _tesseract: Tesseract
}

// Type-erased ExtensionContext pointer for Swift.
#[repr(C)]
pub struct ExtensionContextPtr(SyncPtr<Void>);

// Helper methods for pointer
impl ExtensionContextPtr {
    fn new(ctx: ExtensionContext) -> Self {
        Self(SyncPtr::new(ctx).as_void())
    }

    unsafe fn unowned(&self) -> &ExtensionContext {
        self.0.as_typed_ref().unwrap()
    }

    unsafe fn owned(&mut self) -> ExtensionContext {
        self.0.take_typed()
    }
}

// Wallet extension init method
#[no_mangle]
pub unsafe extern "C" fn wallet_extension_init(
    signature: CStringRef, ui: UI, transport: ServiceTransport,
    value: &mut ManuallyDrop<ExtensionContextPtr>, error: &mut ManuallyDrop<CError>
) -> bool {
    TesseractSwiftError::context(|| {
        let service = TestService::new(ui, signature.try_as_ref()?.into());

        let tesseract = Tesseract::new().transport(transport).service(service);

        let context = ExtensionContext {
            _tesseract: tesseract,
        };

        Ok(ExtensionContextPtr::new(context))
    }).response(value, error)
}

Call initialization function in Swift

import TesseractTransportsService
import CWallet

let transport = IPCTransportIOS(self).toCore()
let signature = "test"

let rustApp = try! TResult<ExtensionContextPtr>.wrap { value, error in
    wallet_extension_init(signature,
                          NativeUI(delegate: self).toCore(),
                          transport, value, error)
}.get()

Usage

The rest of Tesseract APIs stay exacly the same everywhere. Please, consider to go through the docs in our Tesseract shared Core repo.