Hi everyone,
I've been building PipeKit, an iOS SDK that gives mobile teams runtime control over production apps; remote function execution, feature flags, and session replay, and I wanted to share it with the Swift community since Swift macros are central to how it works.
What is it?
PipeKit is a runtime control plane for iOS apps. After your app ships through the App Store, PipeKit lets you:
- Trigger pre-declared Swift functions remotely on production devices
- Toggle feature flags in real-time without shipping an update
- Compose multi-step flows and execute them on target devices
- Replay sessions with video, console logs, and network capture tied to every execution
No dynamic code loading, no eval, no remote script execution. Every function that can be triggered remotely must be compiled into the binary at build time.
How Swift Macros Power It
The SDK relies on two custom macros built with Swift 5.9's macro system:
@Rewire — Remote Function Registration
import PipeKitSDK
@Rewire("clear-cache")
func clearCache() async -> NodeResult {
await CacheManager.shared.purge()
return .success(["freed": "128MB"])
}
@Rewire("toggle-feature")
func toggleFeature(name: String, enabled: Bool) async -> NodeResult {
FeatureFlags.set(name, enabled)
return .success(["feature": name, "enabled": "\(enabled)"])
}
At compile time, @Rewire generates the registration code that catalogs the function with PipeKit's node registry. When the app connects via WebSocket, the SDK reports all registered nodes to the server. From the dashboard, you can trigger any
registered function on a specific device, a group, or your entire fleet.
The macro extracts parameter names and types at compile time, so the dashboard knows the function signature and can present typed input fields. Node results are structured .success with a metadata dictionary or .failure with an error
message.
@PipeFlag — Typed Feature Flags as Properties
class AppFlags {
@PipeFlag var isMaintenanceMode = false
@PipeFlag var maxRetries = 3
@PipeFlag var refreshInterval = 30.0
@PipeFlag("api-endpoint")
var apiEndpoint = "https://api.prod.com/v2"
}
@PipeFlag turns a regular property into a remotely controllable value. The compiled default is always the fallback. When the dashboard pushes an override via WebSocket, the property resolves to the new value immediately. Value resolution
follows a priority chain: remote override → local override → compiled default.
Supported types: Bool, Int, Double, String. Type safety is enforced; the server can't push an Int to a Bool flag.
Flow Composition
Flows let you chain registered nodes into multi-step sequences. Think of it like Apple's Shortcuts app, but for your production devices:
let result = try await FlowEngine.shared.execute(
Flow("maintenance-routine") {
Pipe("clear-cache")
Pipe("toggle-feature")
Pipe("set-log-level")
},
context: context
)
Flows can also be defined and triggered from the dashboard, select nodes, set parameters, pick target devices, execute. Each step runs sequentially, and every execution is recorded with full observability context.
Session Replay and Observability
PipeKit records three layers of telemetry from production devices:
- Video replay at 1fps with H.264 compression (~50-200KB/min)
- Console logs with severity levels and timestamps
- Network capture with request/response payloads and timing
Sessions record continuously while the app is running — not just during remote executions. You get full visibility into what your users experience, whether or not a flow was triggered.
When you do trigger a remote action or execute a flow, the execution is timestamped against the session timeline. You can scrub directly to the exact moment a node ran and see the device state surrounding it.
This gives you two things: general-purpose session replay for understanding user behavior, and execution-linked context for every decision your control plane makes.
PipeKit.configure(
apiKey: "pk_live_xxxxxxxx",
config: PipeKitConfig(
enableSessionReplay: true,
enableFlows: true,
captureNetwork: true,
captureConsoleLogs: true
)
)
Privacy: Sensitive views are masked automatically for password fields. You can mark additional views with .sensitiveContent() in SwiftUI or .markAsSensitive() in UIKit.
Technical Details
- Distribution: XCFramework (arm64, x86_64) + macro binary
- Platforms: iOS 16+, iPadOS 16+, macOS 13+, Mac Catalyst
- Requirements: Xcode 15+, Swift 5.9+
- Dependencies: Zero external dependencies
- Communication: WebSocket for real-time dispatch, HTTP for telemetry upload
- Performance: Session recording runs on a background thread, <1% CPU
The macro binary ships as a pre-compiled executable alongside the XCFramework. Build settings require two entries in Other Swift Flags:
-load-plugin-executable
$(SRCROOT)/Macros/PipeKitMacros#PipeKitMacros
Demo App
The demo repo implements a university management system with 10 registered nodes and 4 pre-built flows:
It covers session recording, flow execution, feature flags, and the full observability stack. Clone and run on the simulator to see everything in action.
Status: Private Beta
PipeKit is currently in private beta. The SDK, dashboard, and docs are live. If you're working on an iOS project where runtime control over production behavior would be useful, especially teams managing enterprise or fleet-deployed apps I'd love to get your feedback.
Reach out if you're interested in trying it: https://pipekit.tech
I'm also happy to answer any questions about the macro implementation, the architecture, or the SDK design decisions.