Transports in Tesseract can be implemented in Swift or Rust, and exported to Swift. If you have crossplatform transport (like WebSocket, WebRTC etc.) it's better to implement it once in Rust and export to the Swift. Apple unique transports can be implemented directly in Swift.
Client and Wallet transports are different, so we have different sets of protocols for them.
Client Transport should implement these protocols:
import TesseractTransportsClient // Client Transports SDK
// Main transport object
public protocol Transport: AnyObject, CoreConvertible<ClientTransport> {
// Transport ID. Unique string like 'ipc' for IPC transport
var id: String { get }
// Check if protocol supported through transport.
func status(proto: BlockchainProtocol) async -> Status
// Create new connection to the Wallet for provided protocol
func connect(proto: BlockchainProtocol) -> any Connection
}
// Wallet connection
public protocol Connection: AnyObject, CoreConvertible<ClientConnection> {
// Send data to the wallet
func send(request: Data) async -> Result<(), TesseractError>
// Receive response
func receive() async -> Result<Data, TesseractError>
}
Workflow:
- Tesseract checks if transport is working and supports needed protocol (wallet installed, bluetooth enabled, etc.) by calling
status
method. - Asks user to select transport based on their returned
Status
objects. - Calls
connect
method in selected transport to obtain newConnection
. - Calls
send
method inConnection
with request data and thenreceive
to get response.
For example of implementation you can check IPCTransportIOS.
Service transport should implement these protocols:
import TesseractTransportsService // Service Transports SDK
// Main Transport protocol
public protocol Transport: AnyObject, CoreConvertible<ServiceTransport> {
// Called when Tesseract starts transport.
// This is a place to start listening for requests
// Returns object, that will be dropped on Tesseract deallocation.
func bind(processor: TransportProcessor) -> any BoundTransport
}
// Helper object which will be dropped on Tesseract deallocation.
// Stop listening in this object deinit
public protocol BoundTransport: AnyObject, CoreConvertible<ServiceBoundTransport> {}
where TransportProcessor
is an object with one public method:
func process(data: Data) async -> Result<Data, TesseractError>
Worklow:
- In
bind
method transport should start listening for requests and store all needed context in its'BoundTransport
. - When data arrives, it should pass it to the
process
method of theTransportProcessor
instance, wait for response, and send it back to the client. - When
BoundTransport
is deallocated, transport stops listening and dropTransportProcessor
and context resources.
For example of implementation you can check IPCTransportIOS.
This documentation will focus on wrapping Rust transports to use them in Swift.
If you interested in implementing transports in Rust, check documentation in the Main Tesseract.rs repo.
To export client Transport
to the Swift client SDK you have to export constructor method from Rust.
#[no_mangle]
pub unsafe extern "C" fn tesseract_client_my_transport_new(
/* constructor parameters */
value: &mut ManuallyDrop<ClientTransport>, error: &mut ManuallyDrop<CError>
) -> bool {
TesseractSwiftError::context(|| {
let transport = MyTransport::new(/* constructor parameters */); // Initialize your transport
Ok(ClientTransport::new(transport)) // Convert it to the interop transport object
}).response(value, error)
}
On the Swift side SDK provides CoreTransport
helper. It will provide memory management and needed protocols.
Simply override it like this:
import TesseractTransportsClient
import CMyTransport // your header target
public final class MyTransport: CoreTransport {
public init(/* constructor parameters */) throws {
try super.init(TesseractError.self) { value, error in
tesseract_client_my_transport_new(/* constructor parameters */, value, error)
}
}
}
MyTransport
transport can be created and added to the client Tesseract
instance.
To export service Transport
to the Swift client SDK you have to export constructor method from Rust.
#[no_mangle]
pub unsafe extern "C" fn tesseract_service_my_transport_new(
/* constructor parameters */
value: &mut ManuallyDrop<ServiceTransport>, error: &mut ManuallyDrop<CError>
) -> bool {
TesseractSwiftError::context(|| {
let transport = MyTransport::new(/* constructor parameters */); // Initialize your transport
Ok(ServiceTransport::new(transport)) // Convert it to the interop transport object
}).response(value, error)
}
On the Swift side SDK provides CoreTransport
helper. It will provide memory management and needed protocols.
Simply override it like this:
import TesseractTransportsService
import CMyTransport // your header target
public final class MyTransport: CoreTransport {
public init(/* constructor parameters */) throws {
try super.init(TesseractError.self) { value, error in
tesseract_service_my_transport_new(/* constructor parameters */, value, error)
}
}
}
MyTransport
transport can be created and added to the service Tesseract
instance.