<p align="left"> <a href="http://tesseract.one/"> <img alt="Tesseract" src ="./.github/logo-horiz.svg" height=128/> </a> </p> # Prerequisites * Install your Rust nightly environment: https://www.rust-lang.org/tools/install * Install Xcode from the App Store. * For Rust we suggest VS Code: https://code.visualstudio.com/ Following rust targets must be installed: ```bash 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: ```toml [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 ```toml [lib] crate-type = ["staticlib"] ``` * create `build.rs` file for C headers generation ```rust 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 ```toml 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 ```rust 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 ```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](https://github.com/tesseract-one/Tesseract.rs) 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](./WALLET.MD) ## 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: ```toml [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 ```toml [lib] crate-type = ["staticlib"] ``` * create `build.rs` file for C headers generation ```rust 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 ```toml 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 ```rust 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 ```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](https://github.com/tesseract-one/Tesseract.rs) repo.