Files
firezone/swift/apple/FirezoneNetworkExtension/SessionEventLoop.swift
Mariusz Klochowicz e76daaaab3 refactor: remove JSON serialization from FFI boundary (#10575)
This PR eliminates JSON-based communication across the FFI boundary,
replacing it with proper
uniffi-generated types for improved type safety, performance, and
reliability. We replace JSON string parameters with native uniffi types
for:
 - Resources (DNS, CIDR, Internet)
 - Device information
 - DNS server lists
 - Network routes (CIDR representation)
 
Also, get rid of JSON serialisation in Swift client IPC in favour of
PropertyList based serialisation.
 
 Fixes: https://github.com/firezone/firezone/issues/9548

---------

Co-authored-by: Thomas Eizinger <thomas@eizinger.io>
2025-10-16 05:15:31 +00:00

86 lines
2.3 KiB
Swift

import FirezoneKit
import Foundation
/// Commands that can be sent to the Session.
enum SessionCommand {
case disconnect
case setInternetResourceState(Bool)
case setDns([String])
case reset(String)
}
/// Runs the session event loop, owning the Session lifecycle.
///
/// When either task completes, both are cancelled and the function returns.
/// This ensures the Session's Drop is called on the Rust side.
func runSessionEventLoop(
session: Session,
commandReceiver: Receiver<SessionCommand>,
eventSender: Sender<Event>
) async {
// Multiplex between commands and events
await withTaskGroup(of: Void.self) { group in
// Event polling task - polls Rust for events and sends to eventSender
group.addTask {
while !Task.isCancelled {
// Poll for next event from Rust
guard let event = await session.nextEvent() else {
// No event returned - session has ended
Log.log("SessionEventLoop: Event stream ended, exiting event loop")
break
}
eventSender.send(event)
}
Log.log("SessionEventLoop: Event polling finished")
}
// Command handling task - receives commands from commandReceiver
group.addTask {
for await command in commandReceiver.stream {
await handleCommand(command, session: session)
// Exit loop if disconnect command
if case .disconnect = command {
Log.log("SessionEventLoop: Disconnect command received, exiting command loop")
break
}
}
Log.log("SessionEventLoop: Command handling finished")
}
// Wait for first task to complete, then cancel all
_ = await group.next()
Log.log("SessionEventLoop: One task completed, cancelling event loop")
group.cancelAll()
}
}
/// Handles a command by calling the appropriate session method.
private func handleCommand(_ command: SessionCommand, session: Session) async {
switch command {
case .disconnect:
do {
try session.disconnect()
} catch {
Log.error(error)
}
case .setInternetResourceState(let active):
session.setInternetResourceState(active: active)
case .setDns(let servers):
do {
try session.setDns(dnsServers: servers)
} catch {
Log.error(error)
}
case .reset(let reason):
session.reset(reason: reason)
}
}