or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

bridge-integration.mdconfiguration.mdindex.mdmethod-calls.mdnotifications.mdplugin-development.md
tile.json

method-calls.mddocs/

Method Calls & Type Safety

Type-safe JavaScript-to-native method invocation with structured parameter access and response handling.

Capabilities

CAPPluginCall

Core class representing method calls from JavaScript with type-safe parameter access and structured response methods.

/**
 * Represents a method call from JavaScript with parameter access and response handling
 */
@objc(CAPPluginCall)
public class CAPPluginCall: NSObject {
    
    // MARK: - Core Properties
    
    /** Unique identifier for this call */
    public let callbackId: String
    
    /** Name of the method being called */
    public let methodName: String
    
    /** Dictionary containing call parameters from JavaScript */
    public let options: NSDictionary
    
    /** Whether this call should be retained after completion (for callbacks/events) */
    public var keepAlive: Bool
    
    /** Whether this call has been saved to the bridge */
    public var isSaved: Bool { get }
    
    // MARK: - Response Methods
    
    /**
     * Resolve the call with no data
     */
    public func resolve()
    
    /**
     * Resolve the call with response data
     * @param data Response data to send to JavaScript
     */
    public func resolve(_ data: PluginCallResultData?)
    
    /**
     * Reject the call with an error message
     * @param message Error message
     */
    public func reject(_ message: String)
    
    /**
     * Reject the call with message and error code
     * @param message Error message
     * @param code Error code identifier
     */
    public func reject(_ message: String, _ code: String?)
    
    /**
     * Reject the call with full error details
     * @param message Error message
     * @param code Error code identifier
     * @param error Underlying Error object
     */
    public func reject(_ message: String, _ code: String?, _ error: Error?)
    
    /**
     * Reject the call with complete error information
     * @param message Error message
     * @param code Error code identifier
     * @param error Underlying Error object
     * @param data Additional error data
     */
    public func reject(_ message: String, _ code: String?, _ error: Error?, _ data: PluginCallResultData?)
    
    /**
     * Reject with standard "not implemented" error
     */
    public func unimplemented()
    
    /**
     * Reject with custom "not implemented" message
     * @param message Custom not implemented message
     */
    public func unimplemented(_ message: String)
    
    /**
     * Reject with standard "unavailable" error
     */
    public func unavailable()
    
    /**
     * Reject with custom "unavailable" message
     * @param message Custom unavailable message
     */
    public func unavailable(_ message: String)
    
    // MARK: - Type-Safe Parameter Access
    
    /**
     * Get string parameter
     * @param key Parameter name
     * @returns String value or nil if not found
     */
    public func getString(_ key: String) -> String?
    
    /**
     * Get string parameter with default value
     * @param key Parameter name
     * @param defaultValue Default value if parameter not found
     * @returns String value or default
     */
    public func getString(_ key: String, _ defaultValue: String?) -> String?
    
    /**
     * Get boolean parameter
     * @param key Parameter name
     * @returns Boolean value or false if not found/invalid
     */
    public func getBool(_ key: String) -> Bool
    
    /**
     * Get boolean parameter with default value
     * @param key Parameter name
     * @param defaultValue Default value if parameter not found
     * @returns Boolean value or default
     */
    public func getBool(_ key: String, _ defaultValue: Bool) -> Bool
    
    /**
     * Get integer parameter
     * @param key Parameter name
     * @returns Integer value or 0 if not found/invalid
     */
    public func getInt(_ key: String) -> Int
    
    /**
     * Get integer parameter with default value
     * @param key Parameter name
     * @param defaultValue Default value if parameter not found
     * @returns Integer value or default
     */
    public func getInt(_ key: String, _ defaultValue: Int) -> Int
    
    /**
     * Get float parameter
     * @param key Parameter name
     * @returns Float value or 0.0 if not found/invalid
     */
    public func getFloat(_ key: String) -> Float
    
    /**
     * Get float parameter with default value
     * @param key Parameter name
     * @param defaultValue Default value if parameter not found
     * @returns Float value or default
     */
    public func getFloat(_ key: String, _ defaultValue: Float) -> Float
    
    /**
     * Get double parameter
     * @param key Parameter name
     * @returns Double value or 0.0 if not found/invalid
     */
    public func getDouble(_ key: String) -> Double
    
    /**
     * Get double parameter with default value
     * @param key Parameter name
     * @param defaultValue Default value if parameter not found
     * @returns Double value or default
     */
    public func getDouble(_ key: String, _ defaultValue: Double) -> Double
    
    /**
     * Get object parameter
     * @param key Parameter name
     * @returns JSObject dictionary or nil if not found
     */
    public func getObject(_ key: String) -> JSObject?
    
    /**
     * Get object parameter with default value
     * @param key Parameter name
     * @param defaultValue Default value if parameter not found
     * @returns JSObject dictionary or default
     */
    public func getObject(_ key: String, _ defaultValue: JSObject?) -> JSObject?
    
    /**
     * Get array parameter
     * @param key Parameter name
     * @returns JSArray or nil if not found
     */
    public func getArray(_ key: String) -> JSArray?
    
    /**
     * Get array parameter with default value
     * @param key Parameter name
     * @param defaultValue Default value if parameter not found
     * @returns JSArray or default
     */
    public func getArray(_ key: String, _ defaultValue: JSArray?) -> JSArray?
    
    /**
     * Get date parameter
     * @param key Parameter name
     * @returns Date object or nil if not found/invalid
     */
    public func getDate(_ key: String) -> Date?
    
    /**
     * Get date parameter with default value
     * @param key Parameter name
     * @param defaultValue Default value if parameter not found
     * @returns Date object or default
     */
    public func getDate(_ key: String, _ defaultValue: Date?) -> Date?
}

Usage Examples:

class MyPlugin: CAPPlugin {
    
    @objc func processData(_ call: CAPPluginCall) {
        // Extract parameters with type safety
        let message = call.getString("message", "Default message")
        let count = call.getInt("count", 1)
        let enabled = call.getBool("enabled", true)
        let options = call.getObject("options") ?? [:]
        let items = call.getArray("items") ?? []
        
        // Validate required parameters
        guard let userId = call.getString("userId") else {
            call.reject("Missing required parameter: userId")
            return
        }
        
        // Perform processing
        processDataAsync(
            message: message,
            count: count,
            userId: userId,
            enabled: enabled,
            options: options,
            items: items
        ) { result in
            switch result {
            case .success(let data):
                call.resolve(["result": data, "processed": true])
            case .failure(let error):
                call.reject("Processing failed", "PROCESS_ERROR", error)
            }
        }
    }
    
    @objc func uploadFile(_ call: CAPPluginCall) {
        // Handle complex object parameters
        guard let fileInfo = call.getObject("file") else {
            call.reject("File information required")
            return
        }
        
        let filename = fileInfo["name"] as? String ?? "untitled"
        let size = fileInfo["size"] as? Int ?? 0
        let mimeType = fileInfo["type"] as? String ?? "application/octet-stream"
        
        // Upload with progress
        uploadFile(name: filename, size: size, mimeType: mimeType) { progress in
            // For progress updates, save the call and use notifyListeners
            self.notifyListeners("uploadProgress", data: [
                "callId": call.callbackId,
                "progress": progress
            ])
        } completion: { success, error in
            if success {
                call.resolve(["uploaded": true, "filename": filename])
            } else {
                call.reject("Upload failed", "UPLOAD_ERROR", error)
            }
        }
    }
    
    @objc func validateInput(_ call: CAPPluginCall) {
        // Validate multiple parameters
        let errors: [String] = []
        
        guard call.getString("email") != nil else {
            errors.append("Email is required")
        }
        
        let age = call.getInt("age", -1)
        guard age >= 0 && age <= 120 else {
            errors.append("Age must be between 0 and 120")
        }
        
        if !errors.isEmpty {
            call.reject("Validation failed", "VALIDATION_ERROR", nil, [
                "errors": errors
            ])
            return
        }
        
        call.resolve(["valid": true])
    }
}

JavaScript Type System

Type definitions and protocols for safe JavaScript-to-Swift data conversion.

/**
 * Base protocol for all JavaScript-compatible value types
 */
public protocol JSValue {}

// Standard types conforming to JSValue
extension String: JSValue {}
extension Bool: JSValue {}
extension Int: JSValue {}
extension Float: JSValue {}
extension Double: JSValue {}
extension NSNumber: JSValue {}
extension NSString: JSValue {}
extension NSArray: JSValue {}
extension NSDictionary: JSValue {}

/**
 * Type aliases for JavaScript data structures
 */
public typealias JSObject = [String: JSValue]
public typealias JSArray = [JSValue]
public typealias PluginCallResultData = JSObject

/**
 * Protocol for containers that provide type-safe value access
 */
public protocol JSValueContainer {
    /**
     * Get value by key as specific type
     * @param key Key to lookup
     * @returns Value cast to specified type or nil
     */
    func getValue<T>(_ key: String, as type: T.Type) -> T?
}

/**
 * String value container protocol
 */
public protocol JSStringContainer {
    func getString(_ key: String) -> String?
    func getString(_ key: String, _ defaultValue: String) -> String
}

/**
 * Boolean value container protocol  
 */
public protocol JSBoolContainer {
    func getBool(_ key: String) -> Bool
    func getBool(_ key: String, _ defaultValue: Bool) -> Bool
}

/**
 * Numeric value container protocols
 */
public protocol JSIntContainer {
    func getInt(_ key: String) -> Int
    func getInt(_ key: String, _ defaultValue: Int) -> Int
}

public protocol JSFloatContainer {
    func getFloat(_ key: String) -> Float
    func getFloat(_ key: String, _ defaultValue: Float) -> Float
}

public protocol JSDoubleContainer {
    func getDouble(_ key: String) -> Double
    func getDouble(_ key: String, _ defaultValue: Double) -> Double
}

/**
 * Object and array container protocols
 */
public protocol JSObjectContainer {
    func getObject(_ key: String) -> JSObject?
    func getObject(_ key: String, _ defaultValue: JSObject?) -> JSObject?
}

public protocol JSArrayContainer {
    func getArray(_ key: String) -> JSArray?
    func getArray(_ key: String, _ defaultValue: JSArray?) -> JSArray?
}

/**
 * Date container protocol
 */
public protocol JSDateContainer {
    func getDate(_ key: String) -> Date?
    func getDate(_ key: String, _ defaultValue: Date?) -> Date?
}

Plugin Response Objects

Structured response objects for plugin method results and errors.

/**
 * Successful plugin method response
 */
@objc(CAPPluginCallResult)
public class CAPPluginCallResult: NSObject {
    
    /** Response data payload */
    public let data: PluginCallResultData?
    
    /**
     * Initialize with response data
     * @param data Response data dictionary
     */
    public init(data: PluginCallResultData?)
}

/**
 * Plugin method error response
 */
@objc(CAPPluginCallError)
public class CAPPluginCallError: NSObject {
    
    /** Error description message */
    public let message: String
    
    /** Error code identifier */
    public let code: String?
    
    /** Underlying system error */
    public let error: Error?
    
    /** Additional error data */
    public let data: PluginCallResultData?
    
    /**
     * Initialize with error details
     * @param message Error message
     * @param code Error code
     * @param error Underlying error
     * @param data Additional error data
     */
    public init(message: String, code: String?, error: Error?, data: PluginCallResultData?)
}

JavaScript Function Export

System for calling JavaScript functions from native code.

/**
 * Protocol for executing JavaScript functions from native code
 */
@objc public protocol JSExport {
    
    /**
     * Call JavaScript function with no arguments
     * @param method Function name to call
     */
    func call(method: String)
    
    /**
     * Call JavaScript function with single argument
     * @param method Function name to call
     * @param argument Single argument value
     */
    func call(method: String, _ argument: JSValue)
    
    /**
     * Call JavaScript function with multiple arguments
     * @param method Function name to call
     * @param arguments Array of argument values
     */
    func call(method: String, _ arguments: [JSValue])
}

JavaScript Export Usage:

class MyPlugin: CAPPlugin {
    
    @objc func triggerJavaScript(_ call: CAPPluginCall) {
        let functionName = call.getString("function", "defaultFunction")
        let args = call.getArray("args") ?? []
        
        // Call JavaScript function
        if let jsExport = webView as? JSExport {
            jsExport.call(method: functionName, args)
        }
        
        call.resolve()
    }
    
    @objc func notifyJavaScript(_ call: CAPPluginCall) {
        let event = call.getString("event", "notification")
        let data = call.getObject("data") ?? [:]
        
        // Execute JavaScript directly  
        let jsCode = "window.dispatchEvent(new CustomEvent('\(event)', { detail: \(data) }));"
        bridge?.eval(js: jsCode)
        
        call.resolve()
    }
}

Advanced Type Handling

Patterns for handling complex data types and conversion scenarios.

class DataPlugin: CAPPlugin {
    
    @objc func processComplexData(_ call: CAPPluginCall) {
        // Handle nested objects
        guard let userObj = call.getObject("user") else {
            call.reject("User object required")
            return
        }
        
        let userName = userObj["name"] as? String ?? "Unknown"
        let userAge = userObj["age"] as? Int ?? 0
        let userPrefs = userObj["preferences"] as? JSObject ?? [:]
        
        // Handle arrays of objects
        let items = call.getArray("items") ?? []
        var processedItems: [JSObject] = []
        
        for item in items {
            if let itemObj = item as? JSObject {
                let processedItem: JSObject = [
                    "id": itemObj["id"] ?? "",
                    "processed": true,
                    "timestamp": Date().timeIntervalSince1970
                ]
                processedItems.append(processedItem)
            }
        }
        
        // Return complex response
        call.resolve([
            "user": [
                "name": userName,
                "age": userAge,
                "preferences": userPrefs
            ],
            "processedItems": processedItems,
            "totalProcessed": processedItems.count
        ])
    }
    
    @objc func handleDates(_ call: CAPPluginCall) {
        // Handle date parameters
        let startDate = call.getDate("startDate") ?? Date()
        let endDate = call.getDate("endDate") ?? Date()
        
        let formatter = DateFormatter()
        formatter.dateStyle = .full
        
        call.resolve([
            "startDate": formatter.string(from: startDate),
            "endDate": formatter.string(from: endDate),
            "duration": endDate.timeIntervalSince(startDate)
        ])
    }
}