peach-kit
peach-kit is what the simulator gives your stub files beyond react and
react-native: the iOS feedback generators, iOS spring physics, a progressive
blur, SwiftUI’s numeric text transition, SwiftUI views, and access to the
simulated device. Use it when you write a module or view file for
peach.config.ts, so your replacement
behaves the way the native code it stands in for does.
You do not install peach-kit. Peach supplies each entry point at runtime to
the files your peach.config.ts names, and your app never bundles it. A stub
file may import peach-kit and any of the subpaths below.
| import | what it gives you |
|---|---|
peach-kit/haptics | the iOS impact, notification and selection feedback generators |
peach-kit/animation | SwiftUI, UIKit and Core Animation springs as Animated.spring parameters |
peach-kit/materials | ProgressiveBlurView, a variable blur with a gradient mask |
peach-kit/text | NumericText, SwiftUI’s numeric content transition |
peach-kit/ui | SwiftUI views, the same set Peach renders for @expo/ui/swift-ui |
peach-kit/runtime | the platform, permissions, notifications and alerts of the simulated device, and React bound to your app |
peach-kit | peach-kit/runtime and peach-kit/ui together |
haptics, animation and runtime are always loaded. materials, text and
ui load on demand the first time your app or a stub file needs them. The
package’s two other entry points, peach-kit/native-state and
peach-kit/internal, belong to the engine and are not available to stub
files.
peach-kit/haptics
The iOS feedback generators. Every haptics library Peach supports plays through this same implementation. A browser has no Taptic Engine, so each call plays a vibration where the browser can vibrate and always shows the tap on the device frame. Like the real generators, these never throw.
| export | what it does |
|---|---|
haptics.impact(style) | UIImpactFeedbackGenerator(style:).impactOccurred(). style is 'light', 'medium', 'heavy', 'soft' or 'rigid'. |
haptics.notification(type) | UINotificationFeedbackGenerator().notificationOccurred(_:). type is 'success', 'warning' or 'error'. |
haptics.selection() | UISelectionFeedbackGenerator().selectionChanged() |
playHapticPattern(pattern) | plays a raw pattern: one pulse in milliseconds, or alternating pulse and pause milliseconds. Use it for Android haptic constants and pattern-based libraries. |
hapticStrength(pattern) | the 0 to 1 strength the device frame draws for a pattern |
isImpactStyle(value), isNotificationType(value) | narrow a string your native prop or method argument carried |
Types: ImpactStyle, NotificationType, HapticPattern.
peach-kit/animation
iOS springs as the physical spring Animated.spring takes. Peach’s
Animated.spring solves the spring in closed form, the way
CASpringAnimation does, so a spring converted here moves exactly as the iOS
spring does.
spring(parameters) returns { mass, stiffness, damping } from any of three
forms:
| parameters | iOS API |
|---|---|
{ response, dampingFraction } | SwiftUI Spring(response:dampingFraction:). A dampingFraction of 1 is critically damped. |
{ duration, bounce? } | SwiftUI Spring(duration:bounce:) and UIKit UIView.animate(springDuration:bounce:). A bounce of 0 is critically damped, up to 1 is bouncier, below 0 is overdamped. |
{ mass?, stiffness, damping } | CASpringAnimation. mass defaults to 1. |
springResponse({ duration, bounce }) converts a duration and bounce into the
equivalent { response, dampingFraction }.
UIKit’s older UIView.animate(withDuration:usingSpringWithDamping:) fits its
spring to a total duration through an undocumented mapping, so it has no form
here.
Types: SpringParameters, SpringPhysics.
peach-kit/materials
ProgressiveBlurView reproduces a UIKit UIVisualEffectView whose backdrop
uses a variable blur with a gradient mask: the blur radius changes along the
mask, and children draw above the blurred backdrop.
Point your app’s native view at that file:
Your app’s requireNativeComponent('AppHeaderBlur') now renders the
replacement with the props your app passes. If your native view names its
props differently, translate them to these:
| prop | type | meaning |
|---|---|---|
maxRadius | number | maximum blur radius, in points |
direction | 'top' | 'bottom' | 'center' | where the mask reaches its maximum radius |
startOffset | number | the opaque plateau at the blurred edge, as a fraction. For center, the clear inset, from 0.01 to 0.3. Defaults to 0. |
blurStyle | BlurStyle | the UIBlurEffect.Style. Defaults to 'regular'. |
style | view style | layout, clipping and other view styling |
ProgressiveBlurView accepts every other View prop and a ref to the view.
BlurStyle covers the five system materials (systemUltraThinMaterial,
systemThinMaterial, systemMaterial, systemThickMaterial,
systemChromeMaterial), their Light and Dark variants, and extraLight,
light, dark, regular and prominent. The variable blur replaces the
material’s overlays, so it adds no material tint. The mask follows the cubic
gradient of @sbaiahmed1/react-native-blur 6.0.1, and the center direction
uses two fixed 20% fade bands around an opaque center. The view is checked
against native captures on iPhone 17 Pro.
Types: ProgressiveBlurViewProps, BlurStyle.
peach-kit/text
NumericText shows a formatted number and plays SwiftUI’s numeric content
transition when its value changes inside an animation. Use it when your native
view displays a number with .contentTransition(.numericText()).
| prop | type | meaning |
|---|---|---|
value | number | the value ContentTransition.numericText(value:) animates |
format | Intl.NumberFormatOptions | how the number is displayed, including currency |
countsDown | boolean | selects SwiftUI’s numericText(countsDown:). Omit it to use the value: form. |
animation | SwiftAnimation | null | the withAnimation transaction, such as { kind: 'default' }. kind is 'default', 'timing', 'spring', 'interpolatingSpring' or 'repeat', with the matching SwiftUI parameters. Omit it to inherit the surrounding transaction; null turns the transition off. |
NumericText accepts every other Text prop and a ref to the text. A string
child replaces the formatted value with text your native code already
formatted.
Types: NumericTextProps, SwiftAnimation.
peach-kit/ui
The SwiftUI views Peach renders when an app uses @expo/ui/swift-ui, available
to your stub files directly. Use them when the native view you are replacing is
built in SwiftUI, so your replacement draws the same controls. Put them inside
a Host, as with @expo/ui.
The views follow the @expo/ui/swift-ui API:
- +Hosting and layout:
Host,HStack,VStack,ZStack,LazyHStack,LazyVStack,Grid,LazyHGrid,LazyVGrid,Group,Spacer,Divider,GeometryReader,ScrollView,GlassEffectContainer,Namespace - +Lists and forms:
List,ListForEach,Form,Section,GroupBox,DisclosureGroup,LabeledContent,Label,SwipeActions,ContentUnavailableView - +Controls:
Button,Toggle,Switch,Slider,Stepper,Picker,SegmentedControl,DatePicker,MultiDatePicker,ColorPicker,Checkbox,TextField,SecureField,Gauge,ProgressView,ControlGroup,PageControl,TabView,Link,ShareLink - +Menus and presentation:
Menu,ContextMenu(withTrigger,ItemsandPreview),Popover,BottomSheet,FullScreenCover,Alert,ConfirmationDialog - +Content and shapes:
Text,Image,Chart,Map,LinearGradient,Rectangle,RoundedRectangle,UnevenRoundedRectangle,ConcentricRectangle,Capsule,Circle,Ellipse,Path,Overlay,Background,Mask,RNHostView
peach-kit/ui also exports the modifier names @expo/ui has, such as
padding, frame and foregroundStyle, so imports written for @expo/ui
resolve. In a stub file they are placeholders that the views ignore: lay out
and style with the views’ own props and style. Its other exports are helpers
Peach’s own adapters share, and are not a stable API.
peach-kit/runtime
Read access to the simulated device, for stub files whose native side would ask the operating system something.
| export | what it returns |
|---|---|
getPlatform() | 'ios' or 'android', the platform of the simulated device |
getSystemPermissions() | the device’s permissions: read a permission’s status, check one, or request it |
getNativeNotifications() | the device’s notification center, for presenting and dismissing notifications |
getNativeAlerts() | the platform alert presenter, with Alert.alert’s arguments |
getReactNative() | the react-native module your app runs on |
getEngine(), getEngineConfig(), getOptionalEngineConfig() | the simulator’s full service object, which Peach’s own stubs build on |
peach-kit/runtime also re-exports React (useState, useEffect,
createElement, Fragment, forwardRef, memo and the rest of the React 19
API, plus the default export) bound to the React instance your app runs on.
A stub file’s react import already resolves to that same instance, so you
only need these when writing a module that cannot import react itself.
Related
- +Custom stubs: where these files go and what else they may import
- +PeachConfig: the
packagesslot