Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

danrom11/FlowBridge

Folders and files

NameName
Last commit message
Last commit date

Latest commit

History

5 Commits

Repository files navigation

FlowBridge

FlowBridge is a cross-language bridge for signals and slots between C++, Objective-C, and Swift. Type-safe events in C++ and convenient hooks in Swift.


✨ Features

  • Type-safe signals in C++
  • Automatic bridging to Swift / Objective-C
  • Pass values (string, number, bool, binary data) between layers
  • No boilerplate callbacks β€” event-driven design

πŸ“¦ Installation

Add FlowBridge as a Swift Package dependency:

dependencies: [
 .package(url: "https://github.com/danrom11/FlowBridge.git", from: "2.1.0")
]

πŸ”§ Usage

FlowBridge revolves around signals (events you define in C++) and slots (handlers you connect in Swift/Objective-C).

1. Define a signal in C++

Use the FLOW_SIGNAL macro to declare a named signal:

#include "FlowBridge.hpp"
FLOW_SIGNAL(tick_score);

i️ You don’t strictly need to register a signal β€” emitting will create it automatically. But calling FlowBridge::registerSignal(tick_score) is recommended for clearer architecture and to document the intent.

2. Emit from C++

Emit an event whenever you want:

FlowBridge::emit(tick_score, "Hello from C++");

You can pass strings, numbers, booleans, or even binary data

3. Connect in Swift

In Swift, subscribe by name and handle the payload:

import FlowBridge
struct FlowSignals {
 static let tick_score = "tick_score"
}
FlowBridge.connect(FlowSignals.tick_score) { data in
 if let text = data as? String {
 print("Received:", text)
 }
}

Or you can use a selector:

import FlowBridge
struct FlowSignals {
 static let tick_score = "tick_score"
}
 
@objc func test(_ data: Any?) {
 print("Data: \(data ?? "none")")
 
 if let stringData = data as? String {
 DispatchQueue.main.async {
 self.nameLabel.text = stringData
 }
 }
}
override func viewDidLoad() {
 super.viewDidLoad()
 nameLabel.text = "0"
 
 FlowBridge.connect(FlowSignals.tick_score, target: self, slot: #selector(test(_:)))
 
 let manager = ManagerWrapper.shared
 manager().start(withInterval: 1000)
}

πŸ“¦ Passing Complex Structures with TLV

Starting from version 2.1.0, FlowBridge supports automatic serialization/deserialization of complex data types (dictionaries, arrays, nested structures) using TLV (Type-Length-Value).

Example: Sending a Document from C++ to Swift

C++ side:

#include "FlowBridge.hpp"
#include "tlv.hpp"
// Create a document structure
tlv::Map document{
 {"user_id", int64_t(42)},
 {"name", std::string("Alice")},
 {"role", std::string("developer")},
 {"skills", tlv::Array{
 std::string("C++"),
 std::string("Swift"),
 std::string("Objective-C")
 }},
 {"active", true},
 {"rating", 4.8}
};
// Emit signal with TLV-encoded data
FlowBridge::emit("user_profile", tlv::encode(tlv::Value(document)));

Swift side:

import FlowBridge
FlowBridge.connect("user_profile") { data in
 // Receive automatically decoded dictionary
 if let profile = data as? [String: Any] {
 let userId = profile["user_id"] as? Int ?? 0
 let name = profile["name"] as? String ?? ""
 let role = profile["role"] as? String ?? ""
 let skills = profile["skills"] as? [String] ?? []
 let active = profile["active"] as? Bool ?? false
 let rating = profile["rating"] as? Double ?? 0.0
 
 print("User: \(name) (ID: \(userId))")
 print("Role: \(role)")
 print("Skills: \(skills.joined(separator: ", "))")
 print("Active: \(active), Rating: \(rating)")
 }
}

Example: Sending Data from Swift to C++

Swift side:

import FlowBridge
// Send a request with parameters
FlowBridge.emit("api_request", data: [
 "endpoint": "/users",
 "method": "GET",
 "params": [
 "page": 1,
 "limit": 20,
 "sort": "name"
 ],
 "headers": [
 "Authorization": "Bearer token123",
 "Accept": "application/json"
 ]
])

C++ side:

#include "FlowBridge.hpp"
#include "tlv.hpp"
// Connect to signal with TLV handling
static FlowBridge::Connection connection = 
 FlowBridge::connect<std::vector<uint8_t>>("api_request",
 [](const std::vector<uint8_t>& buffer) {
 try {
 tlv::Value value = tlv::decode(buffer);
 
 // Check if we received a dictionary
 if (auto request = std::get_if<tlv::Map>(&value)) {
 
 // Extract endpoint
 auto endpoint_it = request->find("endpoint");
 if (endpoint_it != request->end()) {
 if (auto endpoint = std::get_if<std::string>(&endpoint_it->second)) {
 std::cout << "Endpoint: " << *endpoint << std::endl;
 }
 }
 
 // Extract parameters
 auto params_it = request->find("params");
 if (params_it != request->end()) {
 if (auto params = std::get_if<tlv::Map>(&params_it->second)) {
 auto page_it = params->find("page");
 if (page_it != params->end()) {
 if (auto page = std::get_if<int64_t>(&page_it->second)) {
 std::cout << "Page: " << *page << std::endl;
 }
 }
 }
 }
 }
 } catch (const std::exception& e) {
 std::cerr << "Failed to decode TLV: " << e.what() << std::endl;
 }
 });

Supported Types

Swift type C++ type TLV representation
nil nullptr null
Bool bool boolean
Int int64_t integer
Double double float
String std::string string
Data std::vector<uint8_t> binary
Array tlv::Array array
Dictionary tlv::Map map

TLV Advantages

  • Type safety β€” data preserves its types during transmission
  • Arbitrary nesting β€” dictionaries can contain arrays that contain other dictionaries
  • Automatic deserialization β€” data arrives in Swift ready to use
  • Efficiency β€” compact binary representation

πŸ“‚ Examples

A full demo project is available in https://github.com/danrom11/FlowBridge-Examples

About

FlowBridge is a cross-language bridge for signals and slots between C++, Objective-C, and Swift. Type-safe events in C++ and convenient hooks in Swift.

Topics

Resources

License

Stars

Watchers

Forks

Packages

Contributors

AltStyle γ«γ‚ˆγ£γ¦ε€‰ζ›γ•γ‚ŒγŸγƒšγƒΌγ‚Έ (->γ‚ͺγƒͺγ‚ΈγƒŠγƒ«) /