# What RyUI has, against SwiftUI and Expo

Each row is something apps often need: how SwiftUI and Expo do it, and
what RyUI has for it.

✓ has it · ◐ in part (what's missing is said) · ✗ not yet

## Layout

|                                     | SwiftUI                            | Expo (React Native)                    | RyUI                                                                            |
| ----------------------------------- | ---------------------------------- | -------------------------------------- | ------------------------------------------------------------------------------- |
| Stacks, spacing, padding, alignment | `HStack`, `VStack`, `Spacer`       | flexbox                                | ✓ `Row`, `Column`, `Style` (flexbox: `Grow`, `Gap`, `Align`, `Justify`, `Wrap`) |
| Overlays, absolute placement        | `ZStack`, `.overlay`               | `position: absolute`                   | ✓ `Style.Absolute`, `Top`/`Left`/...                                            |
| Scrolling                           | `ScrollView`                       | `ScrollView`                           | ✓ `Scroll` (vertical, horizontal; out-of-sight rows leave the tree)             |
| Lazy lists, cell recycling          | `List`, `LazyVStack`               | `FlatList`                             | ✓ `List` (the platform's: UICollectionView, RecyclerView)                       |
| Grids                               | `LazyVGrid`                        | `FlatList numColumns`                  | ✓ `ListProps.Columns`, `Gap`                                                    |
| Safe area, keyboard                 | automatic                          | `SafeAreaView`, `KeyboardAvoidingView` | ✓ `SafeArea`, `UseEnvironment().Keyboard`                                       |
| Reading a view's size               | `GeometryReader`                   | `onLayout`                             | ✓ `ViewProps.OnLayout`                                                          |
| Aspect ratio, min/max sizes         | `.aspectRatio`, `.frame(min:max:)` | style                                  | ✓ `Style`                                                                       |

## Controls

|                                       | SwiftUI                                  | Expo                     | RyUI                                                                                                                                                                                                                              |
| ------------------------------------- | ---------------------------------------- | ------------------------ | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Text, styled text, links              | `Text`, `AttributedString`               | `Text`                   | ✓ `Text`, `RichText` (spans, links, inline views, selection), `ryui/markdown`                                                                                                                                                     |
| Buttons (variants, icons, menus)      | `Button`, `Menu`                         | `Pressable`              | ✓ `Button` (filled, tinted, gray, bordered; icon; menu), `Menu`, context menus                                                                                                                                                    |
| Text fields                           | `TextField`, `SecureField`, `TextEditor` | `TextInput`              | ✓ `TextField` (secure, keyboards, return keys, multiline)                                                                                                                                                                         |
| Toggle, slider                        | `Toggle`, `Slider`                       | community                | ✓ `Toggle`, `Slider`                                                                                                                                                                                                              |
| Segmented control                     | `Picker(.segmented)`                     | community                | ✓ `Segmented`                                                                                                                                                                                                                     |
| Date and time                         | `DatePicker`                             | community                | ✓ `DatePicker` (date, time, both)                                                                                                                                                                                                 |
| Progress, spinner                     | `ProgressView`                           | `ActivityIndicator`      | ✓ `Progress`, `Activity`                                                                                                                                                                                                          |
| Wheel and menu pickers, stepper       | `Picker`, `Stepper`                      | community                | ✓ `Wheel`, `Stepper`, a menu from `Button.Menu`                                                                                                                                                                                   |
| Color picker                          | `ColorPicker`                            | community                | ✓ `ColorPicker` (iOS's system picker; on Android, which has none, a Material dialog of Google's palette, with opacity)                                                                                                            |
| Icons                                 | SF Symbols                               | `@expo/vector-icons`     | ✓ `Symbol` (SF Symbols; Material Symbols on Android, a curated set)                                                                                                                                                               |
| Images (remote, cached, placeholders) | `AsyncImage`                             | `expo-image`             | ✓ `Image` (memory and disk cache, `Placeholder`)                                                                                                                                                                                  |
| Video                                 | `VideoPlayer`                            | `expo-video`             | ✓ `Video`                                                                                                                                                                                                                         |
| Web content                           | `WKWebView`                              | `react-native-webview`   | ✓ `WebView` (URL, HTML, messages both ways, scripts before the page's, blocked requests, its state and history, downloads, new windows, cookies, a script's value; each request on Android); `ryui/js` runs JavaScript off screen |
| Maps                                  | `Map`                                    | `react-native-maps`      | ✗ (a native view through `ui.Native` meanwhile)                                                                                                                                                                                   |
| Custom drawing                        | `Shape`, `Path`, `Canvas`                | `react-native-svg`, Skia | ◐ `Shape` and `Path` (lines, curves, arcs, rects, ellipses; fill, stroke, caps, joins; an animatable trim); SVG icons drawn natively (`ryui/svg`); no immediate-mode canvas, no gradient fills                                    |
| Native escape hatch                   | `UIViewRepresentable`                    | native modules           | ✓ `ui.Native` views, `ui.Call` modules                                                                                                                                                                                            |

## Navigation and presentation

|                                             | SwiftUI                          | Expo (expo-router) | RyUI                                        |
| ------------------------------------------- | -------------------------------- | ------------------ | ------------------------------------------- |
| Stacks with native push, pop, back gestures | `NavigationStack`                | Stack              | ✓ `Stack`, `UseNavigator`                   |
| Tabs                                        | `TabView`                        | Tabs               | ✓ `Tabs`                                    |
| Bar buttons, titles, search                 | `.toolbar`, `.searchable`        | header options     | ✓ `ScreenOptions` (`Actions`, `Search`)     |
| Sheets with detents                         | `.sheet`, `.presentationDetents` | modal              | ✓ `Sheet` (`Detents`, `Grabber`)            |
| Alerts, action sheets                       | `.alert`, `.confirmationDialog`  | `Alert`            | ✓ `Alert` (both styles)                     |
| Deep links, universal and app links         | `.onOpenURL`                     | Linking            | ✓ `OnOpenURL`, `ryui.json` scheme and links |
| Pull to refresh, swipe actions              | `.refreshable`, `.swipeActions`  | `RefreshControl`   | ✓                                           |

## Motion

|                            | SwiftUI                                                 | Expo (Reanimated)                      | RyUI                                                                                                                                                                        |
| -------------------------- | ------------------------------------------------------- | -------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Animating changes          | `withAnimation`                                         | `withTiming`, `withSpring`             | ✓ `Animate`, `AnimateWith` (ease, linear, spring; repeat, autoreverse)                                                                                                      |
| Transforms                 | `.rotationEffect`, `.scaleEffect`, `.offset`            | `transform`                            | ✓ `Style.Transform`                                                                                                                                                         |
| Enter and exit transitions | `.transition`                                           | entering/exiting                       | ✓ `Style.Transition` (`Fade`, `Pop`, `SlideUp`, your own)                                                                                                                   |
| Frame-driven animation     | `TimelineView(.animation)`                              | `useFrameCallback`                     | ✓ `OnFrame`                                                                                                                                                                 |
| Shadows, gradients, blur   | `.shadow`, `LinearGradient`, materials                  | style, expo-linear-gradient, expo-blur | ✓ `Shadow`, `LinearGradient`, `Material`                                                                                                                                    |
| Gestures                   | tap, long press, drag, magnify, rotate                  | Gesture Handler                        | ✓ tap, long press, pan, pinch, rotate, swipe                                                                                                                                |
| Shared-element transitions | `matchedGeometryEffect`, `.navigationTransition(.zoom)` | shared transitions                     | ✓ `Style.Match` (a view grows from another's place and size), `ScreenOptions.ZoomFrom` (a pushed screen zooms out of a view: iOS 18's zoom; Material's container transform) |
| Haptics                    | `sensoryFeedback`                                       | `expo-haptics`                         | ✓ `Haptic`                                                                                                                                                                  |
| Reduce Motion              | `accessibilityReduceMotion`                             | `useReducedMotion`                     | ✓ `Environment.ReduceMotion` (iOS transitions dissolve)                                                                                                                     |

## Data and services

|                                  | SwiftUI / Apple                                       | Expo                                                 | RyUI                                                                                                                                                                                               |
| -------------------------------- | ----------------------------------------------------- | ---------------------------------------------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| State                            | `@State`, `@Observable`                               | hooks                                                | ✓ signals (fine-grained: a write updates only what reads it)                                                                                                                                       |
| Small persisted values           | `@AppStorage`                                         | AsyncStorage                                         | ✓ `ryui/store`                                                                                                                                                                                     |
| SQLite                           | SwiftData / GRDB                                      | `expo-sqlite`                                        | ✓ `ryui/sqlite` (migrations, typed queries, transactions, writes off the main thread, live queries that run again when a table they read is written, also with arguments that change: `WatchWith`) |
| Secure storage                   | Keychain                                              | `expo-secure-store`                                  | ✓ `ryui/secure`                                                                                                                                                                                    |
| Files, network                   | Foundation                                            | `expo-file-system`, fetch                            | ✓ Go's `os`, `net/http`                                                                                                                                                                            |
| Photos                           | `PhotosPicker`                                        | `expo-image-picker`                                  | ✓ `device.PickPhotos`                                                                                                                                                                              |
| Camera                           | AVFoundation                                          | `expo-camera`                                        | ✓ `ryui/camera`: `Take` (the system's camera), `View` (a live preview, front or back), `Capture`; `ryui/camera/record` records video with sound                                                    |
| Barcodes, QR codes               | AVFoundation, Vision                                  | `expo-camera` scanning                               | ✓ `ryui/scan`: live in `camera.View` (`Scan: scan.Codes(...)`) and in image files (`scan.File`); ML Kit on Android, only in the apps that import it                                                |
| Location                         | Core Location                                         | `expo-location`                                      | ✓ `ryui/location`                                                                                                                                                                                  |
| Local notifications              | UserNotifications                                     | `expo-notifications`                                 | ✓ `ryui/notify` (local; no push yet)                                                                                                                                                               |
| Push notifications               | APNs                                                  | `expo-notifications`                                 | ✗                                                                                                                                                                                                  |
| Clipboard, share sheet, browser  | `UIPasteboard`, `ShareLink`, `SFSafariViewController` | `expo-clipboard`, `expo-sharing`, `expo-web-browser` | ✓ `Copy`, `device.Clipboard`, `Share`, `device.Browse`                                                                                                                                             |
| Keep awake, device info          | `isIdleTimerDisabled`, `UIDevice`                     | `expo-keep-awake`, `expo-device`                     | ✓ `device.KeepAwake`, `device.GetInfo`                                                                                                                                                             |
| Network state                    | `NWPathMonitor`                                       | `expo-network`                                       | ✓ `ryui/network` (online, Wi-Fi or cellular, metered, Data Saver / Low Data Mode)                                                                                                                  |
| Saving to the gallery, Downloads | `PHPhotoLibrary`                                      | `expo-media-library`                                 | ✓ `ryui/library` (MediaStore's collections; Photos, or Documents in Files)                                                                                                                         |
| Work in the background           | `beginBackgroundTask`, `BGTaskScheduler`              | `expo-task-manager`                                  | ◐ `ryui/background`: Android keeps the app running, its progress and buttons in a notification; iOS gives about 30 seconds; no scheduled tasks                                                     |
| Receiving shares from other apps | Share extension                                       | share intent (community)                             | ✓ `ui.OnShare` (text and links, `ryui.json`'s `shareTarget`; on iOS a share extension that opens the app)                                                                                          |
| Audio                            | AVAudioPlayer                                         | `expo-audio`                                         | ✓ `ryui/audio` (load, play, pause, seek, volume, loop); no recording                                                                                                                               |
| Custom fonts                     | `.font(.custom)`                                      | `expo-font`                                          | ✓ `Font.Family`                                                                                                                                                                                    |
| Lifecycle                        | `scenePhase`                                          | AppState                                             | ✓ `app.OnLifecycle`                                                                                                                                                                                |

## Appearance and accessibility

|                                     | SwiftUI                                                  | Expo                       | RyUI                                                                            |
| ----------------------------------- | -------------------------------------------------------- | -------------------------- | ------------------------------------------------------------------------------- |
| Dark mode, semantic colors, accent  | automatic                                                | `useColorScheme`           | ✓ semantic colors, `ryui.json` accent (Material 3 dynamic color)                |
| Dynamic Type, font scale            | automatic                                                | `allowFontScaling`         | ✓ text styles and custom fonts scale                                            |
| Screen reader labels, roles, hiding | `.accessibilityLabel`...                                 | `accessibilityLabel`...    | ✓ `Label`, `Hint`, `Role`, `Decorative`; pressable views are buttons            |
| Announcements                       | `AccessibilityNotification.Announcement`                 | `announceForAccessibility` | ✓ `Announce`                                                                    |
| Custom actions, adjustable          | `.accessibilityAction`, `.accessibilityAdjustableAction` | `accessibilityActions`     | ✓ `ViewProps.Actions`, `OnAdjust` (and List swipe actions are TalkBack actions) |

## Tooling

|                            | Xcode / Expo             | RyUI                                                                              |
| -------------------------- | ------------------------ | --------------------------------------------------------------------------------- |
| Live reload, keeping state | previews, Fast Refresh   | ✓ `ryui dev`: the Go code runs on the Mac, reloads on save, keeps signals' values |
| Release builds and signing | Xcode, EAS Build         | ✓ `ryui build -ios` (.ipa), `ryui build -android` (.aab, .apk)                    |
| GUI tests                  | XCUITest, Detox, Maestro | ✓ `e2e/` (both platforms; a CI job for each, not yet run on GitHub)               |
| Benchmarks                 | Instruments              | ✓ `bench/` (against SwiftUI and Compose)                                          |
| Over-the-air updates       | —                        | EAS Update: ✗                                                                     |

## Not yet

In order of what apps miss most: push notifications and maps (both set
aside for now: push needs an APNs key and a Firebase project, Android's
maps a provider's choice). Maps can be an app's own native view through
`ui.Native` until then.
