iOS Adapter
Hypen adapter for iOS with SwiftUI
iOS Adapter
The iOS adapter (HypenSwift) renders Hypen components to native SwiftUI views. It connects to a Hypen server over WebSocket and translates the patch-based rendering protocol into a live SwiftUI view hierarchy.
Installation
Add the package to your Xcode project via Swift Package Manager:
// In Package.swift
dependencies: [
.package(url: "https://github.com/nicktowe/hypen-swift.git", from: "1.0.0")
]Or in Xcode: File > Add Package Dependencies, then enter the repository URL.
Basic Usage
Drop a HypenView into any SwiftUI view to render a remote Hypen app:
import HypenSwift
struct ContentView: View {
var body: some View {
HypenView(url: "ws://localhost:3000")
}
}HypenView manages the full lifecycle automatically -- it connects on appear, renders the component tree, and disconnects on disappear.
Configuration
Customize the connection with RemoteEngineConfig:
HypenView(
url: "ws://localhost:3000",
config: RemoteEngineConfig(
autoReconnect: true,
reconnectInterval: 3.0,
maxReconnectAttempts: 10,
connectTimeout: 10.0,
pingInterval: 30.0,
debugLogging: true
)
)Configuration Options
| Option | Type | Default | Description |
|---|---|---|---|
autoReconnect | Bool | true | Automatically reconnect on disconnect |
reconnectInterval | TimeInterval | 3.0 | Seconds between reconnection attempts |
maxReconnectAttempts | Int | 10 | Max attempts before giving up (0 = unlimited) |
connectTimeout | TimeInterval | 10.0 | Connection timeout in seconds |
readTimeout | TimeInterval | 30.0 | Read timeout in seconds |
writeTimeout | TimeInterval | 10.0 | Write timeout in seconds |
pingInterval | TimeInterval | 30.0 | Keep-alive ping interval in seconds |
debugLogging | Bool | false | Enable debug logging |
Built-in presets are available: RemoteEngineConfig.default, .debug, and .noReconnect.
Custom Loading and Error Views
Provide custom SwiftUI views for loading and error states:
HypenView(
url: "ws://localhost:3000",
loadingContent: {
VStack {
ProgressView()
Text("Loading app...")
}
},
errorContent: { message in
VStack {
Image(systemName: "wifi.slash")
.font(.largeTitle)
Text(message)
}
}
)Supported Components
HypenSwift includes native SwiftUI renderers for all standard Hypen components:
- Layout: Column, Row, Box, Stack, Center, Container, Spacer, Grid
- Content: Text, Heading, Paragraph, Image, Divider, Link
- Interactive: Button
- Forms: Input, Textarea, Checkbox, Switch, Slider, Select
- UI: Card, Badge, Avatar, Spinner, ProgressBar, List
- Media: Audio, Video (via AVFoundation/AVKit)
- Navigation: Router, Route, Link (with client-side routing)
Supported Applicators (Styling)
All standard Hypen applicators map to native SwiftUI modifiers:
- Spacing: padding, margin
- Size: width, height, fillMaxWidth, fillMaxHeight, aspectRatio
- Color: backgroundColor, foregroundColor (color names, hex, rgba)
- Gradients: linearGradient, radialGradient, conicGradient
- Background: backgroundImage, backgroundSize, backgroundPosition
- Border: border, borderRadius, borderStyle (solid, dashed, dotted, double)
- Layout: alignment, weight, flex, gap, rowGap, columnGap
- Visual Effects: opacity, shadow, blur, boxShadow, clipToBounds
- Transform: rotate, scale, translateX, translateY, compound transforms
- Text: fontSize, fontWeight, fontFamily, textAlign, lineHeight, maxLines
- Events: onClick, onLongPress
Custom Components
Register custom component handlers to extend the renderer:
public struct VideoPlayerComponent: ComponentHandler {
public let typeName = "videoplayer"
public func render(
context: ComponentContext,
modifier: HypenModifier,
children: @escaping () -> AnyView
) -> AnyView {
let src = context.element.getStringProp("src.0") ?? ""
return AnyView(
VideoPlayer(player: AVPlayer(url: URL(string: src)!))
.hypenModifier(modifier)
)
}
}
// Register it
let registry = ComponentRegistry.withDefaults()
registry.register(VideoPlayerComponent())
HypenView(
url: "ws://localhost:3000",
componentRegistry: registry
)Custom Applicators
Add custom styling applicators:
public struct GlowApplicator: ApplicatorHandler {
public let name = "glow"
public func apply(
modifier: inout HypenModifier,
value: Any?,
context: ApplicatorContext
) {
if let radius = value as? Double {
modifier.shadowRadius = CGFloat(radius)
modifier.shadowColor = .blue.opacity(0.5)
}
}
}
let applicators = ApplicatorRegistry.withDefaults()
applicators.register(GlowApplicator())
HypenView(
url: "ws://localhost:3000",
applicatorRegistry: applicators
)Using RemoteEngine Directly
For more control, use RemoteEngine and HypenRenderer directly:
import HypenSwift
import Combine
@MainActor
class AppViewModel: ObservableObject {
private var engine: RemoteEngine?
private let renderer = HypenRenderer()
private var cancellables = Set<AnyCancellable>()
func connect() {
let engine = try! RemoteEngine(urlString: "ws://localhost:3000")
self.engine = engine
engine.connectionState
.sink { state in print("State: \(state)") }
.store(in: &cancellables)
engine.patches
.receive(on: DispatchQueue.main)
.sink { [weak self] patches in
self?.renderer.applyPatches(patches)
}
.store(in: &cancellables)
engine.state
.receive(on: DispatchQueue.main)
.sink { [weak self] state in
self?.renderer.updateState(state)
}
.store(in: &cancellables)
engine.connect()
}
func dispatchAction(_ action: String, payload: [String: Any]? = nil) {
engine?.dispatchAction(action, payload: payload)
}
}Session Management
RemoteEngine supports session persistence and resumption:
let engine = try RemoteEngine(
urlString: "ws://localhost:3000",
sessionOptions: SessionOptions(
id: savedSessionId, // Resume a previous session
props: [ // Client metadata
"platform": "ios",
"version": "1.0.0"
]
)
)
// Listen for session events
engine.sessionEstablished
.sink { info in
print("Session: \(info.sessionId), new: \(info.isNew), restored: \(info.isRestored)")
}
.store(in: &cancellables)
engine.sessionExpired
.sink { reason in
print("Session expired: \(reason)")
}
.store(in: &cancellables)Platform Support
HypenSwift supports all Apple platforms:
| Platform | Minimum Version |
|---|---|
| iOS | 15.0+ |
| macOS | 12.0+ |
| tvOS | 15.0+ |
| watchOS | 8.0+ |
Platform-specific features (such as keyboard types on iOS or window background colors on macOS) are handled automatically with #if os() conditionals.
Requirements
- Xcode 16+ (Swift 6.0)
- iOS 15+ / macOS 12+ / tvOS 15+ / watchOS 8+
- A running Hypen server to connect to (see TypeScript SDK)
See Also
- TypeScript SDK -- Server-side module and state API
- Remote App Tutorial -- Build a cross-platform remote app
- Android Adapter -- Android/Jetpack Compose adapter
- Web Adapter -- Browser DOM and Canvas adapter
- Components Guide -- All built-in components
- Styling Guide -- Applicators reference