From react-native-hifi
Provides iOS-native SwiftUI views like List, Section, Toggle, and Picker in Expo React Native apps for authentic iOS UI, with RNHostView for embedding React Native content.
How this skill is triggered — by the user, by Claude, or both
Slash command
/react-native-hifi:expo-ui-swift-uiThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
`@expo/ui/swift-ui` provides iOS-native SwiftUI views directly in React Native Expo apps. The API mirrors SwiftUI naming conventions, so iOS developers find it immediately familiar. These components render as true SwiftUI views, not approximations.
@expo/ui/swift-ui provides iOS-native SwiftUI views directly in React Native Expo apps. The API mirrors SwiftUI naming conventions, so iOS developers find it immediately familiar. These components render as true SwiftUI views, not approximations.
Core principle: Use @expo/ui/swift-ui for iOS-specific screens that must feel fully native. Combine with RNHostView to embed React Native content inside SwiftUI hierarchies when needed.
RNHostView// iOS-specific SwiftUI components
import { List, Section, Label, Toggle, Picker, Gauge } from '@expo/ui/swift-ui';
// Cross-platform @expo/ui components (Switch, Slider, etc.) work on both platforms
import { Switch, Slider } from '@expo/ui';
SwiftUI's List provides native iOS list rendering with section headers, footers, and grouped styles:
import { List, Section, Label, Text } from '@expo/ui/swift-ui';
function SettingsScreen() {
return (
<List style={{ flex: 1 }}>
<Section header="General" footer="Customize your app experience">
<Label
title="Language"
systemImage="globe"
onPress={() => router.push('/language')}
/>
<Label
title="Notifications"
systemImage="bell"
onPress={() => router.push('/notifications')}
/>
</Section>
<Section header="Account">
<Label
title="Profile"
systemImage="person.circle"
onPress={() => router.push('/profile')}
/>
<Label
title="Privacy"
systemImage="lock.shield"
onPress={() => router.push('/privacy')}
/>
</Section>
</List>
);
}
// Inset grouped (default iOS Settings style)
<List listStyle="insetGrouped" style={{ flex: 1 }}>
{/* ... */}
</List>
// Plain list
<List listStyle="plain" style={{ flex: 1 }}>
{/* ... */}
</List>
// Sidebar (iPad)
<List listStyle="sidebar" style={{ flex: 1 }}>
{/* ... */}
</List>
Native iOS toggle with SwiftUI styling:
import { Toggle } from '@expo/ui/swift-ui';
import { useState } from 'react';
function NotificationSettings() {
const [pushEnabled, setPushEnabled] = useState(true);
const [soundEnabled, setSoundEnabled] = useState(false);
return (
<List style={{ flex: 1 }}>
<Section header="Notifications">
<Toggle
value={pushEnabled}
onValueChange={setPushEnabled}
label="Push Notifications"
systemImage="bell.fill"
/>
<Toggle
value={soundEnabled}
onValueChange={setSoundEnabled}
label="Sound"
systemImage="speaker.wave.2"
/>
</Section>
</List>
);
}
Native iOS picker with multiple styles:
import { Picker } from '@expo/ui/swift-ui';
import { useState } from 'react';
function ThemePicker() {
const [theme, setTheme] = useState('system');
return (
<Picker
selectedValue={theme}
onValueChange={setTheme}
label="Theme"
style="menu" // 'menu', 'wheel', 'segmented', 'inline'
>
<Picker.Item label="System" value="system" />
<Picker.Item label="Light" value="light" />
<Picker.Item label="Dark" value="dark" />
</Picker>
);
}
Native iOS gauge for displaying progress or levels:
import { Gauge } from '@expo/ui/swift-ui';
function StorageIndicator({ used, total }) {
return (
<Gauge
value={used / total}
label="Storage"
currentValueLabel={`${used} GB`}
minimumValueLabel="0"
maximumValueLabel={`${total} GB`}
gaugeStyle="accessoryCircular" // 'linearCapacity', 'accessoryCircular', 'accessoryLinear'
/>
);
}
SwiftUI-style label with system image:
import { Label } from '@expo/ui/swift-ui';
<Label
title="Downloads"
systemImage="arrow.down.circle"
tintColor="#007AFF"
/>
<Label
title="Favorites"
subtitle="12 items"
systemImage="heart.fill"
tintColor="#FF3B30"
/>
RNHostView lets you embed React Native components inside a SwiftUI view hierarchy. This is the inverse of using SwiftUI in React Native.
import { RNHostView } from '@expo/ui/swift-ui';
// In your SwiftUI-driven layout
function HybridScreen() {
return (
<List style={{ flex: 1 }}>
<Section header="Native Section">
<Label title="Native Row" systemImage="star" />
</Section>
<Section header="React Native Content">
<RNHostView style={{ height: 200 }}>
{/* This React Native subtree renders inside SwiftUI */}
<View style={{ flex: 1, backgroundColor: '#f0f0f0', borderRadius: 8 }}>
<Text style={{ padding: 16, fontSize: 16 }}>
React Native content embedded in SwiftUI
</Text>
<CustomChart data={chartData} />
</View>
</RNHostView>
</Section>
</List>
);
}
RNHostView children receive a fixed frame from SwiftUIWrap custom SwiftUI views for use in React Native:
import { requireNativeView } from 'expo';
const NativeSwiftUIView = requireNativeView('MySwiftUIView');
function CustomNativeView({ title, onAction }) {
return (
<NativeSwiftUIView
style={{ height: 200 }}
title={title}
onAction={onAction}
/>
);
}
// ios/MySwiftUIView.swift
import ExpoModulesCore
import SwiftUI
class MySwiftUIModule: Module {
public func definition() -> ModuleDefinition {
Name("MySwiftUIView")
View(MySwiftUIView.self) {
Prop("title") { (view, title: String) in
view.title = title
}
Events("onAction")
}
}
}
struct MySwiftUIView: ExpoView {
@State var title: String = ""
let onAction = EventDispatcher()
var body: some View {
VStack {
Text(title)
.font(.headline)
Button("Tap Me") {
onAction(["action": "tapped"])
}
}
}
}
components/
SettingsForm.tsx # Shared logic/default
SettingsForm.ios.tsx # SwiftUI-based iOS version
SettingsForm.android.tsx # Jetpack Compose Android version
// components/SettingsForm.ios.tsx
import { List, Section, Toggle, Picker } from '@expo/ui/swift-ui';
export function SettingsForm({ settings, onUpdate }) {
return (
<List listStyle="insetGrouped" style={{ flex: 1 }}>
<Section header="Appearance">
<Toggle
value={settings.darkMode}
onValueChange={(v) => onUpdate('darkMode', v)}
label="Dark Mode"
systemImage="moon.fill"
/>
<Picker
selectedValue={settings.accentColor}
onValueChange={(v) => onUpdate('accentColor', v)}
label="Accent Color"
style="menu"
>
<Picker.Item label="Blue" value="blue" />
<Picker.Item label="Purple" value="purple" />
<Picker.Item label="Green" value="green" />
</Picker>
</Section>
</List>
);
}
import { Platform } from 'react-native';
function AdaptiveSettings() {
if (Platform.OS === 'ios') {
const { List, Section, Toggle } = require('@expo/ui/swift-ui');
return (
<List listStyle="insetGrouped" style={{ flex: 1 }}>
{/* SwiftUI rendering */}
</List>
);
}
// Cross-platform fallback
return <ScrollView>{/* ... */}</ScrollView>;
}
| Mistake | Fix |
|---|---|
Importing @expo/ui/swift-ui on Android | Guard with Platform.OS or use .ios.tsx file extension |
Applying React Native StyleSheet to SwiftUI components | SwiftUI components use their own props for styling; use listStyle, gaugeStyle, etc. |
Oversizing RNHostView content | Content must fit within the SwiftUI-provided frame; set explicit height |
Not rebuilding after adding @expo/ui | Requires native rebuild: npx expo run:ios or eas build |
| Mixing SwiftUI navigation with Expo Router | Use Expo Router for screen navigation; SwiftUI for in-screen UI only |
| Expecting SwiftUI hot reload | SwiftUI views require a native rebuild when the Swift code changes |
| Task | Pattern |
|---|---|
| Import SwiftUI components | import { ... } from '@expo/ui/swift-ui' |
| Grouped list | <List listStyle="insetGrouped"> |
| Section with header | <Section header="Title"> |
| Toggle | <Toggle value={v} onValueChange={fn} label="Text" /> |
| Picker | <Picker selectedValue={v} onValueChange={fn} style="menu"> |
| System icon label | <Label title="Text" systemImage="icon.name" /> |
| Embed RN in SwiftUI | <RNHostView><ReactNativeContent /></RNHostView> |
| Custom SwiftUI module | Expo Modules API with Swift + SwiftUI |
| Platform guard | Platform.OS === 'ios' or .ios.tsx file |
npx claudepluginhub bidah/react-native-hifi --plugin react-native-hifiIntegrates SwiftUI views and modifiers into Expo apps using @expo/ui/swift-ui for iOS-native UI. Guides Host wrapping, RNHostView for React Native embeds, and SDK 55 extensions.
Enables SwiftUI Views and modifiers in Expo apps via @expo/ui/swift-ui package. Guides SDK 55 installation, API usage mirroring SwiftUI, Host wrapping, RNHostView embedding, and extensions.
Builds React Native and Expo UIs: Expo Router navigation, Flexbox layouts, animations, native controls, modals, gestures, and StyleSheet styling.