CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-capacitor--ios

iOS native runtime implementation for Capacitor that bridges JavaScript and native iOS APIs.

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

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)
        ])
    }
}

Install with Tessl CLI

npx tessl i tessl/npm-capacitor--ios

docs

bridge-integration.md

configuration.md

index.md

method-calls.md

notifications.md

plugin-development.md

tile.json