Skip to content

Custom Actions ​

Developer Feature

This guide is for developers who want to extend Canoryn with custom Swift actions.

Overview ​

Custom actions let you add new capabilities to Canoryn that aren't included by default.

Action Structure ​

Every action implements the ActionHandler protocol:

swift
protocol ActionHandler {
    var id: String { get }
    var name: String { get }
    var category: String { get }
    var inputSchema: [PortDefinition] { get }
    var outputSchema: [PortDefinition] { get }

    func execute(inputs: [String: Any]) async throws -> [String: Any]
}

Creating a Custom Action ​

1. Define the Handler ​

swift
struct MyCustomAction: ActionHandler {
    let id = "custom.my_action"
    let name = "My Custom Action"
    let category = "custom"

    let inputSchema: [PortDefinition] = [
        PortDefinition(name: "message", type: .string)
    ]

    let outputSchema: [PortDefinition] = [
        PortDefinition(name: "result", type: .string)
    ]

    func execute(inputs: [String: Any]) async throws -> [String: Any] {
        let message = inputs["message"] as? String ?? ""
        return ["result": "Processed: \(message)"]
    }
}

2. Register the Action ​

swift
ActionRegistry.shared.register(MyCustomAction())

3. Use in Blueprints ​

Your action will appear in the node picker under your category.

Port Types ​

TypeSwift TypeDescription
.stringStringText data
.numberDoubleNumeric data
.booleanBoolTrue/false
.anyAnyFlexible type
.flow-Execution flow

Best Practices ​

  1. Use async/await: Long operations should be async
  2. Handle errors: Throw descriptive errors
  3. Validate inputs: Check types and required fields
  4. Document: Add clear descriptions for discoverability

Example: HTTP Request Action ​

swift
struct HTTPRequestAction: ActionHandler {
    let id = "custom.http_request"
    let name = "HTTP Request"
    let category = "network"

    let inputSchema: [PortDefinition] = [
        PortDefinition(name: "url", type: .string),
        PortDefinition(name: "method", type: .string)
    ]

    let outputSchema: [PortDefinition] = [
        PortDefinition(name: "body", type: .string),
        PortDefinition(name: "status", type: .number)
    ]

    func execute(inputs: [String: Any]) async throws -> [String: Any] {
        guard let urlString = inputs["url"] as? String,
              let url = URL(string: urlString) else {
            throw ActionError.invalidInput("Invalid URL")
        }

        let (data, response) = try await URLSession.shared.data(from: url)
        let httpResponse = response as? HTTPURLResponse

        return [
            "body": String(data: data, encoding: .utf8) ?? "",
            "status": httpResponse?.statusCode ?? 0
        ]
    }
}

Distribution ​

Custom actions can be shared via:

  • GitHub repositories
  • Blueprint Store (coming soon)
  • Direct .swift file sharing