https://github.com/skiptools/skip-fuse-ui
Native Swift package that bridges the SwiftUI API to Android via SkipUI
https://github.com/skiptools/skip-fuse-ui
Last synced: about 2 months ago
JSON representation
Native Swift package that bridges the SwiftUI API to Android via SkipUI
- Host: GitHub
- URL: https://github.com/skiptools/skip-fuse-ui
- Owner: skiptools
- License: mpl-2.0
- Created: 2025-01-26T17:22:28.000Z (over 1 year ago)
- Default Branch: main
- Last Pushed: 2026-04-11T01:41:49.000Z (2 months ago)
- Last Synced: 2026-04-11T03:25:14.394Z (2 months ago)
- Language: Swift
- Size: 1.47 MB
- Stars: 16
- Watchers: 3
- Forks: 12
- Open Issues: 9
-
Metadata Files:
- Readme: README.md
- Changelog: CHANGELOG.md
- License: LICENSE.txt
Awesome Lists containing this project
README
# SkipFuseUI
SkipFuseUI provides the SwiftUI API surface for [Skip Fuse](https://skip.dev/docs/modes/#fuse) apps on Android. It acts as a thin Swift bridging layer that delegates rendering to [SkipUI](https://skip.dev/docs/modules/skip-ui/), which implements SwiftUI views as Jetpack Compose composables. On iOS, `import SwiftUI` resolves to Apple's framework as usual; on Android, it resolves to SkipFuseUI, giving you a single SwiftUI codebase that runs natively on both platforms.
## How It Works
SkipFuseUI sits between your SwiftUI code and SkipUI's Compose implementation. Your Swift views are compiled natively for Android by the Swift Android SDK, and at render time each view produces a Kotlin-side SkipUI counterpart that Compose renders on screen.
```mermaid
flowchart LR
A["Your SwiftUI Code"] --> B["SkipFuseUI\n(Swift on Android)"]
B -->|"Java_view"| C["SkipUI\n(Kotlin/Compose)"]
C --> D["Jetpack Compose\nUI on Screen"]
style A fill:#555555,stroke:#333,color:#fff
style B fill:#6b3fa0,stroke:#4a2d6e,color:#fff
style C fill:#2e6da4,stroke:#1a4a6e,color:#fff
style D fill:#2d7a2d,stroke:#1a5c1a,color:#fff
```
The key mechanism is the `SkipUIBridging` protocol. Every SkipFuseUI view type conforms to it by exposing a `Java_view` property that returns the equivalent SkipUI Kotlin object. When Compose needs to render your view hierarchy, it walks the tree of `Java_view` references — each one backed by [SkipBridge](https://skip.dev/docs/modules/skip-bridge/) JNI calls between Swift and Kotlin.
### Module Relationships
```mermaid
flowchart TB
subgraph "Your App"
APP["App SwiftUI Code"]
end
subgraph "SkipFuseUI Package"
SFUI["SkipFuseUI\n(re-exports SwiftUI\non Android)"]
SSU["SkipSwiftUI\n(Swift view types,\nproperty wrappers,\nmodifiers)"]
end
subgraph "Bridging Infrastructure"
SB["SkipBridge\n(JNI object lifecycle,\ntype conversion)"]
SF["SkipFuse\n(@Observable,\nOSLog, bridging\nsupport)"]
SAB["SkipAndroidBridge\n(Android-specific\nJNI helpers)"]
SJNI["SwiftJNI\n(low-level JNI\nC/Swift wrapper)"]
end
subgraph "Compose Implementation"
SUI["SkipUI\n(SwiftUI → Jetpack\nCompose mapping)"]
SM["SkipModel\n(Compose state\ntracking)"]
end
APP --> SFUI
SFUI --> SSU
SSU --> SB
SSU --> SF
SSU --> SUI
SB --> SAB
SB --> SJNI
SUI --> SM
style APP fill:#555555,stroke:#333,color:#fff
style SFUI fill:#6b3fa0,stroke:#4a2d6e,color:#fff
style SSU fill:#6b3fa0,stroke:#4a2d6e,color:#fff
style SB fill:#b33030,stroke:#8a1a1a,color:#fff
style SF fill:#b33030,stroke:#8a1a1a,color:#fff
style SAB fill:#b33030,stroke:#8a1a1a,color:#fff
style SJNI fill:#b33030,stroke:#8a1a1a,color:#fff
style SUI fill:#2e6da4,stroke:#1a4a6e,color:#fff
style SM fill:#2e6da4,stroke:#1a4a6e,color:#fff
```
On iOS, `SkipFuseUI` simply re-exports Apple's `SwiftUI` — the entire SkipSwiftUI layer is compiled away.
### Bridging Pattern
Every SwiftUI type in SkipFuseUI follows the same pattern: a Swift struct or class holds the view's parameters, and its `Java_view` property constructs the Kotlin equivalent on demand.
```mermaid
sequenceDiagram
participant App as Your View (Swift)
participant Fuse as SkipFuseUI VStack (Swift)
participant Bridge as SkipBridge (JNI)
participant UI as SkipUI VStack (Kotlin)
participant Compose as Jetpack Compose
App->>Fuse: VStack { Text("Hello") }
Note over Fuse: Stores alignment,
spacing, content
Compose->>Fuse: Request Java_view
Fuse->>Bridge: Create SkipUI.VStack
with bridged content
Bridge->>UI: JNI call → Kotlin object
UI->>Compose: Emit Column composable
```
Content views are recursively bridged via `Java_viewOrEmpty`, which walks the view tree and converts each Swift view into its Kotlin counterpart.
### State Bridging
SwiftUI property wrappers (`@State`, `@Binding`, `@AppStorage`) are backed by bridge-aware box types that synchronize values between Swift and Compose's reactive state system:
```mermaid
flowchart LR
S["@State var count = 0\n(Swift)"] -->|"BridgedStateBox"| K["StateSupport\n(Kotlin/Compose)"]
K -->|"MutableState"| C["Compose\nRecomposition"]
C -->|"read triggers\naccess()"| S
style S fill:#6b3fa0,stroke:#4a2d6e,color:#fff
style K fill:#2e6da4,stroke:#1a4a6e,color:#fff
style C fill:#2d7a2d,stroke:#1a5c1a,color:#fff
```
When Swift code writes to a `@State` property, the `BridgedStateBox` notifies Compose's `MutableState`, triggering recomposition. When Compose reads the value, it calls back into Swift via the bridge. This two-way sync ensures that SwiftUI's declarative state model works identically on Android.
`@Observable` types require `import SkipFuse` to enable this state tracking. See the [App Development](https://skip.dev/docs/app-development/#ui) guide for details.
## What SkipFuseUI Covers
SkipFuseUI mirrors the SwiftUI API surface for iOS 16+, including:
- **Containers**: `VStack`, `HStack`, `ZStack`, `List`, `ScrollView`, `LazyVGrid`, `LazyHGrid`, `NavigationStack`, `TabView`, `Form`, `Section`, `Group`
- **Controls**: `Button`, `Toggle`, `Slider`, `Stepper`, `Picker`, `DatePicker`, `TextField`, `SecureField`, `TextEditor`
- **Components**: `Text`, `Image`, `AsyncImage`, `Label`, `Link`, `ProgressView`, `Divider`, `ShareLink`
- **Graphics**: `Color`, `Gradient`, `Shape` (Circle, Rectangle, Capsule, etc.), `Path`, `Material`
- **Layout**: `GeometryReader`, `Alignment`, `EdgeInsets`, `ViewThatFits`, `Grid`
- **State**: `@State`, `@Binding`, `@Environment`, `@AppStorage`, `@FocusState`
- **Modifiers**: `.padding`, `.frame`, `.background`, `.overlay`, `.opacity`, `.rotation`, `.shadow`, `.clipShape`, `.sheet`, `.alert`, `.onAppear`, `.task`, and many more
- **Navigation**: `NavigationStack`, `NavigationLink`, `NavigationPath`, `.navigationTitle`, `.toolbar`
- **Gestures**: `TapGesture`, `LongPressGesture`, `DragGesture`
- **Animation**: `withAnimation`, `.animation`, `.transition`, `Spring`
- **UIKit compatibility**: `UIApplication`, `UIColor`, `UIImage`, `UIPasteboard`
For the full list of supported SwiftUI components, see the [SkipUI documentation](https://skip.dev/docs/modules/skip-ui/#supported-swiftui).
## Related Documentation
- [App Development](https://skip.dev/docs/app-development/) — Building dual-platform apps with Skip, including UI and view model coding
- [Skip Modes](https://skip.dev/docs/modes/) — Fuse vs. Lite mode and when to use each
- [Bridging Reference](https://skip.dev/docs/bridging/) — Supported Swift language features and types for bridging
- [Cross-Platform Topics](https://skip.dev/docs/platformcustomization/) — Integrating platform-specific code with `#if SKIP` and `#if os(Android)`
- [SkipUI Module](https://skip.dev/docs/modules/skip-ui/) — Supported SwiftUI components and Compose integration topics
- [SkipBridge Module](https://skip.dev/docs/modules/skip-bridge/) — The JNI bridging infrastructure that SkipFuseUI depends on
- [SkipFuse Module](https://skip.dev/docs/modules/skip-fuse/) — Observable state tracking and Android runtime support
## License
This software is licensed under the
[Mozilla Public License 2.0](https://www.mozilla.org/MPL/).