A7KDQ~9%|TM^29c*zlPLJ9wJB3<$mRX)
zkP6CQythd$Qx3B{)bv-h;6}a&m7|48sa>~MCYXldLxSWC1wYyjf{TYPd8A&ZNvUED
zOe49xgUGJurTxJxixxY$4FXOVdGeVWRTqt7!KBZqC{Ou`lejwToIwOa4mqNRu_BL2
zo$kFbp#;>Xn+LmE7fxF3)Z7g7L>q{LJmBUCu0Z*#
zfZen8w~&uK((F&0bjDUyOtr4$%gQp*tv14ry-Qw~x}P&29xvH{-RcRlPOQ;=RYN@S-*$$6LLCTyv|941zJ91VuA$MI^H>o>-a;7
z_qoCQb0lNWoBLDrU$AY39zCl70uVWjtON|9GvB>`qlm(0DTp}=RBk>H(ZkJ(_70#4
zM}of3Gf81@AM18%HircZPzb@m=fJCI4{3iJ&GRXPmBY3#B-MVasTpz3mau{+m`|Ml
z-1nnpQN7k|U1m3~WPG8z&HEz?^@|@9ev+@3Fch-AU9~c;qj1&f2(iru!ssUeo9j37G
z8DLP)cso|{{Zk^}n!$a-K!%ij4h@RNsIr%x9pOxX;8o{4f<(Ehs6@I{%7^VGk{A-U
zs-G`ZP=(R-%H-jXC9&!{AtGcNdj)OyKMJTCIU3#w3+?
zNdWGg>8`bX8`R~X^D!TP%qH>qHOOm;<4j3A(Q3`y_vl^d&?ciytlc*xv+i*0!_!Vi
zABEvOb_4i3`JN@9*GH%W|L~b$v7ql_iNk^LYxkGzfL4!~LIt&*p~Ut;g1OKzHxU5E
z3+29Ik(H~MO8@rPBv_HJ5iKO;3;P3>wF;5EvEO_qrt|`-KXkA^^-r#)I}U3Ga9TYP
zXBc;o6r;8lI~iLerZQPFIwirj3n+h=HZWhct5c$EXII&m65pYG-nvb@q2~gp{W?AW
z8mmhUj3idyZ6Yt`lO0=2R3~=;(47IpH7y#NvO{)*2jE%X!fM~^ZBy6by~ezx_@j*}
z`9H{Q4qJ!m-Z$eGOG-UKrvx6R%zRA&4g3*FL*|THV4n%%hYN{VTrFGtDM*paqv%LS
zDUzJ5F7OYesECvnX^yU04eT7kYVdI7A7=|d=zAm7CI6f~6ELT1)o|g=bSm1RtA2WAAv)ap
z$=&LBgQza8MW56aUZBV>@tAW`(@%0|>3)8KIi+=AJiR+nfo(*_r*k+MTZ@%*GTPT
z+nsE`5(XpNn*1;$EJoKe`mabL$l)n21V{EobmbofLc7CE2LeO&NvisHLUI%P1+|@E
z^G1fGP1#8>^^0N(UH|ezl0R=HWEDf_lrrcRVqDt(4#v|lg?0@PeVG;E{=8UEIxGZK
z*}%9;e8G@2w*ptCKi&wPbwzTN;@O{2RlOGQ*2^r=@hVTOP$Bj_6y#hn&A`{h7Z#W3$}%Idt>Bwn45&KzN|kkww6&4xt?gxcYu;1>i!d-w2gY6OGvJ>m>(6@;m?
zaIqFL5?sBR0I5kA&*AV;3aek>H+ZQ&T7G!Y130;1(1~Lu{ViV0|IRJJam=NhOT^?$
zx?feO7VXxrwkFFhodE~}SizwjX6c~PVOds8!gQ#GTu83Ii(sk6u0Q`|s!aMlk@Qu|
zpXdiYWc>RZoBP@aDCR3>7PB9D(fx0UsoFuD{$3W_a{K+|SJ$QBH!;{fM4<7A3&9`O
zV7;5VeA%XZ;~EV7aOn~{c*1UdfMl<2S|x9X%G;yJ<%kj&&aw1RECE4<8hy+I4y{~w
zVSqF%`8^I)>d&E<3tI;za^PDm#%5_k?O2Az#VnP088%|yti~yT#w%y%Zw9%!l%AtK
zsiO7jfpM?OC*dW;u8uD5akLs6S~B|mi;E#Rw6nC9O6kiZVWn+EBu{qIa2Uk+#-VcO
z*H;&fj$#8Mif`qhWjsYM6osJYqu#t4)E`=&aL=%G)fah_SI5tfocwK#8LP(Z3mpx>
zf?*>UN|#KX*_*o`;i3p4s6Vy7!7>X^wZ^o*PQ7e5xx0A~$ZA_4cfsWF0FbeP`o`kN
z+Q^!j2-@H@1ap{5Ok=Ipx3l?rCBFFhaVHKZp*?*+yNRUtQ8{^4IIcn7PC)Kp#aNhm
zb~u+Y!`5*^3GlI3
zq1+j*d2BjEKb)-cq7!1L#+zWQ#_bo*uy*JolbP3WN);=#w@5qJcvq?~5)=`+>a1#)
zv8)x6UNuHnyn?vzl;iU2l6pq7^lh
zB!gseofu4>Q_J-EA1S)<4p*cvsk)}Mo4EX!CYv#2SK6sCk0#|sL{oJFdNA@qgQO;$
zE}8G9w!i*h{fsTxX6gwm=Scm3;0O#Uzl>f4Tm>rSl9upN=wha>{P
zg94NSaX1=Sy)vdt>>j(3v2@>Ny#(3(xLlAqvHfu(gIL+ZU$~d9Mx`1Os?FnvL#j57
z?Z3Zu|D{JsZTpi?aFnk~lZ-n3w7yu+a+c}UjpgJY4hy^Cj?D6xw#;Rwcm(_V&o2V7
zm;wQU+GU?z?7hE)Qc*LT=X^SChIBa(X@BV5CQ2FmLN@!%#N>5(d%j$Zw%YqwYEg|V
zvwr*c-?;-#6SU4((c4oQ#qLZ>bzNT(Bie=!Ennw992>k)tnsVRe$Q!1#2{y(cx%%1
zx$4x1RKaRV4WedrTK}sQQ0vN{^^TY8vPn&A-TkA9r?my-nSU_#ugHTN(j9i~YgN}_Hk((Q2IU-3
zpVmEWm(X;kAvm@uF6Y~X)VEJWz_uk+$EYY^g{=k6Xj>})f=7W~X
zAO_!(!u-O2X1a3e)P-PL^^*yLX-3-sb8P9VR_G
zEQA`2N7y*+F~bJGIZTxlR&d9!t1X(}B>MJ#kP_QQ&=_QlFopY=U)wkGiLPZuRp6<$
z-SZsyy`U0gWDS&;7JJ&8sFrzFOSj1UH7T4JIRNW|gB=-T`F50P)7##IT_p3Gl(Tc$
z6bgPWYj0?LHe=&eaxWMgO{Nc;4zi1uP9~?y5<6qT-LHzpEx|d2=Yqp=&OP@$q80rh
zmP64>seZ%b8p;#0h7#^beFUMQxIjU9@(dB7w8dq{9|`6?yLoY|13asCU%IB`k5J_F
z#f`I`WoUlvN737eh7p4Q-Y+O7G6pk}Xx;g>v{X1{vh&51R!Q=h`lkJFQ8^QoqDS)0
z=XV<#N>BPTEdpm}{Mo7JZBiRbIdB_%675tuWd^%%&?nG$#rzHf&Rbf)K&HQ3IZJIf
zFcJxsy5O)rx~#2A#(rmst%kSFv^$$VBjev@i-p4y`o9EMEtI_-8yjnmAemWN
z3Cofmz5oox0XQEoYWv4=MmzXgIk9#mA45fCCp*pwl-)~=jb(W0cKi$dwp#>dE&u~N
z4uQzDAVMb6g6aCS13<)7y$a~n4_gbg#G{W#4P*mrYOT%H*_S$12QGY>nb1DkpGxN!
zRQ4*7ioYfD@fpml5e5VGD&`#gj<-Pj1{7&rE^vmVFLb%L)ZPnpT(?OCADs7dP_;94
zZZ$zrG+Xi!je{4RPz~Hjxxa}Y5-}aK=$PycntFdH)@oV6*_c*gY=HfD!SR7WYw(cY
zX+0F_yZQM2G>%rUbDsl?_)AoI(h++UC!%gzctGkJKlJu)T>a1HVtB(%p1fjdx=bJXq?vy&&4CRf6ih*}0}Hvi0?dhjeVJLQ(R{8VvpQfdA%0vS2cG}`(FSzFviz^=t=To`QyiP66muH
z+73O6LTZYfDOZ5KFYnFbWFrc0Q^i1t29RW{UZmjIA{9H%M>MYxTy8#=Fr3K|eIrSS
zrb6J}vL0WZeX!j7JO_&PGy8)r9oJ>wh;0kGH`+#17I6ML-v5M@%+<7Nu0so-)_Lx#
zJc}cZV9x2Ve&%?540x0*rwiYzf0RvK@-iiH2*goMuI9LDGOarpiT8lX$erW_8#Myu
zzhUEYc0V-?DyEIkUUP&4ww*@V?Y{BcYDS@R)WNl)8Gj>etCjd5F0H4?Fr`Iz!J@UZ
z?aFzJ<`l4CF6)4(8AlW}IQA1t-o?aiatU~Z9$Wz>`37#)K6ZU{Y0WeMAz*f$GWBitX%^rrO%3)O^>9`$
zmglhZO3eF3k72r~3fi`ht=v|4cSds#G{^IX)qRA^OG_;*=;bJN7pX(ueBwKVwZkCu
z@bo8DW7w!l23wpeb1$YYhUK^Fy^7VGiKd}IM^I|+WRUI6`r+3ml3o8fRqfX;I)G5n
zjpG8&9d?qZgeM*&t-6%~_$=?!uWltK^}J7gl&5|XAEGcmqDH9&bo(Cu3+
zny79QcK8sD;yN*pqEl0y(Et&u`GNHn#EVVw!&vvTSti;Y+-C6j1Ln{XKqamt7UT(N
zmspcdFz)uTHw7vT<*n8Nz8vw@K(6s`b0H*cCtmmpZIw)^_qgXMT7p|O-HF_7k=3wd
zZ@9yP55SJ7=N4_ZT+LF7@CG
zhd<6SohQ_jnkwR^%Hq2Ik!_Ff;+|b&Pz6Pp
z8j=%1A=9I0XuYY~v{4#oI-DU%ctDm%t<&E|8$dT3wI28|$1E>}$2ACvyCF
zp$k-JNHRM*Df~3BK=*=pX5Goi;#m$FL;O^Um`{*6x>f5&cr>tl0Ng2hC+D5GS`?*Y
zyddQA;j^1rKt=G)hT7=|Cal-=Akb(dp;=Ub79}g&DOUpGu4UD<16omI+tx7Fjzc1pD5VE`GIqYW6G3>RMnFmoS
z1iUQC07lmU2_Y=SCxYVq{O1?61^Ln%rr0#XyDPYGkHIIl6Sk@1SXuXlebvf>IQ}wL
z`B75bzC>XLwQ(nm9w2LbF&bAQovG_idRKpmY=W~ekZobuy}15P=3BiP-e`h!T?4xE
ze34>%yo^+S!yr<9he@nI{8TMK?JM1H>uE1zNKu0w0};U51|AR;_ksxWWa0>vA1Kj{
z%U)1DyeAZEsyFWh=JxNA~>
zFB&H%bL>KqH>5hl+5arA5hQOWx@pN@>i=Z-rXto*y6OzMnQzln^8~jFJ$a|w>qik)
z!{vJuN&Ukm+L$IXt@^
zKkx{jeZ~0ANzHO8=qOcnuwcVL9Bs{m1L@uXiL?0}NZE%svB}*pQrE+^A*W%##p#aD
zTmcaVYezud>WomCk5ZD{A(bHKb}6Ydpk^};YqCuUiY;(1#2^z{9`xpJsvvBJB@G^S
zlSsn>yB}=^#!Cq*=<-h>Bi3~nzjjyK1`V+>!aFQ$1)gObPZ*asxdL?U%Z}5mVch)p
zc0GyjuuVX@XvE_bzd+8EnjC$f@l1y0=d$q;V&}{sHs#jEd6c}S^8{PkgBNnh^{&OP
zJ*CHS`_K8$-}S}WX` for custom label style. ([#46288](https://github.com/expo/expo/pull/46288) by [@kudo](https://github.com/kudo))
- [universal] Added `` for custom label style. ([#46288](https://github.com/expo/expo/pull/46288) by [@kudo](https://github.com/kudo))
### 🐛 Bug fixes
+
### 💡 Others
- [universal] Revamp web universal components (`Button`, `Checkbox`, `Picker`, `Slider`,`Switch`,`TextInput`,) with shared design tokens, light / dark themes, and keyboard focus styles. ([#46258](https://github.com/expo/expo/pull/46258) by [@zoontek](https://github.com/zoontek))
diff --git a/packages/expo-ui/android/src/main/java/expo/modules/ui/ExpoUIModule.kt b/packages/expo-ui/android/src/main/java/expo/modules/ui/ExpoUIModule.kt
index 4d8a2953f2fcaf..573dc4fe9fa92c 100644
--- a/packages/expo-ui/android/src/main/java/expo/modules/ui/ExpoUIModule.kt
+++ b/packages/expo-ui/android/src/main/java/expo/modules/ui/ExpoUIModule.kt
@@ -610,6 +610,20 @@ class ExpoUIModule : Module() {
}
}
+ ExpoUIView("NavigationBarView") {
+ Content { props ->
+ NavigationBarContent(props)
+ }
+ }
+
+ ExpoUIView("NavigationBarItemView") {
+ val onButtonPressed by Event()
+
+ Content { props ->
+ NavigationBarItemContent(props) { onButtonPressed(Unit) }
+ }
+ }
+
ExpoUIView("SpacerView") {
Content { props ->
SpacerContent(props)
diff --git a/packages/expo-ui/android/src/main/java/expo/modules/ui/NavigationBarView.kt b/packages/expo-ui/android/src/main/java/expo/modules/ui/NavigationBarView.kt
new file mode 100644
index 00000000000000..83101fb1411db4
--- /dev/null
+++ b/packages/expo-ui/android/src/main/java/expo/modules/ui/NavigationBarView.kt
@@ -0,0 +1,95 @@
+package expo.modules.ui
+
+import android.graphics.Color
+import androidx.compose.material3.NavigationBar
+import androidx.compose.material3.NavigationBarDefaults
+import androidx.compose.material3.NavigationBarItem
+import androidx.compose.material3.NavigationBarItemDefaults
+import androidx.compose.material3.contentColorFor
+import androidx.compose.runtime.Composable
+import androidx.compose.ui.graphics.Color as ComposeColor
+import androidx.compose.ui.unit.dp
+import expo.modules.kotlin.records.Field
+import expo.modules.kotlin.records.Record
+import expo.modules.kotlin.types.OptimizedRecord
+import expo.modules.kotlin.views.ComposeProps
+import expo.modules.kotlin.views.FunctionalComposableScope
+import expo.modules.kotlin.views.OptimizedComposeProps
+
+@OptimizedRecord
+data class NavigationBarItemColors(
+ @Field val selectedIconColor: Color? = null,
+ @Field val selectedTextColor: Color? = null,
+ @Field val selectedIndicatorColor: Color? = null,
+ @Field val unselectedIconColor: Color? = null,
+ @Field val unselectedTextColor: Color? = null,
+ @Field val disabledIconColor: Color? = null,
+ @Field val disabledTextColor: Color? = null
+) : Record
+
+@OptimizedComposeProps
+data class NavigationBarProps(
+ val containerColor: Color? = null,
+ val contentColor: Color? = null,
+ val tonalElevation: Float? = null,
+ val modifiers: ModifierList = emptyList()
+) : ComposeProps
+
+@OptimizedComposeProps
+data class NavigationBarItemProps(
+ val selected: Boolean = false,
+ val enabled: Boolean = true,
+ val alwaysShowLabel: Boolean = true,
+ val colors: NavigationBarItemColors = NavigationBarItemColors(),
+ val modifiers: ModifierList = emptyList()
+) : ComposeProps
+
+@Composable
+fun FunctionalComposableScope.NavigationBarContent(props: NavigationBarProps) {
+ val resolvedContainerColor = props.containerColor.composeOrNull ?: NavigationBarDefaults.containerColor
+ val modifier = ModifierRegistry.applyModifiers(props.modifiers, appContext, composableScope, globalEventDispatcher)
+
+ NavigationBar(
+ modifier = modifier,
+ containerColor = resolvedContainerColor,
+ contentColor = props.contentColor.composeOrNull ?: contentColorFor(resolvedContainerColor),
+ tonalElevation = props.tonalElevation?.dp ?: NavigationBarDefaults.Elevation
+ ) {
+ Children(UIComposableScope(rowScope = this@NavigationBar), filter = { !isSlotView(it) })
+ }
+}
+
+@Composable
+fun FunctionalComposableScope.NavigationBarItemContent(
+ props: NavigationBarItemProps,
+ onClick: () -> Unit
+) {
+ val iconSlotView = findChildSlotView(view, "icon")
+ val labelSlotView = findChildSlotView(view, "label")
+ val modifier = ModifierRegistry.applyModifiers(props.modifiers, appContext, composableScope, globalEventDispatcher)
+ val label: (@Composable () -> Unit)? = labelSlotView?.let { slot -> { slot.renderSlot() } }
+ val rowScope = composableScope.rowScope ?: return
+
+ with(rowScope) {
+ NavigationBarItem(
+ selected = props.selected,
+ onClick = onClick,
+ icon = {
+ iconSlotView?.renderSlot()
+ },
+ modifier = modifier,
+ enabled = props.enabled,
+ label = label,
+ alwaysShowLabel = props.alwaysShowLabel,
+ colors = NavigationBarItemDefaults.colors(
+ selectedIconColor = props.colors.selectedIconColor.composeOrNull ?: ComposeColor.Unspecified,
+ selectedTextColor = props.colors.selectedTextColor.composeOrNull ?: ComposeColor.Unspecified,
+ indicatorColor = props.colors.selectedIndicatorColor.composeOrNull ?: ComposeColor.Unspecified,
+ unselectedIconColor = props.colors.unselectedIconColor.composeOrNull ?: ComposeColor.Unspecified,
+ unselectedTextColor = props.colors.unselectedTextColor.composeOrNull ?: ComposeColor.Unspecified,
+ disabledIconColor = props.colors.disabledIconColor.composeOrNull ?: ComposeColor.Unspecified,
+ disabledTextColor = props.colors.disabledTextColor.composeOrNull ?: ComposeColor.Unspecified
+ )
+ )
+ }
+}
diff --git a/packages/expo-ui/build/jetpack-compose/NavigationBar/index.d.ts b/packages/expo-ui/build/jetpack-compose/NavigationBar/index.d.ts
new file mode 100644
index 00000000000000..e84f86222a7eb5
--- /dev/null
+++ b/packages/expo-ui/build/jetpack-compose/NavigationBar/index.d.ts
@@ -0,0 +1,101 @@
+import { type ColorValue } from 'react-native';
+import { type ModifierConfig } from '../../types';
+type SlotProps = {
+ children: React.ReactNode;
+};
+/**
+ * Colors for navigation bar items in different states.
+ */
+export type NavigationBarItemColors = {
+ selectedIconColor?: ColorValue;
+ selectedTextColor?: ColorValue;
+ selectedIndicatorColor?: ColorValue;
+ unselectedIconColor?: ColorValue;
+ unselectedTextColor?: ColorValue;
+ disabledIconColor?: ColorValue;
+ disabledTextColor?: ColorValue;
+};
+export type NavigationBarProps = {
+ /**
+ * Background color of the navigation bar.
+ * @default NavigationBarDefaults.containerColor
+ */
+ containerColor?: ColorValue;
+ /**
+ * Preferred content color inside the navigation bar.
+ * @default contentColorFor(containerColor)
+ */
+ contentColor?: ColorValue;
+ /**
+ * Tonal elevation in dp.
+ * @default NavigationBarDefaults.Elevation
+ */
+ tonalElevation?: number;
+ /**
+ * Modifiers for the component.
+ */
+ modifiers?: ModifierConfig[];
+ /**
+ * Navigation bar items.
+ */
+ children?: React.ReactNode;
+};
+export type NavigationBarItemProps = {
+ /**
+ * Whether this item is currently selected.
+ */
+ selected: boolean;
+ /**
+ * Callback that is called when the item is clicked.
+ */
+ onClick?: () => void;
+ /**
+ * Whether the item is enabled.
+ * @default true
+ */
+ enabled?: boolean;
+ /**
+ * Whether to always show the label.
+ * @default true
+ */
+ alwaysShowLabel?: boolean;
+ /**
+ * Colors for the item in different states.
+ */
+ colors?: NavigationBarItemColors;
+ /**
+ * Modifiers for the component.
+ */
+ modifiers?: ModifierConfig[];
+ /**
+ * Children containing `Icon`, `SelectedIcon`, and `Label` slots.
+ */
+ children?: React.ReactNode;
+};
+/**
+ * Icon slot for `NavigationBarItem`.
+ */
+declare function NavigationBarItemIcon(props: SlotProps): import("react/jsx-runtime").JSX.Element;
+/**
+ * Selected icon slot for `NavigationBarItem`. Falls back to `Icon` when omitted.
+ */
+declare function NavigationBarItemSelectedIcon(props: SlotProps): import("react/jsx-runtime").JSX.Element;
+/**
+ * Label slot for `NavigationBarItem`.
+ */
+declare function NavigationBarItemLabel(props: SlotProps): import("react/jsx-runtime").JSX.Element;
+/**
+ * A Material Design 3 navigation bar.
+ */
+export declare function NavigationBar(props: NavigationBarProps): import("react/jsx-runtime").JSX.Element;
+/**
+ * A Material Design 3 navigation bar item. Must be used inside `NavigationBar`.
+ */
+declare function NavigationBarItemComponent(props: NavigationBarItemProps): import("react/jsx-runtime").JSX.Element;
+declare namespace NavigationBarItemComponent {
+ var Icon: typeof NavigationBarItemIcon;
+ var SelectedIcon: typeof NavigationBarItemSelectedIcon;
+ var Label: typeof NavigationBarItemLabel;
+}
+export { NavigationBarItemComponent as NavigationBarItem };
+//# sourceMappingURL=index.d.ts.map
\ No newline at end of file
diff --git a/packages/expo-ui/build/jetpack-compose/NavigationBar/index.d.ts.map b/packages/expo-ui/build/jetpack-compose/NavigationBar/index.d.ts.map
new file mode 100644
index 00000000000000..4b9f3a464c3653
--- /dev/null
+++ b/packages/expo-ui/build/jetpack-compose/NavigationBar/index.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/jetpack-compose/NavigationBar/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,UAAU,EAAE,MAAM,cAAc,CAAC;AAE/C,OAAO,EAAE,KAAK,cAAc,EAAkB,MAAM,aAAa,CAAC;AAGlE,KAAK,SAAS,GAAG;IACf,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,CAAC;AAOF;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC,iBAAiB,CAAC,EAAE,UAAU,CAAC;IAC/B,iBAAiB,CAAC,EAAE,UAAU,CAAC;IAC/B,sBAAsB,CAAC,EAAE,UAAU,CAAC;IACpC,mBAAmB,CAAC,EAAE,UAAU,CAAC;IACjC,mBAAmB,CAAC,EAAE,UAAU,CAAC;IACjC,iBAAiB,CAAC,EAAE,UAAU,CAAC;IAC/B,iBAAiB,CAAC,EAAE,UAAU,CAAC;CAChC,CAAC;AAEF,MAAM,MAAM,kBAAkB,GAAG;IAC/B;;;OAGG;IACH,cAAc,CAAC,EAAE,UAAU,CAAC;IAC5B;;;OAGG;IACH,YAAY,CAAC,EAAE,UAAU,CAAC;IAC1B;;;OAGG;IACH,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB;;OAEG;IACH,SAAS,CAAC,EAAE,cAAc,EAAE,CAAC;IAC7B;;OAEG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,CAAC;AAEF,MAAM,MAAM,sBAAsB,GAAG;IACnC;;OAEG;IACH,QAAQ,EAAE,OAAO,CAAC;IAClB;;OAEG;IACH,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB;;;OAGG;IACH,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB;;;OAGG;IACH,eAAe,CAAC,EAAE,OAAO,CAAC;IAC1B;;OAEG;IACH,MAAM,CAAC,EAAE,uBAAuB,CAAC;IACjC;;OAEG;IACH,SAAS,CAAC,EAAE,cAAc,EAAE,CAAC;IAC7B;;OAEG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,CAAC;AAuCF;;GAEG;AACH,iBAAS,qBAAqB,CAAC,KAAK,EAAE,SAAS,2CAE9C;AAED;;GAEG;AACH,iBAAS,6BAA6B,CAAC,KAAK,EAAE,SAAS,2CAEtD;AAED;;GAEG;AACH,iBAAS,sBAAsB,CAAC,KAAK,EAAE,SAAS,2CAE/C;AAED;;GAEG;AACH,wBAAgB,aAAa,CAAC,KAAK,EAAE,kBAAkB,2CAOtD;AAED;;GAEG;AACH,iBAAS,0BAA0B,CAAC,KAAK,EAAE,sBAAsB,2CAOhE;kBAPQ,0BAA0B;;;;;AAanC,OAAO,EAAE,0BAA0B,IAAI,iBAAiB,EAAE,CAAC"}
\ No newline at end of file
diff --git a/packages/expo-ui/build/jetpack-compose/index.d.ts b/packages/expo-ui/build/jetpack-compose/index.d.ts
index 1046f05fef6b24..d967890d40437c 100644
--- a/packages/expo-ui/build/jetpack-compose/index.d.ts
+++ b/packages/expo-ui/build/jetpack-compose/index.d.ts
@@ -29,6 +29,7 @@ export { TextField, OutlinedTextField, type TextFieldProps, type TextFieldRef, t
export * from './ToggleButton';
export * from './Shape';
export * from './ModalBottomSheet';
+export * from './NavigationBar';
export * from './Carousel';
export { HorizontalPager, type HorizontalPagerHandle, type HorizontalPagerProps, } from './HorizontalPager';
export * from './SearchBar';
diff --git a/packages/expo-ui/build/jetpack-compose/index.d.ts.map b/packages/expo-ui/build/jetpack-compose/index.d.ts.map
index 9523984cfd4b5f..48109c840de453 100644
--- a/packages/expo-ui/build/jetpack-compose/index.d.ts.map
+++ b/packages/expo-ui/build/jetpack-compose/index.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/jetpack-compose/index.ts"],"names":[],"mappings":"AAAA,OAAO,uCAAuC,CAAC;AAE/C,cAAc,eAAe,CAAC;AAC9B,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAClF,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,WAAW,CAAC;AAC1B,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,OAAO,EACL,SAAS,EACT,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,EAC7B,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC;AACrB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,SAAS,CAAC;AACxB,cAAc,oBAAoB,CAAC;AACnC,cAAc,YAAY,CAAC;AAC3B,OAAO,EACL,eAAe,EACf,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GAC1B,MAAM,mBAAmB,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,KAAK,SAAS,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9C,cAAc,WAAW,CAAC;AAC1B,cAAc,oBAAoB,CAAC;AAEnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,OAAO,CAAC;AACtB,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,YAAY,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC"}
\ No newline at end of file
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/jetpack-compose/index.ts"],"names":[],"mappings":"AAAA,OAAO,uCAAuC,CAAC;AAE/C,cAAc,eAAe,CAAC;AAC9B,cAAc,SAAS,CAAC;AACxB,cAAc,aAAa,CAAC;AAC5B,OAAO,EAAE,gBAAgB,EAAE,KAAK,qBAAqB,EAAE,MAAM,oBAAoB,CAAC;AAClF,cAAc,QAAQ,CAAC;AACvB,cAAc,YAAY,CAAC;AAC3B,cAAc,QAAQ,CAAC;AACvB,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,WAAW,CAAC;AAC1B,cAAc,QAAQ,CAAC;AACvB,cAAc,cAAc,CAAC;AAC7B,cAAc,WAAW,CAAC;AAC1B,cAAc,YAAY,CAAC;AAC3B,cAAc,cAAc,CAAC;AAC7B,cAAc,cAAc,CAAC;AAC7B,cAAc,mBAAmB,CAAC;AAClC,cAAc,YAAY,CAAC;AAC3B,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,UAAU,CAAC;AACzB,cAAc,cAAc,CAAC;AAC7B,OAAO,EACL,SAAS,EACT,iBAAiB,EACjB,KAAK,cAAc,EACnB,KAAK,YAAY,EACjB,KAAK,uBAAuB,EAC5B,KAAK,kBAAkB,EACvB,KAAK,wBAAwB,EAC7B,KAAK,qBAAqB,EAC1B,KAAK,wBAAwB,EAC7B,KAAK,eAAe,GACrB,MAAM,aAAa,CAAC;AACrB,cAAc,gBAAgB,CAAC;AAC/B,cAAc,SAAS,CAAC;AACxB,cAAc,oBAAoB,CAAC;AACnC,cAAc,iBAAiB,CAAC;AAChC,cAAc,YAAY,CAAC;AAC3B,OAAO,EACL,eAAe,EACf,KAAK,qBAAqB,EAC1B,KAAK,oBAAoB,GAC1B,MAAM,mBAAmB,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC;AAC3B,cAAc,mBAAmB,CAAC;AAClC,cAAc,6BAA6B,CAAC;AAC5C,cAAc,wBAAwB,CAAC;AACvC,cAAc,oBAAoB,CAAC;AACnC,cAAc,eAAe,CAAC;AAC9B,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,KAAK,SAAS,EAAE,IAAI,EAAE,MAAM,QAAQ,CAAC;AAC9C,cAAc,WAAW,CAAC;AAC1B,cAAc,oBAAoB,CAAC;AAEnC,cAAc,sBAAsB,CAAC;AACrC,cAAc,OAAO,CAAC;AACtB,cAAc,OAAO,CAAC;AACtB,cAAc,UAAU,CAAC;AACzB,cAAc,WAAW,CAAC;AAC1B,OAAO,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC1C,YAAY,EAAE,SAAS,EAAE,MAAM,UAAU,CAAC;AAC1C,YAAY,EAAE,kBAAkB,EAAE,MAAM,gBAAgB,CAAC"}
\ No newline at end of file
diff --git a/packages/expo-ui/src/jetpack-compose/NavigationBar/index.tsx b/packages/expo-ui/src/jetpack-compose/NavigationBar/index.tsx
new file mode 100644
index 00000000000000..321166d82e43b3
--- /dev/null
+++ b/packages/expo-ui/src/jetpack-compose/NavigationBar/index.tsx
@@ -0,0 +1,174 @@
+import { requireNativeView } from 'expo';
+import { type ColorValue } from 'react-native';
+
+import { type ModifierConfig, type ViewEvent } from '../../types';
+import { createViewModifierEventListener } from '../modifiers';
+
+type SlotProps = {
+ children: React.ReactNode;
+};
+
+type NativeSlotViewProps = {
+ slotName: string;
+ children: React.ReactNode;
+};
+
+/**
+ * Colors for navigation bar items in different states.
+ */
+export type NavigationBarItemColors = {
+ selectedIconColor?: ColorValue;
+ selectedTextColor?: ColorValue;
+ selectedIndicatorColor?: ColorValue;
+ unselectedIconColor?: ColorValue;
+ unselectedTextColor?: ColorValue;
+ disabledIconColor?: ColorValue;
+ disabledTextColor?: ColorValue;
+};
+
+export type NavigationBarProps = {
+ /**
+ * Background color of the navigation bar.
+ * @default NavigationBarDefaults.containerColor
+ */
+ containerColor?: ColorValue;
+ /**
+ * Preferred content color inside the navigation bar.
+ * @default contentColorFor(containerColor)
+ */
+ contentColor?: ColorValue;
+ /**
+ * Tonal elevation in dp.
+ * @default NavigationBarDefaults.Elevation
+ */
+ tonalElevation?: number;
+ /**
+ * Modifiers for the component.
+ */
+ modifiers?: ModifierConfig[];
+ /**
+ * Navigation bar items.
+ */
+ children?: React.ReactNode;
+};
+
+export type NavigationBarItemProps = {
+ /**
+ * Whether this item is currently selected.
+ */
+ selected: boolean;
+ /**
+ * Callback that is called when the item is clicked.
+ */
+ onClick?: () => void;
+ /**
+ * Whether the item is enabled.
+ * @default true
+ */
+ enabled?: boolean;
+ /**
+ * Whether to always show the label.
+ * @default true
+ */
+ alwaysShowLabel?: boolean;
+ /**
+ * Colors for the item in different states.
+ */
+ colors?: NavigationBarItemColors;
+ /**
+ * Modifiers for the component.
+ */
+ modifiers?: ModifierConfig[];
+ /**
+ * Children containing `Icon`, `SelectedIcon`, and `Label` slots.
+ */
+ children?: React.ReactNode;
+};
+
+type NativeNavigationBarItemProps = Omit &
+ ViewEvent<'onButtonPressed', void>;
+
+const NavigationBarNativeView: React.ComponentType = requireNativeView(
+ 'ExpoUI',
+ 'NavigationBarView'
+);
+
+const NavigationBarItemNativeView: React.ComponentType =
+ requireNativeView('ExpoUI', 'NavigationBarItemView');
+
+const SlotNativeView: React.ComponentType = requireNativeView(
+ 'ExpoUI',
+ 'SlotView'
+);
+
+function transformNavigationBarProps(props: NavigationBarProps): NavigationBarProps {
+ const { modifiers, ...restProps } = props;
+ return {
+ modifiers,
+ ...(modifiers ? createViewModifierEventListener(modifiers) : undefined),
+ ...restProps,
+ };
+}
+
+function transformNavigationBarItemProps(
+ props: NavigationBarItemProps
+): NativeNavigationBarItemProps {
+ const { modifiers, onClick, ...restProps } = props;
+ return {
+ modifiers,
+ ...(modifiers ? createViewModifierEventListener(modifiers) : undefined),
+ ...restProps,
+ onButtonPressed: () => onClick?.(),
+ };
+}
+
+/**
+ * Icon slot for `NavigationBarItem`.
+ */
+function NavigationBarItemIcon(props: SlotProps) {
+ return {props.children};
+}
+
+/**
+ * Selected icon slot for `NavigationBarItem`. Falls back to `Icon` when omitted.
+ */
+function NavigationBarItemSelectedIcon(props: SlotProps) {
+ return {props.children};
+}
+
+/**
+ * Label slot for `NavigationBarItem`.
+ */
+function NavigationBarItemLabel(props: SlotProps) {
+ return {props.children};
+}
+
+/**
+ * A Material Design 3 navigation bar.
+ */
+export function NavigationBar(props: NavigationBarProps) {
+ const { children, ...restProps } = props;
+ return (
+
+ {children}
+
+ );
+}
+
+/**
+ * A Material Design 3 navigation bar item. Must be used inside `NavigationBar`.
+ */
+function NavigationBarItemComponent(props: NavigationBarItemProps) {
+ const { children, ...restProps } = props;
+ return (
+
+ {children}
+
+ );
+}
+
+NavigationBarItemComponent.Icon = NavigationBarItemIcon;
+NavigationBarItemComponent.SelectedIcon = NavigationBarItemSelectedIcon;
+NavigationBarItemComponent.Label = NavigationBarItemLabel;
+
+export { NavigationBarItemComponent as NavigationBarItem };
diff --git a/packages/expo-ui/src/jetpack-compose/index.ts b/packages/expo-ui/src/jetpack-compose/index.ts
index a266cab370381c..5aedc12b0e97e1 100644
--- a/packages/expo-ui/src/jetpack-compose/index.ts
+++ b/packages/expo-ui/src/jetpack-compose/index.ts
@@ -41,6 +41,7 @@ export {
export * from './ToggleButton';
export * from './Shape';
export * from './ModalBottomSheet';
+export * from './NavigationBar';
export * from './Carousel';
export {
HorizontalPager,
diff --git a/tools/src/commands/GenerateDocsAPIData.ts b/tools/src/commands/GenerateDocsAPIData.ts
index a3881a444e45c9..7f526272426876 100644
--- a/tools/src/commands/GenerateDocsAPIData.ts
+++ b/tools/src/commands/GenerateDocsAPIData.ts
@@ -131,6 +131,7 @@ const uiPackagesMapping: Record = {
'expo-ui/jetpack-compose/progress': ['jetpack-compose/Progress/index.tsx', 'expo-ui'],
'expo-ui/jetpack-compose/listitem': ['jetpack-compose/ListItem/index.tsx', 'expo-ui'],
'expo-ui/jetpack-compose/modifiers': ['jetpack-compose/modifiers/index.ts', 'expo-ui'],
+ 'expo-ui/jetpack-compose/navigationbar': ['jetpack-compose/NavigationBar/index.tsx', 'expo-ui'],
'expo-ui/jetpack-compose/segmentedbutton': [
'jetpack-compose/SegmentedButton/index.tsx',
'expo-ui',
From d1f582162340fa891f420f12e39a72752e63bd2f Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Szymon=20=C5=9Awierk?=
<58403334+sswrk@users.noreply.github.com>
Date: Wed, 27 May 2026 09:39:32 +0200
Subject: [PATCH 2/7] [docs] EAS CLI build
`--refresh-ad-hoc-provisioning-profile` flag documentation (#46290)
# Why
EAS CLI 19.1.0 adds `eas build --refresh-ad-hoc-provisioning-profile`, so non-interactive CI builds can refresh ad hoc provisioning profiles when an App Store Connect API key is set up. Our docs still say you must run `eas build` interactively after registering a device; this PR documents the new workflow.
# How
- Document the flag, prerequisites, and an example in [internal distribution](https://docs.expo.dev/build/internal-distribution/) (Automation on CI).
- Cross-link from [building on CI](https://docs.expo.dev/build/building-on-ci/).
# Test Plan
Verified it renders correctly:
# Checklist
- [ ] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin).
- [x] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
---
docs/pages/build/building-on-ci.mdx | 2 ++
docs/pages/build/internal-distribution.mdx | 24 +++++++++++++++++++++-
2 files changed, 25 insertions(+), 1 deletion(-)
diff --git a/docs/pages/build/building-on-ci.mdx b/docs/pages/build/building-on-ci.mdx
index 25bc4688a07327..554e66f0b4185f 100644
--- a/docs/pages/build/building-on-ci.mdx
+++ b/docs/pages/build/building-on-ci.mdx
@@ -96,6 +96,8 @@ Using the information you've gathered, pass it into the build command through en
- `EXPO_APPLE_TEAM_ID`: Your Apple Team ID. For example, `77KQ969CHE`.
- `EXPO_APPLE_TEAM_TYPE`: Your Apple Team Type. Valid types are `IN_HOUSE`, `COMPANY_OR_ORGANIZATION`, or `INDIVIDUAL`.
+If you run [internal distribution](/build/internal-distribution/) builds on CI with ad hoc provisioning, refresh the ad hoc provisioning profile so registered devices added after the last build are included. For `eas build`, pass [`--refresh-ad-hoc-provisioning-profile`](/eas/cli/#eas-build) with `--non-interactive`. For [EAS Workflows](/eas/workflows/get-started), set `refresh_ad_hoc_provisioning_profile: true` in the build job's `params` ([build job parameters](/eas/workflows/pre-packaged-jobs#build)). See [Automation on CI](/build/internal-distribution/#automation-on-ci-optional) for requirements and an example command.
+
### Trigger new builds
Now that we're authenticated with Expo CLI, we can create the build step.
diff --git a/docs/pages/build/internal-distribution.mdx b/docs/pages/build/internal-distribution.mdx
index ed9315542d6052..8e08a7a378ecd4 100644
--- a/docs/pages/build/internal-distribution.mdx
+++ b/docs/pages/build/internal-distribution.mdx
@@ -30,7 +30,29 @@ See the tutorial on Internal distribution with EAS Build below for more informat
### Automation on CI (optional)
-It's possible to run internal distribution builds non-interactively in CI using the `--non-interactive` flag. However, if you are using ad hoc provisioning on iOS you will not be able to add new devices to your provisioning profile when using this flag. After registering a device through `eas device:create`, you need to run `eas build` interactively and authenticate with Apple in order for EAS to add the device to your provisioning profile. [Learn more about triggering builds from CI](/build/building-on-ci).
+You can run internal distribution builds non-interactively in CI with the [`--non-interactive`](/eas/cli/#eas-build) flag. [Learn more about triggering builds from CI](/build/building-on-ci).
+
+For iOS ad hoc builds, `eas build --non-interactive` reuses a valid provisioning profile without updating its device list. The build can succeed, but the app may not install on registered devices added after the profile was last updated.
+
+Pass `--refresh-ad-hoc-provisioning-profile` with `--non-interactive` to update the Expo-managed ad hoc provisioning profile on the Apple Developer Portal before the build.
+
+> **info** `--refresh-ad-hoc-provisioning-profile` requires EAS CLI 19.1.0 or later.
+
+EAS authenticates with an App Store Connect API key. It reads devices registered on EAS for your Apple team. It registers any missing UDIDs on the portal. Then it refreshes the profile device list.
+
+When you use this flag, EAS selects all matching devices for the build target's Apple platform: iPhone and iPad for iOS, Mac for macOS.
+
+
+
+For [EAS Workflows](/eas/workflows/get-started), set `refresh_ad_hoc_provisioning_profile: true` in the build job's `params` with the same profile requirements. See the [build job parameters](/eas/workflows/pre-packaged-jobs#build).
+
+The profile must set [`"distribution": "internal"`](/eas/json/#distribution) and use [credentials managed by EAS](/app-signing/app-credentials/). You need at least one device from [`eas device:create`](/eas/cli/#eas-devicecreate) and an App Store Connect API key in CI through [environment variables](/build/building-on-ci/#optional-provide-an-asc-api-token-for-your-apple-team) (`EXPO_ASC_API_KEY_PATH`, `EXPO_ASC_KEY_ID`, and `EXPO_ASC_ISSUER_ID`) or a key stored in EAS for submissions on the project.
+
+Otherwise, after registering a device with [`eas device:create`](/eas/cli/#eas-devicecreate), run [`eas build`](/eas/cli/#eas-build) interactively and sign in with your Apple account so EAS can update the ad hoc provisioning profile.
### Managing devices
From 32e32baceec55080194aedfb0a50c1a725bcdd4d Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?Szymon=20=C5=9Awierk?=
<58403334+sswrk@users.noreply.github.com>
Date: Wed, 27 May 2026 09:42:22 +0200
Subject: [PATCH 3/7] [docs] EAS workflows build job
`refresh_ad_hoc_provisioning_profile` parameter documentation (#46291)
# Why
The base branch documents `eas build --refresh-ad-hoc-provisioning-profile` for internal iOS ad hoc builds on CI and links to EAS Workflows, but the workflows docs did not yet document `refresh_ad_hoc_provisioning_profile` on the build job.
# How
- Document `refresh_ad_hoc_provisioning_profile` on the `build job in pre-packaged-jobs.mdx` and `syntax.mdx` (syntax, parameters table, CLI flag mapping).
- Add an example workflow for an internal iOS build with profile refresh.
# Test Plan
Verified it renders correctly:
# Checklist
- [ ] I added a `changelog.md` entry and rebuilt the package sources according to [this short guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin).
- [x] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
---
.../pages/eas/workflows/pre-packaged-jobs.mdx | 35 ++++++++++++++++---
docs/pages/eas/workflows/syntax.mdx | 1 +
2 files changed, 31 insertions(+), 5 deletions(-)
diff --git a/docs/pages/eas/workflows/pre-packaged-jobs.mdx b/docs/pages/eas/workflows/pre-packaged-jobs.mdx
index 5c3c50bac0620c..ebe138c537c4d3 100644
--- a/docs/pages/eas/workflows/pre-packaged-jobs.mdx
+++ b/docs/pages/eas/workflows/pre-packaged-jobs.mdx
@@ -34,17 +34,19 @@ jobs:
platform: android | ios # required
profile: string # optional - default: production
message: string # optional
+ refresh_ad_hoc_provisioning_profile: boolean # optional
```
#### Parameters
You can pass the following parameters into the `params` list:
-| Parameter | Type | Description |
-| --------- | ------ | ------------------------------------------------------------------------------------------------------------- |
-| platform | string | **Required.** The platform to build for. Can be either `android` or `ios`. |
-| profile | string | Optional. The build profile to use. Defaults to `production`. |
-| message | string | Optional. Custom message attached to the build. Corresponds to the `--message` flag when running `eas build`. |
+| Parameter | Type | Description |
+| ----------------------------------- | ------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
+| platform | string | **Required.** The platform to build for. Can be either `android` or `ios`. |
+| profile | string | Optional. The build profile to use. Defaults to `production`. |
+| message | string | Optional. Custom message attached to the build. Corresponds to the `--message` flag when running `eas build`. |
+| refresh_ad_hoc_provisioning_profile | boolean | Optional. Refreshes the managed ad hoc provisioning profile before an iOS internal build starts. Corresponds to the [`--refresh-ad-hoc-provisioning-profile`](/eas/cli/#eas-build) flag when running `eas build`. See [internal distribution on CI](/build/internal-distribution/#automation-on-ci-optional). |
#### Environment variables
@@ -179,6 +181,29 @@ jobs:
+
+
+This workflow builds your iOS app for internal distribution and refreshes the ad hoc provisioning profile when you push to the main branch.
+
+```yaml .eas/workflows/build-ios-internal.yml
+name: Build iOS internal
+
+on:
+ push:
+ branches: ['main']
+
+jobs:
+ build_ios:
+ name: Build iOS Internal
+ type: build
+ params:
+ platform: ios
+ profile: preview
+ refresh_ad_hoc_provisioning_profile: true
+```
+
+
+
## Deploy
Deploy your application using [EAS Hosting](/eas/hosting/introduction).
diff --git a/docs/pages/eas/workflows/syntax.mdx b/docs/pages/eas/workflows/syntax.mdx
index b75fbb69c76dcf..09f9c1ad7b7a6f 100644
--- a/docs/pages/eas/workflows/syntax.mdx
+++ b/docs/pages/eas/workflows/syntax.mdx
@@ -1038,6 +1038,7 @@ jobs:
platform: ios | android # required
profile: string # optional, default: production
message: string # optional
+ refresh_ad_hoc_provisioning_profile: boolean # optional
```
This job outputs the following properties:
From 8d41c4d706eea97b4cd63e5b2b55a8366ca45f14 Mon Sep 17 00:00:00 2001
From: Self Not Found
Date: Wed, 27 May 2026 07:49:20 +0000
Subject: [PATCH 4/7] [file-system] Make File.write() async (#45992)
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
# Why
Inspired by #44359. The current implementation of `File.write()` could
block JS thread when writing a lot of data. The async `File.write()`
could make the app more responsive.
Plus, the legacy expo FileSystem API `writeAsStringAsync()` and the
`write()` in `react-native-fs` are also async. I think the async API
will help users migrating to the new `expo-file-system`
# How
1. Extract common private function `writeToFile()` for the write logic
for the iOS/Android native code
2. Wrap it with `AsyncFunction("write")`/`Function("writeSync")`
3. Update type definitions and comments in `expo-file-system` package
4. Replace all the old sync `File.write()` with `File.writeSync()`in
tests
5. Add test cases for the new async `File.write()`, with/without
different optiions
6. Update downstream calls of `File.write()` in this repo
7. Update docs
This PR is largely based on the changes in #44359, as this is my first
contribution to `expo` and I need reference implementations.
# Test Plan
1. `CI=1 pnpm build`, `pnpm lint --fix` and `CI=1 pnpm test` passed in
`packages/expo-file-system`
Output
```
> expo-module test
PASS Android src/__tests__/FSNetworkTasks-test.native.ts
PASS Android src/__tests__/FileSystem-test.native.ts
PASS Node src/__tests__/FileSystemWatcher-test.ts
PASS Web src/__tests__/FileSystemWatcher-test.ts
PASS Android src/__tests__/FileSystemWatcher-test.ts
PASS Android src/legacy/__tests__/FileSystem-test.native.ts
PASS iOS src/__tests__/FileSystem-test.native.ts
PASS iOS src/__tests__/FSNetworkTasks-test.native.ts
PASS iOS src/__tests__/FileSystemWatcher-test.ts
PASS iOS src/legacy/__tests__/FileSystem-test.native.ts
Test Suites: 10 passed, 10 total
Tests: 14 skipped, 204 passed, 218 total
Snapshots: 0 total
Time: 1.713 s
Ran all test suites in 4 projects.
```
2. `pnpm test:ios` passed in `apps/bare-expo`
Screenshot
3. `pnpm et check expo-file-system` passed in project root (tested on
e81ba4c0548e008601b36aa8385908a83363d5e8)
Output
```
🛠 Rebuilding expotools
✨ Successfully built expotools
🔍 Checking expo-file-system package
🏃♀️ Running pnpm run clean
🏃♀️ Running pnpm run build
🏃♀️ Running pnpm run test --watch false --passWithNoTests
🏃♀️ Running pnpm run lint --max-warnings 0
✨ expo-file-system checks passed
🔌 Checking expo-file-system plugin
🏃♀️ Running pnpm run clean plugin
🏃♀️ Running pnpm run build plugin
🏃♀️ Running pnpm run test plugin --watch false --passWithNoTests
🏃♀️ Running pnpm run lint plugin --max-warnings 0
✨ expo-file-system checks passed
🏁 All packages passed.
```
# Checklist
- [x] I added a `changelog.md` entry and rebuilt the package sources
according to [this short
guide](https://github.com/expo/expo/blob/main/CONTRIBUTING.md#-before-submitting)
- [ ] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
---
.../src/screens/FileSystemScreen.tsx | 14 +-
apps/test-suite/tests/ContactsNext.ts | 4 +-
apps/test-suite/tests/FileSystem.ts | 259 ++++++++++--------
.../pages/versions/unversioned/sdk/crypto.mdx | 2 +-
.../versions/unversioned/sdk/filesystem.mdx | 8 +-
packages/expo-file-system/CHANGELOG.md | 2 +
.../modules/filesystem/FileSystemModule.kt | 43 +--
.../internal/NativeFileSystem.types.d.ts | 7 +-
.../internal/NativeFileSystem.types.d.ts.map | 2 +-
.../build/legacyWarnings.d.ts | 2 +-
.../ios/FileSystemModule.swift | 41 ++-
packages/expo-file-system/mocks/FileSystem.ts | 9 +-
.../src/__tests__/FileSystem-test.native.ts | 71 +++--
.../src/internal/NativeFileSystem.types.ts | 8 +-
.../expo-file-system/src/legacyWarnings.ts | 2 +-
15 files changed, 288 insertions(+), 186 deletions(-)
diff --git a/apps/native-component-list/src/screens/FileSystemScreen.tsx b/apps/native-component-list/src/screens/FileSystemScreen.tsx
index 1ebbb2f2b83fac..128a460f53388d 100644
--- a/apps/native-component-list/src/screens/FileSystemScreen.tsx
+++ b/apps/native-component-list/src/screens/FileSystemScreen.tsx
@@ -109,10 +109,10 @@ function FileSourcesSection({ setCurrentFile }: { setCurrentFile: (f: File) => v
{
+ onPress={async () => {
const file = new File(Paths.cache, 'test_sandbox', 'test.txt');
file.create({ intermediates: true, overwrite: true });
- file.write('Hello from FileSystem sandbox! Timestamp: ' + Date.now());
+ await file.write('Hello from FileSystem sandbox! Timestamp: ' + Date.now());
setCurrentFile(file);
Alert.alert('Created', file.uri);
}}
@@ -279,7 +279,7 @@ function ReadWriteSection({ withCurrentFile }: { withCurrentFile: WithCurrentFil
{
- file.write('Written at ' + new Date().toISOString());
+ await file.write('Written at ' + new Date().toISOString());
return 'OK - size is now: ' + file.size;
})}
/>
@@ -287,7 +287,7 @@ function ReadWriteSection({ withCurrentFile }: { withCurrentFile: WithCurrentFil
title="write() base64"
action={withCurrentFile(async (file) => {
// Base64 of "Hello Base64!"
- file.write('SGVsbG8gQmFzZTY0IQ==', { encoding: 'base64' });
+ await file.write('SGVsbG8gQmFzZTY0IQ==', { encoding: 'base64' });
return 'OK - text() = ' + truncate(await file.text());
})}
/>
@@ -295,7 +295,7 @@ function ReadWriteSection({ withCurrentFile }: { withCurrentFile: WithCurrentFil
title="write() Uint8Array"
action={withCurrentFile(async (file) => {
const bytes = new Uint8Array([72, 101, 108, 108, 111]); // "Hello"
- file.write(bytes);
+ await file.write(bytes);
return 'OK - text() = ' + file.textSync();
})}
/>
@@ -687,7 +687,7 @@ function DirectoryOperationsSection({
title="Create file 'test_created.txt' in picked dir"
action={async () => {
const file = safDirectory.createFile('test_created.txt', 'text/plain');
- file.write('Created at ' + new Date().toISOString());
+ await file.write('Created at ' + new Date().toISOString());
setCurrentFile(file);
return { uri: file.uri, name: file.name };
}}
@@ -788,7 +788,7 @@ function FileLifecycleSection({
const name = `test_${Date.now()}.txt`;
const file = new File(Paths.cache, 'test_sandbox', name);
file.create({ intermediates: true });
- file.write('Created for lifecycle test');
+ await file.write('Created for lifecycle test');
setCurrentFile(file);
return { uri: file.uri, exists: file.exists, size: file.size };
}}
diff --git a/apps/test-suite/tests/ContactsNext.ts b/apps/test-suite/tests/ContactsNext.ts
index 4a90c4a24a5352..1ba7e7b02cad71 100644
--- a/apps/test-suite/tests/ContactsNext.ts
+++ b/apps/test-suite/tests/ContactsNext.ts
@@ -120,7 +120,7 @@ export async function test(t) {
const url = 'https://picsum.photos/200';
const response = await fetch(url);
const src = new File(Paths.cache, 'file.pdf');
- src.write(await response.bytes());
+ await src.write(await response.bytes());
const contactDetails = {
givenName: 'Image',
familyName: 'User',
@@ -960,7 +960,7 @@ export async function test(t) {
const url = 'https://picsum.photos/200';
const response = await fetch(url);
const src = new File(Paths.cache, 'file.pdf');
- src.write(await response.bytes());
+ await src.write(await response.bytes());
await contact.setImage(src.uri);
const retrievedImage = await contact.getImage();
const retrievedThumbnail = await contact.getThumbnail();
diff --git a/apps/test-suite/tests/FileSystem.ts b/apps/test-suite/tests/FileSystem.ts
index 9c3eb73dc2c166..6b7ced025bafe8 100644
--- a/apps/test-suite/tests/FileSystem.ts
+++ b/apps/test-suite/tests/FileSystem.ts
@@ -79,13 +79,13 @@ export async function test({ describe, expect, it, ...t }) {
expect(safDirectory.list().length).toBe(0);
const file = safDirectory.createFile('newFile', 'text/plain');
- file.write('test');
+ file.writeSync('test');
expect(file.textSync()).toBe('test');
expect(file.bytesSync()).toEqual(new Uint8Array([116, 101, 115, 116]));
expect(file.base64Sync()).toBe('dGVzdA==');
const file2 = safDirectory.createFile('newFile2', 'text/plain');
- file2.write(new Uint8Array([116, 101, 115, 116]));
+ file2.writeSync(new Uint8Array([116, 101, 115, 116]));
expect(file2.textSync()).toBe('test');
expect(file2.size).toBe(4);
expect(safDirectory.size).toBe(8);
@@ -111,7 +111,7 @@ export async function test({ describe, expect, it, ...t }) {
if (Platform.OS === 'ios') {
it('allows picking files', async () => {
const file = new File(testDirectory, 'selectMe.txt');
- file.write('test');
+ file.writeSync('test');
const result = await File.pickFileAsync(testDirectory);
const safFile = Array.isArray(result) ? result[0] : result;
@@ -124,7 +124,7 @@ export async function test({ describe, expect, it, ...t }) {
expect(directory.exists).toBe(true);
const file = new File(directory, 'test.txt');
- file.write('test');
+ file.writeSync('test');
expect(file.exists).toBe(true);
const selectedDirectory = await Directory.pickDirectoryAsync(testDirectory);
@@ -138,7 +138,7 @@ export async function test({ describe, expect, it, ...t }) {
// Create a file in the selected directory
const file2 = new File(directory.uri, 'newFile.txt');
- file2.write('test');
+ file2.writeSync('test');
expect(file2.exists).toBe(true);
expect(file2.textSync()).toBe('test');
@@ -249,13 +249,13 @@ export async function test({ describe, expect, it, ...t }) {
it('emits a modified event when a watched file is written', async () => {
const file = new File(watcherDirectory, 'modified.txt');
file.create();
- file.write('before');
+ file.writeSync('before');
const events: { type: string }[] = [];
const subscription = file.watch((event) => events.push(event));
try {
- file.write('after');
+ file.writeSync('after');
await delay(300);
expect(events.some((event) => event.type === 'modified')).toBe(true);
@@ -284,7 +284,7 @@ export async function test({ describe, expect, it, ...t }) {
it('filters events by requested types', async () => {
const file = new File(watcherDirectory, 'filter.txt');
file.create();
- file.write('before');
+ file.writeSync('before');
const events: { type: string }[] = [];
const subscription = file.watch((event) => events.push(event), {
@@ -292,7 +292,7 @@ export async function test({ describe, expect, it, ...t }) {
});
try {
- file.write('after');
+ file.writeSync('after');
await delay(300);
expect(events.length).toBe(0);
@@ -421,7 +421,7 @@ export async function test({ describe, expect, it, ...t }) {
it('Works with spaces as filename', () => {
const outputFile = new File(testDirectory, 'my new file.txt');
expect(outputFile.exists).toBe(false);
- outputFile.write('Hello world');
+ outputFile.writeSync('Hello world');
expect(outputFile.exists).toBe(true);
expect(outputFile.name).toBe('my new file.txt');
});
@@ -487,37 +487,44 @@ export async function test({ describe, expect, it, ...t }) {
it('Writes a string to a file reference', () => {
const outputFile = new File(testDirectory, 'file.txt');
expect(outputFile.exists).toBe(false);
- outputFile.write('Hello world');
+ outputFile.writeSync('Hello world');
expect(outputFile.exists).toBe(true);
});
it('overwrites files by default when using write()', () => {
const file = new File(testDirectory, 'overwrite-test.txt');
- file.write('First');
+ file.writeSync('First');
expect(file.textSync()).toBe('First');
- file.write('Second');
+ file.writeSync('Second');
expect(file.textSync()).toBe('Second');
});
it('overwrites a longer file with a shorter string', () => {
const file = new File(testDirectory, 'overwrite-shorter.txt');
- file.write('This is a long string');
+ file.writeSync('This is a long string');
expect(file.textSync()).toBe('This is a long string');
- file.write('Short');
+ file.writeSync('Short');
expect(file.textSync()).toBe('Short');
});
- it('Writes a base64 encoded string to a file reference', () => {
+ it('Writes a base64 encoded string to a file reference using File.writeSync', () => {
const outputFile = new File(testDirectory, 'file.txt');
expect(outputFile.exists).toBe(false);
- outputFile.write('SGVsbG8gd29ybGQh', { encoding: 'base64' });
+ outputFile.writeSync('SGVsbG8gd29ybGQh', { encoding: 'base64' });
expect(outputFile.textSync()).toEqual('Hello world!');
});
- it('Writes a string to a file reference', async () => {
+ it('Writes a base64 encoded string to a file reference using File.write', async () => {
+ const outputFile = new File(testDirectory, 'file_async.txt');
+ expect(outputFile.exists).toBe(false);
+ await outputFile.write('SGVsbG8gd29ybGQh', { encoding: 'base64' });
+ expect(outputFile.textSync()).toEqual('Hello world!');
+ });
+
+ it('Writes a string to a file reference using File.writeSync', async () => {
const outputFile = new File(testDirectory, 'file.txt');
outputFile.create();
- outputFile.write(new Uint8Array([97, 98, 99]));
+ outputFile.writeSync(new Uint8Array([97, 98, 99]));
expect(outputFile.exists).toBe(true);
expect(await outputFile.bytes()).toEqual(new Uint8Array([97, 98, 99]));
expect(outputFile.bytesSync()).toEqual(new Uint8Array([97, 98, 99]));
@@ -525,9 +532,17 @@ export async function test({ describe, expect, it, ...t }) {
expect(outputFile.textSync()).toBe('abc');
});
+ it('Writes a string to a file reference using File.write', async () => {
+ const outputFile = new File(testDirectory, 'file_async.txt');
+ outputFile.create();
+ await outputFile.write('0abcd');
+ expect(outputFile.exists).toBe(true);
+ expect(await outputFile.text()).toBe('0abcd');
+ });
+
it('Reads a string from a file reference', async () => {
const outputFile = new File(testDirectory, 'file2.txt');
- outputFile.write('Hello world');
+ outputFile.writeSync('Hello world');
expect(outputFile.exists).toBe(true);
const content = await outputFile.text();
expect(content).toBe('Hello world');
@@ -564,30 +579,44 @@ export async function test({ describe, expect, it, ...t }) {
expect(content).toBe('Hello world');
});
- it('appends a string using File.write', async () => {
+ it('appends a string using File.writeSync', async () => {
const file = new File(testDirectory, 'next-append.txt');
- file.write('Hello');
- file.write(' world', { append: true });
+ file.writeSync('Hello');
+ file.writeSync(' world', { append: true });
expect(file.textSync()).toBe('Hello world');
});
- it('appends bytes using File.write', async () => {
+ it('appends a string using File.write', async () => {
+ const file = new File(testDirectory, 'next-append_async.txt');
+ await file.write('Hello');
+ await file.write(' world', { append: true });
+ expect(file.textSync()).toBe('Hello world');
+ });
+
+ it('appends bytes using File.writeSync', async () => {
const file = new File(testDirectory, 'next-append-bytes.txt');
- file.write('Hello');
- file.write(new Uint8Array([32, 119, 111, 114, 108, 100]), { append: true }); // ' world'
+ file.writeSync('Hello');
+ file.writeSync(new Uint8Array([32, 119, 111, 114, 108, 100]), { append: true }); // ' world'
+ expect(file.textSync()).toBe('Hello world');
+ });
+
+ it('appends bytes using File.write', async () => {
+ const file = new File(testDirectory, 'next-append-bytes_async.txt');
+ await file.write('Hello');
+ await file.write(new Uint8Array([32, 119, 111, 114, 108, 100]), { append: true }); // ' world'
expect(file.textSync()).toBe('Hello world');
});
it('creates a new file if append is true but file does not exist', async () => {
const file = new File(testDirectory, 'new-file-append.txt');
- file.write('Hello', { append: true });
+ file.writeSync('Hello', { append: true });
expect(file.textSync()).toBe('Hello');
});
});
it('Deletes a file reference', () => {
const outputFile = new File(testDirectory, 'file3.txt');
- outputFile.write('Hello world');
+ outputFile.writeSync('Hello world');
expect(outputFile.exists).toBe(true);
outputFile.delete();
@@ -601,7 +630,7 @@ export async function test({ describe, expect, it, ...t }) {
const childDir = new Directory(parentDir, 'child');
childDir.create();
const file = new File(childDir, 'file.txt');
- file.write('Hello world');
+ file.writeSync('Hello world');
expect(parentDir.exists).toBe(true);
parentDir.delete();
expect(parentDir.exists).toBe(false);
@@ -694,7 +723,7 @@ export async function test({ describe, expect, it, ...t }) {
const file = new File(testDirectory, 'newFolder');
file.create();
expect(file.exists).toBe(true);
- file.write('Hello world');
+ file.writeSync('Hello world');
expect(file.textSync()).toBe('Hello world');
file.create({ overwrite: true });
expect(file.textSync()).toBe('');
@@ -732,7 +761,7 @@ export async function test({ describe, expect, it, ...t }) {
it('Copies it to a folder', () => {
const src = new File(testDirectory, 'file.txt');
- src.write('Hello world');
+ src.writeSync('Hello world');
const dstFolder = new Directory(testDirectory, 'destination');
dstFolder.create();
src.copySync(dstFolder);
@@ -745,14 +774,14 @@ export async function test({ describe, expect, it, ...t }) {
it('Throws an error when copying to a nonexistant folder without options', () => {
const file = new File(testDirectory, 'file.txt');
- file.write('Hello world');
+ file.writeSync('Hello world');
const folder = new Directory(testDirectory, 'destination');
expect(() => file.copySync(folder)).toThrow();
});
it('Copies it to a file', () => {
const src = new File(testDirectory, 'file.txt');
- src.write('Hello world');
+ src.writeSync('Hello world');
const dst = new File(testDirectory, 'file2.txt');
src.copySync(dst);
expect(dst.exists).toBe(true);
@@ -770,7 +799,7 @@ export async function test({ describe, expect, it, ...t }) {
try {
dst.delete();
} catch {}
- src.write('Hello world');
+ src.writeSync('Hello world');
src.copySync(dst);
expect(dst.uri).toBe(FS.documentDirectory + 'file.txt');
expect(dst.exists).toBe(true);
@@ -779,17 +808,17 @@ export async function test({ describe, expect, it, ...t }) {
it('throws when destination file exists and overwrite is not set', () => {
const src = new File(testDirectory, 'src.txt');
- src.write('source');
+ src.writeSync('source');
const dst = new File(testDirectory, 'dst.txt');
- dst.write('destination');
+ dst.writeSync('destination');
expect(() => src.copySync(dst)).toThrow();
});
it('overwrites destination file when overwrite is true', () => {
const src = new File(testDirectory, 'src.txt');
- src.write('source');
+ src.writeSync('source');
const dst = new File(testDirectory, 'dst.txt');
- dst.write('destination');
+ dst.writeSync('destination');
src.copySync(dst, { overwrite: true });
expect(dst.textSync()).toBe('source');
expect(src.exists).toBe(true);
@@ -797,11 +826,11 @@ export async function test({ describe, expect, it, ...t }) {
it('overwrites file in destination directory when overwrite is true', () => {
const src = new File(testDirectory, 'file.txt');
- src.write('new content');
+ src.writeSync('new content');
const dstFolder = new Directory(testDirectory, 'destination');
dstFolder.create();
const existing = new File(dstFolder, 'file.txt');
- existing.write('old content');
+ existing.writeSync('old content');
src.copySync(dstFolder, { overwrite: true });
expect(new File(dstFolder, 'file.txt').textSync()).toBe('new content');
});
@@ -846,11 +875,11 @@ export async function test({ describe, expect, it, ...t }) {
it('overwrites destination directory when overwrite is true', () => {
const src = new Directory(testDirectory, 'srcDir');
src.create();
- new File(src, 'file.txt').write('from source');
+ new File(src, 'file.txt').writeSync('from source');
const dst = new Directory(testDirectory, 'dstDir');
dst.create();
- new File(dst, 'old.txt').write('old content');
+ new File(dst, 'old.txt').writeSync('old content');
src.copySync(dst, { overwrite: true });
expect(dst.exists).toBe(true);
@@ -867,7 +896,7 @@ export async function test({ describe, expect, it, ...t }) {
it('moves it to a folder', () => {
const src = new File(testDirectory, 'file.txt');
- src.write('Hello world');
+ src.writeSync('Hello world');
const dstFolder = new Directory(testDirectory, 'destination');
dstFolder.create();
src.moveSync(dstFolder);
@@ -880,14 +909,14 @@ export async function test({ describe, expect, it, ...t }) {
it('Throws an error when moving to a nonexistant folder without options', () => {
const file = new File(testDirectory, 'file.txt');
- file.write('Hello world');
+ file.writeSync('Hello world');
const folder = new Directory(testDirectory, 'destination');
expect(() => file.moveSync(folder)).toThrow();
});
it('moves it to a file', () => {
const src = new File(testDirectory, 'file.txt');
- src.write('Hello world');
+ src.writeSync('Hello world');
const dst = new File(testDirectory, 'file2.txt');
src.moveSync(dst);
expect(dst.exists).toBe(true);
@@ -898,17 +927,17 @@ export async function test({ describe, expect, it, ...t }) {
it('throws when destination file exists and overwrite is not set', () => {
const src = new File(testDirectory, 'src.txt');
- src.write('source');
+ src.writeSync('source');
const dst = new File(testDirectory, 'dst.txt');
- dst.write('destination');
+ dst.writeSync('destination');
expect(() => src.moveSync(dst)).toThrow();
});
it('overwrites destination file when overwrite is true', () => {
const src = new File(testDirectory, 'src.txt');
- src.write('source');
+ src.writeSync('source');
const dst = new File(testDirectory, 'dst.txt');
- dst.write('destination');
+ dst.writeSync('destination');
src.moveSync(dst, { overwrite: true });
expect(dst.textSync()).toBe('source');
expect(src.uri).toBe(dst.uri);
@@ -916,10 +945,10 @@ export async function test({ describe, expect, it, ...t }) {
it('overwrites file in destination directory when overwrite is true', () => {
const src = new File(testDirectory, 'file.txt');
- src.write('new content');
+ src.writeSync('new content');
const dstFolder = new Directory(testDirectory, 'destination');
dstFolder.create();
- new File(dstFolder, 'file.txt').write('old content');
+ new File(dstFolder, 'file.txt').writeSync('old content');
src.moveSync(dstFolder, { overwrite: true });
expect(new File(dstFolder, 'file.txt').textSync()).toBe('new content');
expect(src.uri).toBe(new File(dstFolder, 'file.txt').uri);
@@ -929,7 +958,7 @@ export async function test({ describe, expect, it, ...t }) {
describe('When renaming a file', () => {
it('renames a file and updates its uri and existence', () => {
const originalFile = new File(testDirectory, 'original.txt');
- originalFile.write('Hello world');
+ originalFile.writeSync('Hello world');
originalFile.rename('renamed.txt');
expect(originalFile.exists).toBe(true);
expect(originalFile.uri).toBe(testDirectory + 'renamed.txt');
@@ -937,7 +966,7 @@ export async function test({ describe, expect, it, ...t }) {
it('renames a file and verifies it appears in the parent directory listing', () => {
const fileToRename = new File(testDirectory, 'toRename.txt');
- fileToRename.write('Hello world');
+ fileToRename.writeSync('Hello world');
fileToRename.rename('renamedFile.txt');
const parentDir = new Directory(testDirectory);
@@ -948,7 +977,7 @@ export async function test({ describe, expect, it, ...t }) {
it('ensures the old file name no longer exists after renaming', () => {
const file = new File(testDirectory, 'oldName.txt');
- file.write('Hello world');
+ file.writeSync('Hello world');
file.rename('newName.txt');
expect(new File(testDirectory, 'oldName.txt').exists).toBe(false);
expect(new File(testDirectory, 'newName.txt').exists).toBe(true);
@@ -956,7 +985,7 @@ export async function test({ describe, expect, it, ...t }) {
it('retains file contents after renaming', () => {
const file = new File(testDirectory, 'contentFile.txt');
- file.write('Sample content');
+ file.writeSync('Sample content');
file.rename('contentFileRenamed.txt');
const renamedFile = new File(testDirectory, 'contentFileRenamed.txt');
expect(renamedFile.textSync()).toBe('Sample content');
@@ -965,8 +994,8 @@ export async function test({ describe, expect, it, ...t }) {
it('throws an error when renaming to an existing file name', () => {
const file1 = new File(testDirectory, 'fileA.txt');
const file2 = new File(testDirectory, 'fileB.txt');
- file1.write('A');
- file2.write('B');
+ file1.writeSync('A');
+ file2.writeSync('B');
expect(() => file1.rename('fileB.txt')).toThrow();
});
@@ -977,7 +1006,7 @@ export async function test({ describe, expect, it, ...t }) {
it('renames a file and preserves file metadata', () => {
const file = new File(testDirectory, 'metadata.txt');
- file.write('Content');
+ file.writeSync('Content');
const originalSize = file.size;
const originalMd5 = file.md5;
file.rename('metadataRenamed.txt');
@@ -987,15 +1016,15 @@ export async function test({ describe, expect, it, ...t }) {
it('throws an error when renaming to an empty string', () => {
const file = new File(testDirectory, 'file.txt');
- file.write('Content');
+ file.writeSync('Content');
expect(() => file.rename('')).toThrow();
});
it('renames a file and updates parent directory listing correctly', () => {
const file1 = new File(testDirectory, 'file1.txt');
const file2 = new File(testDirectory, 'file2.txt');
- file1.write('Content 1');
- file2.write('Content 2');
+ file1.writeSync('Content 1');
+ file2.writeSync('Content 2');
file1.rename('renamedFile1.txt');
@@ -1021,7 +1050,7 @@ export async function test({ describe, expect, it, ...t }) {
it('Throws an error when moving to a nonexistant folder without options', () => {
const file = new File(testDirectory, 'file.txt');
- file.write('Hello world');
+ file.writeSync('Hello world');
const folder = new Directory(testDirectory, 'some/nonexistent/directory/');
expect(() => file.moveSync(folder)).toThrow();
});
@@ -1046,13 +1075,13 @@ export async function test({ describe, expect, it, ...t }) {
it('overwrites destination directory when overwrite is true', () => {
const src = new Directory(testDirectory, 'srcDir');
src.create();
- new File(src, 'file.txt').write('from source');
+ new File(src, 'file.txt').writeSync('from source');
const dstFolder = new Directory(testDirectory, 'destination');
dstFolder.create();
const dst = new Directory(dstFolder, 'srcDir');
dst.create();
- new File(dst, 'old.txt').write('old content');
+ new File(dst, 'old.txt').writeSync('old content');
src.moveSync(dstFolder, { overwrite: true });
expect(src.exists).toBe(true);
@@ -1109,7 +1138,7 @@ export async function test({ describe, expect, it, ...t }) {
describe('Copy operations - SAF file', () => {
it('copies SAF file -> SAF directory (creates file inside)', () => {
const srcFile = safDirectory.createFile('source.txt', 'text/plain');
- srcFile.write('test content');
+ srcFile.writeSync('test content');
const dstDir = safDirectory.createDirectory('targetDir');
srcFile.copySync(dstDir);
@@ -1123,7 +1152,7 @@ export async function test({ describe, expect, it, ...t }) {
it('copies SAF file -> local file', () => {
const srcFile = safDirectory.createFile('source.txt', 'text/plain');
- srcFile.write('test content');
+ srcFile.writeSync('test content');
const dstFile = new File(localDirectory, 'dest.txt');
srcFile.copySync(dstFile);
@@ -1135,7 +1164,7 @@ export async function test({ describe, expect, it, ...t }) {
it('copies SAF file -> local directory (creates file inside)', () => {
const srcFile = safDirectory.createFile('source.txt', 'text/plain');
- srcFile.write('test content');
+ srcFile.writeSync('test content');
srcFile.copySync(localDirectory);
@@ -1149,7 +1178,7 @@ export async function test({ describe, expect, it, ...t }) {
it('copies SAF directory -> SAF directory (existing)', () => {
const srcDir = safDirectory.createDirectory('sourceDir');
const srcFile = srcDir.createFile('nested.txt', 'text/plain');
- srcFile.write('nested content');
+ srcFile.writeSync('nested content');
const dstDir = safDirectory.createDirectory('destDir');
srcDir.copySync(dstDir);
@@ -1166,7 +1195,7 @@ export async function test({ describe, expect, it, ...t }) {
it('copies SAF directory -> local directory (existing)', () => {
const srcDir = safDirectory.createDirectory('sourceDir2');
const srcFile = srcDir.createFile('nested.txt', 'text/plain');
- srcFile.write('nested content');
+ srcFile.writeSync('nested content');
// Destination directory must exist for directory copy
srcDir.copySync(localDirectory);
@@ -1183,7 +1212,7 @@ export async function test({ describe, expect, it, ...t }) {
describe('Copy operations - local to SAF', () => {
it('copies local file -> SAF directory (creates file inside)', () => {
const srcFile = new File(localDirectory, 'localfile.txt');
- srcFile.write('test content');
+ srcFile.writeSync('test content');
const dstDir = safDirectory.createDirectory('localToSafDir');
srcFile.copySync(dstDir);
@@ -1198,7 +1227,7 @@ export async function test({ describe, expect, it, ...t }) {
const srcDir = new Directory(localDirectory, 'localSourceDir');
srcDir.create();
const srcFile = new File(srcDir, 'nested.txt');
- srcFile.write('nested content');
+ srcFile.writeSync('nested content');
srcDir.copySync(safDirectory);
@@ -1251,7 +1280,7 @@ export async function test({ describe, expect, it, ...t }) {
describe('Move operations - SAF file', () => {
it('moves SAF file -> SAF directory (creates file inside)', () => {
const srcFile = safDirectory.createFile('moveSource.txt', 'text/plain');
- srcFile.write('test content');
+ srcFile.writeSync('test content');
const originalUri = srcFile.uri;
const dstDir = safDirectory.createDirectory('moveTargetDir');
@@ -1269,7 +1298,7 @@ export async function test({ describe, expect, it, ...t }) {
it('moves SAF file -> local file', () => {
const srcFile = safDirectory.createFile('source.txt', 'text/plain');
- srcFile.write('test content');
+ srcFile.writeSync('test content');
const originalUri = srcFile.uri;
const dstFile = new File(localDirectory, 'dest.txt');
@@ -1283,7 +1312,7 @@ export async function test({ describe, expect, it, ...t }) {
it('moves SAF file -> local directory (creates file inside)', () => {
const srcFile = safDirectory.createFile('source.txt', 'text/plain');
- srcFile.write('test content');
+ srcFile.writeSync('test content');
const originalUri = srcFile.uri;
srcFile.moveSync(localDirectory);
@@ -1300,7 +1329,7 @@ export async function test({ describe, expect, it, ...t }) {
it('moves SAF directory -> SAF directory (existing)', () => {
const srcDir = safDirectory.createDirectory('moveSrcDir');
const srcFile = srcDir.createFile('nested.txt', 'text/plain');
- srcFile.write('nested content');
+ srcFile.writeSync('nested content');
const originalUri = srcDir.uri;
const dstDir = safDirectory.createDirectory('moveDestDir');
@@ -1316,7 +1345,7 @@ export async function test({ describe, expect, it, ...t }) {
it('moves SAF directory -> local directory (existing)', () => {
const srcDir = safDirectory.createDirectory('moveSrcDir2');
const srcFile = srcDir.createFile('nested.txt', 'text/plain');
- srcFile.write('nested content');
+ srcFile.writeSync('nested content');
const originalUri = srcDir.uri;
const localDest = new Directory(localDirectory, 'safMoveTarget');
@@ -1335,7 +1364,7 @@ export async function test({ describe, expect, it, ...t }) {
describe('Move operations - local to SAF', () => {
it('moves local file -> SAF directory (creates file inside)', () => {
const srcFile = new File(localDirectory, 'localMoveFile.txt');
- srcFile.write('test content');
+ srcFile.writeSync('test content');
const originalUri = srcFile.uri;
const dstDir = safDirectory.createDirectory('localMoveTarget');
@@ -1357,7 +1386,7 @@ export async function test({ describe, expect, it, ...t }) {
const srcDir = new Directory(localDirectory, 'localMoveDir');
srcDir.create();
const srcFile = new File(srcDir, 'nested.txt');
- srcFile.write('nested content');
+ srcFile.writeSync('nested content');
const originalUri = srcDir.uri;
srcDir.moveSync(safDirectory);
@@ -1397,7 +1426,7 @@ export async function test({ describe, expect, it, ...t }) {
it('throws when destination directory does not exist (file copy)', () => {
const srcFile = safDirectory.createFile('source.txt', 'text/plain');
- srcFile.write('test');
+ srcFile.writeSync('test');
const nonExistentDir = new Directory(localDirectory, 'nonexistent');
expect(() => srcFile.copySync(nonExistentDir)).toThrow();
@@ -1405,7 +1434,7 @@ export async function test({ describe, expect, it, ...t }) {
it('throws when destination directory does not exist (file move)', () => {
const srcFile = safDirectory.createFile('source.txt', 'text/plain');
- srcFile.write('test');
+ srcFile.writeSync('test');
const nonExistentDir = new Directory(localDirectory, 'nonexistent');
expect(() => srcFile.moveSync(nonExistentDir)).toThrow();
@@ -1432,7 +1461,7 @@ export async function test({ describe, expect, it, ...t }) {
const dir = new Directory(testDirectory, 'contentDir/');
dir.create();
const file = new File(dir, 'file.txt');
- file.write('test');
+ file.writeSync('test');
dir.rename('renamedContentDir');
const renamedDir = new Directory(testDirectory, 'renamedContentDir/');
expect(renamedDir.exists).toBe(true);
@@ -1470,7 +1499,7 @@ export async function test({ describe, expect, it, ...t }) {
const dir = new Directory(testDirectory, 'metadataDir/');
dir.create();
const file = new File(dir, 'test.txt');
- file.write('Test content');
+ file.writeSync('Test content');
const originalSize = dir.size;
@@ -1567,7 +1596,7 @@ export async function test({ describe, expect, it, ...t }) {
const md5 = '2942bfabb3d05332b66eb128e0842cff';
const response = await fetch(url);
const src = new File(testDirectory, 'file.pdf');
- src.write(await response.bytes());
+ src.writeSync(await response.bytes());
expect(src.md5).toEqual(md5);
});
@@ -1740,7 +1769,7 @@ export async function test({ describe, expect, it, ...t }) {
const url = 'https://httpbingo.org/bytes/10240';
const file = new File(testDirectory, 'idempotent_progress.bin');
file.create();
- file.write('existing content');
+ file.writeSync('existing content');
const progressUpdates: { bytesWritten: number; totalBytes: number }[] = [];
@@ -1761,7 +1790,7 @@ export async function test({ describe, expect, it, ...t }) {
describe('Computes file properties', () => {
it('computes size', async () => {
const file = new File(testDirectory, 'file.txt');
- file.write('Hello world');
+ file.writeSync('Hello world');
expect(file.size).toBe(11);
});
@@ -1770,7 +1799,7 @@ export async function test({ describe, expect, it, ...t }) {
testDirectory,
'creationTime_is_earlier_than_modificationTime_or_equal.txt'
);
- file.write('Hello world');
+ file.writeSync('Hello world');
expect(file.creationTime).not.toBeNull();
expect(file.modificationTime).not.toBeNull();
expect(file.creationTime).toBeLessThanOrEqual(file.modificationTime);
@@ -1778,7 +1807,7 @@ export async function test({ describe, expect, it, ...t }) {
it('computes md5', async () => {
const file = new File(testDirectory, 'file.txt');
- file.write('Hello world');
+ file.writeSync('Hello world');
expect(file.md5).toBe('3e25960a79dbc69b674cd4ec67a72c62');
});
@@ -1794,7 +1823,7 @@ export async function test({ describe, expect, it, ...t }) {
const dir = new Directory(testDirectory, 'directory');
const file = new File(testDirectory, 'directory', 'file.txt');
file.create({ intermediates: true });
- file.write('Hello world');
+ file.writeSync('Hello world');
expect(dir.size).toBe(11);
});
});
@@ -1802,7 +1831,7 @@ export async function test({ describe, expect, it, ...t }) {
describe('Returns base64', () => {
it('gets base64 of a file', async () => {
const src = new File(testDirectory, 'file.txt');
- src.write('Hello world');
+ src.writeSync('Hello world');
expect(await src.base64()).toBe('SGVsbG8gd29ybGQ=');
expect(src.base64Sync()).toBe('SGVsbG8gd29ybGQ=');
});
@@ -1811,7 +1840,7 @@ export async function test({ describe, expect, it, ...t }) {
describe('Returns bytes', () => {
it('gets file as a Uint8Array', async () => {
const src = new File(testDirectory, 'file.txt');
- src.write('Hello world');
+ src.writeSync('Hello world');
expect(src.bytesSync()).toEqual(
new Uint8Array([72, 101, 108, 108, 111, 32, 119, 111, 114, 108, 100])
);
@@ -1911,7 +1940,7 @@ export async function test({ describe, expect, it, ...t }) {
const url = `${testDirectory}execute_correctly.txt`;
const src = new File(url);
src.create();
- src.write('Hello World');
+ src.writeSync('Hello World');
const result = src.info({ md5: true });
expect(result.exists).toBe(true);
if (result.exists) {
@@ -1926,7 +1955,7 @@ export async function test({ describe, expect, it, ...t }) {
it('executes correctly when options are undefined', () => {
const url = `${testDirectory}executes_correctly_when_options_are_undefined.txt`;
const src = new File(url);
- src.write('Hello World');
+ src.writeSync('Hello World');
const result = src.info();
if (result.exists) {
expect(result.md5).toBeNull();
@@ -1935,7 +1964,7 @@ export async function test({ describe, expect, it, ...t }) {
it('returns exists false if file does not exist', () => {
const url = `${testDirectory}returns_exists_false_if_file_does_not_exist.txt`;
const src = new File(url);
- src.write('Hello world');
+ src.writeSync('Hello world');
src.delete();
const result = src.info();
expect(result.exists).toBe(false);
@@ -1960,7 +1989,7 @@ export async function test({ describe, expect, it, ...t }) {
const src = new Directory(url);
src.create();
const file = new File(`${url}1.txt`);
- file.write('Hello world');
+ file.writeSync('Hello world');
const result = src.info();
@@ -1997,7 +2026,7 @@ export async function test({ describe, expect, it, ...t }) {
describe('Exposes file handles', () => {
it('Allows opening files', () => {
const src = new File(testDirectory, 'file.txt');
- src.write('Hello world');
+ src.writeSync('Hello world');
const handle = src.open();
expect(handle.readBytes(4)).toEqual(new Uint8Array([72, 101, 108, 108])); // Hell
expect(handle.readBytes(4)).toEqual(new Uint8Array([111, 32, 119, 111])); // o wo
@@ -2009,7 +2038,7 @@ export async function test({ describe, expect, it, ...t }) {
it('Resets position on close', () => {
const src = new File(testDirectory, 'file.txt');
- src.write('abcde');
+ src.writeSync('abcde');
let handle = src.open();
expect(handle.readBytes(1)).toEqual(new Uint8Array([97])); // a
handle.close();
@@ -2020,7 +2049,7 @@ export async function test({ describe, expect, it, ...t }) {
it('Throws on reading from closed handle', () => {
const src = new File(testDirectory, 'file.txt');
- src.write('abcde');
+ src.writeSync('abcde');
const handle = src.open();
expect(handle.readBytes(1)).toEqual(new Uint8Array([97])); // a
handle.close();
@@ -2029,7 +2058,7 @@ export async function test({ describe, expect, it, ...t }) {
it('Can open multiple handles to the same file', () => {
const src = new File(testDirectory, 'file.txt');
- src.write('abcde');
+ src.writeSync('abcde');
const handle = src.open();
const handle2 = src.open();
expect(handle.readBytes(1)).toEqual(new Uint8Array([97])); // a
@@ -2038,7 +2067,7 @@ export async function test({ describe, expect, it, ...t }) {
it('Returns null offset on closed handle', () => {
const src = new File(testDirectory, 'file.txt');
- src.write('abcde');
+ src.writeSync('abcde');
const handle = src.open();
handle.close();
expect(handle.offset).toBe(null);
@@ -2046,7 +2075,7 @@ export async function test({ describe, expect, it, ...t }) {
it('Returns null size on closed handle', () => {
const src = new File(testDirectory, 'file.txt');
- src.write('abcde');
+ src.writeSync('abcde');
const handle = src.open();
handle.close();
expect(handle.size).toBe(null);
@@ -2058,7 +2087,7 @@ export async function test({ describe, expect, it, ...t }) {
const handle = src.open();
expect(handle.readBytes(2)).toEqual(new Uint8Array([])); // a
handle.close();
- src.write('abcde');
+ src.writeSync('abcde');
const handle2 = src.open();
expect(handle2.readBytes(1)).toEqual(new Uint8Array([97])); // a
handle2.close();
@@ -2067,7 +2096,7 @@ export async function test({ describe, expect, it, ...t }) {
it('Reads a file in chunks', () => {
const src = new File(testDirectory, 'abcs.txt');
const alphabet = 'abcdefghijklmnopqrstuvwxyz';
- src.write(alphabet.repeat(1000) + 'ending');
+ src.writeSync(alphabet.repeat(1000) + 'ending');
const handle = src.open();
for (let i = 0; i < 250; i++) {
const chunk = handle.readBytes(26 * 4);
@@ -2105,7 +2134,7 @@ export async function test({ describe, expect, it, ...t }) {
describe('It supports different FileMode options', () => {
it('opens in ReadOnly mode and reads data', () => {
const src = new File(testDirectory, 'mode-read.txt');
- src.write('Hello');
+ src.writeSync('Hello');
const handle = src.open(FileMode.ReadOnly);
expect(handle.readBytes(5)).toEqual(new Uint8Array([72, 101, 108, 108, 111]));
handle.close();
@@ -2113,7 +2142,7 @@ export async function test({ describe, expect, it, ...t }) {
it('throws when writing to a ReadOnly handle', () => {
const src = new File(testDirectory, 'mode-read-only.txt');
- src.write('Hello');
+ src.writeSync('Hello');
const handle = src.open(FileMode.ReadOnly);
expect(() => handle.writeBytes(new Uint8Array([65]))).toThrow();
handle.close();
@@ -2138,7 +2167,7 @@ export async function test({ describe, expect, it, ...t }) {
it('opens in ReadWrite mode and supports both reading and writing', () => {
const src = new File(testDirectory, 'mode-rw.txt');
- src.write('Hello');
+ src.writeSync('Hello');
const handle = src.open(FileMode.ReadWrite);
expect(handle.readBytes(5)).toEqual(new Uint8Array([72, 101, 108, 108, 111]));
handle.offset = 0;
@@ -2149,7 +2178,7 @@ export async function test({ describe, expect, it, ...t }) {
it('opens in Append mode and appends data', () => {
const src = new File(testDirectory, 'mode-append.txt');
- src.write('Hello');
+ src.writeSync('Hello');
const handle = src.open(FileMode.Append);
handle.writeBytes(new Uint8Array([32, 87, 111, 114, 108, 100])); // ' World'
handle.close();
@@ -2158,7 +2187,7 @@ export async function test({ describe, expect, it, ...t }) {
it('opens in Truncate mode and wipes existing content', () => {
const src = new File(testDirectory, 'mode-truncate.txt');
- src.write('Old content');
+ src.writeSync('Old content');
const handle = src.open(FileMode.Truncate);
expect(handle.size).toBe(0);
handle.writeBytes(new Uint8Array([78, 101, 119])); // New
@@ -2170,7 +2199,7 @@ export async function test({ describe, expect, it, ...t }) {
it('Provides a ReadableStream', async () => {
const src = new File(testDirectory, 'abcs.txt');
const alphabet = 'abcdefghijklmnopqrstuvwxyz';
- src.write(alphabet);
+ src.writeSync(alphabet);
const stream = src.readableStream();
for await (const chunk of stream) {
expect(chunk[0]).toBe(alphabet.charCodeAt(0));
@@ -2180,7 +2209,7 @@ export async function test({ describe, expect, it, ...t }) {
it('Provides a ReadableStream with byob support', async () => {
const src = new File(testDirectory, 'abcs.txt');
const alphabet = 'abcdefghij'.repeat(1000);
- src.write(alphabet);
+ src.writeSync(alphabet);
const stream = src.readableStream();
const array1 = new Uint8Array(5000);
const array2 = new Uint8Array(5000);
@@ -2212,14 +2241,14 @@ export async function test({ describe, expect, it, ...t }) {
const src = new File(asset.localUri);
expect(src.type).toBe('image/jpeg');
const src2 = new File(testDirectory, 'file.txt');
- src2.write('abcde');
+ src2.writeSync('abcde');
expect(src2.type).toBe('text/plain');
});
// You can also use something like container twostoryrobot/simple-file-upload to test if the file is saved correctly
it('Supports sending a file using blob', async () => {
const src = new File(testDirectory, 'file.txt');
- src.write('abcde');
+ src.writeSync('abcde');
const response = await fetch('https://httpbingo.org/anything', {
method: 'POST',
@@ -2232,7 +2261,7 @@ export async function test({ describe, expect, it, ...t }) {
// You can also use this docker image: twostoryrobot/simple-file-upload to test e2e blob upload.
it('Supports sending a file using blob with formdata', async () => {
const src = new File(testDirectory, 'file.txt');
- src.write('abcde');
+ src.writeSync('abcde');
const formData = new FormData();
@@ -2248,7 +2277,7 @@ export async function test({ describe, expect, it, ...t }) {
it('Supports sending a named file blob using blob with formdata', async () => {
const src = new File(testDirectory, 'file.txt');
- src.write('abcde');
+ src.writeSync('abcde');
const formData = new FormData();
@@ -2292,13 +2321,13 @@ function addAppleAppGroupsTestSuiteAsync({ describe, expect, it, ...t }) {
scopedIt('Writes a string to a file reference', () => {
const outputFile = new File(sharedContainerTestDir, 'file.txt');
expect(outputFile.exists).toBe(false);
- outputFile.write('Hello world');
+ outputFile.writeSync('Hello world');
expect(outputFile.exists).toBe(true);
});
scopedIt('Deletes a file reference', () => {
const outputFile = new File(sharedContainerTestDir, 'file3.txt');
- outputFile.write('Hello world');
+ outputFile.writeSync('Hello world');
expect(outputFile.exists).toBe(true);
outputFile.delete();
diff --git a/docs/pages/versions/unversioned/sdk/crypto.mdx b/docs/pages/versions/unversioned/sdk/crypto.mdx
index 9ca6d0f3f6bafa..2ddfbc8e0f3565 100644
--- a/docs/pages/versions/unversioned/sdk/crypto.mdx
+++ b/docs/pages/versions/unversioned/sdk/crypto.mdx
@@ -125,7 +125,7 @@ async function encryptAndSaveData(plaintextData: Uint8Array) {
// Save encrypted file
const file = new File(Paths.cache, 'encrypted.dat');
file.create({ overwrite: true });
- file.write(encryptedBytes);
+ await file.write(encryptedBytes);
}
```
diff --git a/docs/pages/versions/unversioned/sdk/filesystem.mdx b/docs/pages/versions/unversioned/sdk/filesystem.mdx
index 2327fbc68eccfd..eaa5a52bcba55f 100644
--- a/docs/pages/versions/unversioned/sdk/filesystem.mdx
+++ b/docs/pages/versions/unversioned/sdk/filesystem.mdx
@@ -107,7 +107,7 @@ import { File, Paths } from 'expo-file-system';
try {
const file = new File(Paths.cache, 'example.txt');
file.create(); // can throw an error if the file already exists or no permission to create it
- file.write('Hello, world!');
+ await file.write('Hello, world!'); // or `file.writeSync('Hello, world!');` for synchronous call
console.log(file.textSync()); // Hello, world!
} catch (error) {
console.error(error);
@@ -179,7 +179,7 @@ import { File, Paths } from 'expo-file-system';
const url = 'https://pdfobject.com/pdf/sample.pdf';
const response = await fetch(url);
const src = new File(Paths.cache, 'file.pdf');
-src.write(await response.bytes());
+await src.write(await response.bytes());
```
@@ -193,7 +193,7 @@ import { fetch } from 'expo/fetch';
import { File, Paths } from 'expo-file-system';
const file = new File(Paths.cache, 'file.txt');
-file.write('Hello, world!');
+await file.write('Hello, world!');
const response = await fetch('https://example.com', {
method: 'POST',
@@ -208,7 +208,7 @@ import { fetch } from 'expo/fetch';
import { File, Paths } from 'expo-file-system';
const file = new File(Paths.cache, 'file.txt');
-file.write('Hello, world!');
+await file.write('Hello, world!');
const formData = new FormData();
formData.append('data', file);
const response = await fetch('https://example.com', {
diff --git a/packages/expo-file-system/CHANGELOG.md b/packages/expo-file-system/CHANGELOG.md
index 5b7d5c2be32484..7085ca9339b7b5 100644
--- a/packages/expo-file-system/CHANGELOG.md
+++ b/packages/expo-file-system/CHANGELOG.md
@@ -4,6 +4,8 @@
### 🛠 Breaking changes
+- `File.write()` is now asynchronous and returns a Promise. Use `File.writeSync()` for synchronous behavior. ([#45992](https://github.com/expo/expo/pull/45992) by [@wh201906](https://github.com/wh201906))
+
### 🎉 New features
### 🐛 Bug fixes
diff --git a/packages/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt b/packages/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt
index 5ad4088a817c8a..8e73636f4475b8 100644
--- a/packages/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt
+++ b/packages/expo-file-system/android/src/main/java/expo/modules/filesystem/FileSystemModule.kt
@@ -29,6 +29,27 @@ class FileSystemModule : Module() {
private val downloadStore = DownloadTaskStore()
+ private fun writeToFile(
+ file: FileSystemFile,
+ content: Either,
+ options: WriteOptions?
+ ) {
+ val append = options?.append ?: false
+ if (content.`is`(String::class)) {
+ content.get(String::class).let {
+ if (options?.encoding == EncodingType.BASE64) {
+ file.write(Base64.decode(it, Base64.DEFAULT), append)
+ } else {
+ file.write(it, append)
+ }
+ }
+ } else if (content.`is`(TypedArray::class)) {
+ content.get(TypedArray::class).let {
+ file.write(it, append)
+ }
+ }
+ }
+
@RequiresApi(Build.VERSION_CODES.O)
override fun definition() = ModuleDefinition {
Name("FileSystem")
@@ -138,22 +159,12 @@ class FileSystemModule : Module() {
file.create(options ?: CreateOptions())
}
- Function("write") { file: FileSystemFile, content: Either, options: WriteOptions? ->
- val append = options?.append ?: false
- if (content.`is`(String::class)) {
- content.get(String::class).let {
- if (options?.encoding == EncodingType.BASE64) {
- file.write(Base64.decode(it, Base64.DEFAULT), append)
- } else {
- file.write(it, append)
- }
- }
- }
- if (content.`is`(TypedArray::class)) {
- content.get(TypedArray::class).let {
- file.write(it, append)
- }
- }
+ AsyncFunction("write") Coroutine { file: FileSystemFile, content: Either, options: WriteOptions? ->
+ writeToFile(file, content, options)
+ }
+
+ Function("writeSync") { file: FileSystemFile, content: Either, options: WriteOptions? ->
+ writeToFile(file, content, options)
}
AsyncFunction("text") { file: FileSystemFile ->
diff --git a/packages/expo-file-system/build/internal/NativeFileSystem.types.d.ts b/packages/expo-file-system/build/internal/NativeFileSystem.types.d.ts
index d3c5e0a093b677..930b1841721d0c 100644
--- a/packages/expo-file-system/build/internal/NativeFileSystem.types.d.ts
+++ b/packages/expo-file-system/build/internal/NativeFileSystem.types.d.ts
@@ -152,7 +152,12 @@ export declare class NativeFileSystemFile {
* Writes content to the file.
* @param content The content to write into the file.
*/
- write(content: string | Uint8Array, options?: FileWriteOptions): void;
+ write(content: string | Uint8Array, options?: FileWriteOptions): Promise;
+ /**
+ * Writes content to the file.
+ * @param content The content to write into the file.
+ */
+ writeSync(content: string | Uint8Array, options?: FileWriteOptions): void;
/**
* Deletes a file.
*
diff --git a/packages/expo-file-system/build/internal/NativeFileSystem.types.d.ts.map b/packages/expo-file-system/build/internal/NativeFileSystem.types.d.ts.map
index e630122e944ec5..bb23bffd09b80f 100644
--- a/packages/expo-file-system/build/internal/NativeFileSystem.types.d.ts.map
+++ b/packages/expo-file-system/build/internal/NativeFileSystem.types.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"NativeFileSystem.types.d.ts","sourceRoot":"","sources":["../../src/internal/NativeFileSystem.types.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
+{"version":3,"file":"NativeFileSystem.types.d.ts","sourceRoot":"","sources":["../../src/internal/NativeFileSystem.types.ts"],"names":[],"mappings":"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"}
\ No newline at end of file
diff --git a/packages/expo-file-system/build/legacyWarnings.d.ts b/packages/expo-file-system/build/legacyWarnings.d.ts
index 647c8d3c0c23ed..c6d045cebdc205 100644
--- a/packages/expo-file-system/build/legacyWarnings.d.ts
+++ b/packages/expo-file-system/build/legacyWarnings.d.ts
@@ -12,7 +12,7 @@ export declare function readAsStringAsync(fileUri: string, options?: ReadingOpti
*/
export declare function getContentUriAsync(fileUri: string): Promise;
/**
- * @deprecated Use `new File().write()` or import this method from `expo-file-system/legacy`. This method will throw in runtime.
+ * @deprecated Use `await new File().write()` or `new File().writeSync()` or import this method from `expo-file-system/legacy`. This method will throw in runtime.
*/
export declare function writeAsStringAsync(fileUri: string, contents: string, options?: WritingOptions): Promise;
/**
diff --git a/packages/expo-file-system/ios/FileSystemModule.swift b/packages/expo-file-system/ios/FileSystemModule.swift
index 7af943c7823e2d..7d44c0a38493a4 100644
--- a/packages/expo-file-system/ios/FileSystemModule.swift
+++ b/packages/expo-file-system/ios/FileSystemModule.swift
@@ -34,6 +34,26 @@ public final class FileSystemModule: Module {
return attributes[.systemFreeSize] as? Int64
}
+ private func writeToFile(
+ _ file: FileSystemFile,
+ content: Either,
+ options: WriteOptions?
+ ) throws {
+ let append = options?.append ?? false
+ if let content: String = content.get() {
+ if options?.encoding == WriteEncoding.base64 {
+ guard let data = Data(base64Encoded: content, options: .ignoreUnknownCharacters) else {
+ throw UnableToWriteBase64DataException(file.url.absoluteString)
+ }
+ try file.write(data, append: append)
+ } else {
+ try file.write(content, append: append)
+ }
+ } else if let content: TypedArray = content.get() {
+ try file.write(content, append: append)
+ }
+ }
+
public func definition() -> ModuleDefinition {
Name("FileSystem")
@@ -174,21 +194,12 @@ public final class FileSystemModule: Module {
return try file.info(options: options ?? InfoOptions())
}
- Function("write") { (file: FileSystemFile, content: Either, options: WriteOptions?) in
- let append = options?.append ?? false
- if let content: String = content.get() {
- if options?.encoding == WriteEncoding.base64 {
- guard let data = Data(base64Encoded: content, options: .ignoreUnknownCharacters) else {
- throw UnableToWriteBase64DataException(file.url.absoluteString)
- }
- try file.write(data, append: append)
- } else {
- try file.write(content, append: append)
- }
- }
- if let content: TypedArray = content.get() {
- try file.write(content, append: append)
- }
+ AsyncFunction("write") { (file: FileSystemFile, content: Either, options: WriteOptions?) in
+ try writeToFile(file, content: content, options: options)
+ }
+
+ Function("writeSync") { (file: FileSystemFile, content: Either, options: WriteOptions?) in
+ try writeToFile(file, content: content, options: options)
}
Property("size") { file in
diff --git a/packages/expo-file-system/mocks/FileSystem.ts b/packages/expo-file-system/mocks/FileSystem.ts
index 53bb7ad621a12b..30f6cbde9aacec 100644
--- a/packages/expo-file-system/mocks/FileSystem.ts
+++ b/packages/expo-file-system/mocks/FileSystem.ts
@@ -246,7 +246,7 @@ export class FileSystemFile {
});
}
- write(
+ writeSync(
content: string | Uint8Array,
options: { append?: boolean; encoding?: 'utf8' | 'base64' } = {}
): void {
@@ -280,6 +280,13 @@ export class FileSystemFile {
}
}
+ async write(
+ content: string | Uint8Array,
+ options: { append?: boolean; encoding?: 'utf8' | 'base64' } = {}
+ ): Promise {
+ this.writeSync(content, options);
+ }
+
private readBytesOrThrow(): Uint8Array {
const entry = store.get(normalizeKey(this.uri));
if (!entry || entry.kind !== 'file' || !entry.exists) {
diff --git a/packages/expo-file-system/src/__tests__/FileSystem-test.native.ts b/packages/expo-file-system/src/__tests__/FileSystem-test.native.ts
index d51f7239ed6632..7b65243e446e40 100644
--- a/packages/expo-file-system/src/__tests__/FileSystem-test.native.ts
+++ b/packages/expo-file-system/src/__tests__/FileSystem-test.native.ts
@@ -62,6 +62,7 @@ describe('expo-file-system new API', () => {
expect(typeof file.moveSync).toBe('function');
expect(typeof file.text).toBe('function');
expect(typeof file.write).toBe('function');
+ expect(typeof file.writeSync).toBe('function');
expect(typeof file.json).toBe('function');
expect(typeof file.formData).toBe('function');
});
@@ -122,6 +123,15 @@ describe('expo-file-system new API', () => {
await expect(file.move(destination, { overwrite: true })).resolves.toBeUndefined();
});
+ it('File.write returns a promise and File.writeSync stays synchronous', async () => {
+ const file = new File(Paths.cache, 'test.txt');
+
+ expect(file.write('hello')).toBeInstanceOf(Promise);
+ const result = await file.write('hello');
+ expect(result).toBeUndefined();
+ expect(file.writeSync('hello')).toBeUndefined();
+ });
+
it('Directory copy and move return promises', async () => {
const dir = new Directory(Paths.document, 'subdir');
dir.create();
@@ -193,8 +203,8 @@ describe('expo-file-system behavioral mock', () => {
const dir = new Directory(Paths.cache, 'info-dir');
dir.create();
const creationTime = dir.info().creationTime;
- dir.createFile('a.txt', null).write('abc');
- dir.createFile('b.txt', null).write('de');
+ dir.createFile('a.txt', null).writeSync('abc');
+ dir.createFile('b.txt', null).writeSync('de');
expect(dir.info()).toMatchObject({
exists: true,
@@ -204,32 +214,53 @@ describe('expo-file-system behavioral mock', () => {
});
});
- it('File.write(string) and File.text() roundtrip utf-8', async () => {
+ it('File.writeSync(string) and File.text() roundtrip utf-8', async () => {
const file = new File(Paths.cache, 'hello.txt');
- file.write('hello world');
+ file.writeSync('hello world');
+ expect(file.textSync()).toBe('hello world');
+ await expect(file.text()).resolves.toBe('hello world');
+ });
+
+ it('File.write(string) and File.text() roundtrip utf-8', async () => {
+ const file = new File(Paths.cache, 'hello_async.txt');
+ await file.write('hello world');
expect(file.textSync()).toBe('hello world');
await expect(file.text()).resolves.toBe('hello world');
});
- it('File.write(Uint8Array) and File.bytes() roundtrip byte-for-byte', async () => {
+ it('File.writeSync(Uint8Array) and File.bytes() roundtrip byte-for-byte', async () => {
const file = new File(Paths.cache, 'bin.dat');
const payload = new Uint8Array([1, 2, 3, 4, 5]);
- file.write(payload);
+ file.writeSync(payload);
expect(Array.from(file.bytesSync())).toEqual([1, 2, 3, 4, 5]);
await expect(file.bytes()).resolves.toEqual(payload);
});
- it('File.write with append option appends to existing bytes', () => {
+ it('File.writeSync with append option appends to existing bytes', () => {
const file = new File(Paths.cache, 'log.txt');
- file.write('a');
- file.write('b', { append: true });
- file.write('c', { append: true });
+ file.writeSync('a');
+ file.writeSync('b', { append: true });
+ file.writeSync('c', { append: true });
+ expect(file.textSync()).toBe('abc');
+ });
+
+ it('File.write with append option appends to existing bytes', async () => {
+ const file = new File(Paths.cache, 'log_async.txt');
+ await file.write('a');
+ await file.write('b', { append: true });
+ await file.write('c', { append: true });
expect(file.textSync()).toBe('abc');
});
- it('File.write with base64 encoding decodes before storing', () => {
+ it('File.writeSync with base64 encoding decodes before storing', () => {
const file = new File(Paths.cache, 'encoded.txt');
- file.write(Buffer.from('hello').toString('base64'), { encoding: 'base64' });
+ file.writeSync(Buffer.from('hello').toString('base64'), { encoding: 'base64' });
+ expect(file.textSync()).toBe('hello');
+ });
+
+ it('File.write with base64 encoding decodes before storing', async () => {
+ const file = new File(Paths.cache, 'encoded_async.txt');
+ await file.write(Buffer.from('hello').toString('base64'), { encoding: 'base64' });
expect(file.textSync()).toBe('hello');
});
@@ -247,7 +278,7 @@ describe('expo-file-system behavioral mock', () => {
expect(creationTime).toBeGreaterThan(0);
expect(file.modificationTime).toBe(creationTime);
- file.write('hello');
+ file.writeSync('hello');
expect(file.size).toBe(5);
expect(file.creationTime).toBe(creationTime);
expect(file.modificationTime).toBeGreaterThan(creationTime!);
@@ -266,7 +297,7 @@ describe('expo-file-system behavioral mock', () => {
it('File.move updates this.uri and removes the source', async () => {
const source = new File(Paths.cache, 'source.txt');
- source.write('payload');
+ source.writeSync('payload');
const originalUri = source.uri;
const destDir = new Directory(Paths.cache, 'moved');
@@ -285,7 +316,7 @@ describe('expo-file-system behavioral mock', () => {
it('File.copy leaves the source intact and copies contents', async () => {
const source = new File(Paths.cache, 'copy-src.txt');
- source.write('original');
+ source.writeSync('original');
const dest = new File(Paths.cache, 'copy-dest.txt');
await source.copy(dest);
@@ -295,7 +326,7 @@ describe('expo-file-system behavioral mock', () => {
expect(dest.textSync()).toBe('original');
// Writing to the copy must not mutate the source.
- dest.write('mutated');
+ dest.writeSync('mutated');
expect(source.textSync()).toBe('original');
expect(dest.textSync()).toBe('mutated');
});
@@ -303,9 +334,9 @@ describe('expo-file-system behavioral mock', () => {
it('Directory.delete removes the directory and all descendants', () => {
const dir = new Directory(Paths.cache, 'doomed');
dir.create();
- dir.createFile('a.txt', null).write('a');
+ dir.createFile('a.txt', null).writeSync('a');
const inner = dir.createDirectory('inner');
- inner.createFile('b.txt', null).write('b');
+ inner.createFile('b.txt', null).writeSync('b');
dir.delete();
@@ -361,7 +392,7 @@ describe('expo-file-system behavioral mock', () => {
function makeFile(name: string, contents = 'hello'): File {
const file = new File(Paths.cache, name);
file.create();
- file.write(contents);
+ file.writeSync(contents);
return file;
}
@@ -401,7 +432,7 @@ describe('expo-file-system behavioral mock', () => {
const file = dir.createFile('x.txt', 'text/plain');
expect(file.type).toBe('text/plain');
- file.write('hello');
+ file.writeSync('hello');
expect(file.type).toBe('text/plain');
const handle = file.open(FileMode.Truncate);
diff --git a/packages/expo-file-system/src/internal/NativeFileSystem.types.ts b/packages/expo-file-system/src/internal/NativeFileSystem.types.ts
index 5f6aaf6aa744ba..01f8a6ad20aed6 100644
--- a/packages/expo-file-system/src/internal/NativeFileSystem.types.ts
+++ b/packages/expo-file-system/src/internal/NativeFileSystem.types.ts
@@ -205,7 +205,13 @@ export declare class NativeFileSystemFile {
* Writes content to the file.
* @param content The content to write into the file.
*/
- write(content: string | Uint8Array, options?: FileWriteOptions): void;
+ write(content: string | Uint8Array, options?: FileWriteOptions): Promise;
+
+ /**
+ * Writes content to the file.
+ * @param content The content to write into the file.
+ */
+ writeSync(content: string | Uint8Array, options?: FileWriteOptions): void;
/**
* Deletes a file.
diff --git a/packages/expo-file-system/src/legacyWarnings.ts b/packages/expo-file-system/src/legacyWarnings.ts
index 0c2be1d956920e..89d5f0eb0bf795 100644
--- a/packages/expo-file-system/src/legacyWarnings.ts
+++ b/packages/expo-file-system/src/legacyWarnings.ts
@@ -46,7 +46,7 @@ export async function getContentUriAsync(fileUri: string): Promise {
}
/**
- * @deprecated Use `new File().write()` or import this method from `expo-file-system/legacy`. This method will throw in runtime.
+ * @deprecated Use `await new File().write()` or `new File().writeSync()` or import this method from `expo-file-system/legacy`. This method will throw in runtime.
*/
export async function writeAsStringAsync(
fileUri: string,
From 369b698c5c8a6171e1f6b777e16c4e03dfe3eac4 Mon Sep 17 00:00:00 2001
From: SJvaca30 <122259127+SJvaca30@users.noreply.github.com>
Date: Wed, 27 May 2026 17:05:15 +0900
Subject: [PATCH 5/7] [docs][ui] Remove Picker from SDK 55 drop-in replacements
(#46325)
---
docs/pages/versions/v55.0.0/sdk/picker.mdx | 2 -
.../sdk/ui/drop-in-replacements/index.mdx | 1 -
.../sdk/ui/drop-in-replacements/picker.mdx | 142 ------------------
.../v55.0.0/expo-ui/community/picker.json | 1 -
4 files changed, 146 deletions(-)
delete mode 100644 docs/pages/versions/v55.0.0/sdk/ui/drop-in-replacements/picker.mdx
delete mode 100644 docs/public/static/data/v55.0.0/expo-ui/community/picker.json
diff --git a/docs/pages/versions/v55.0.0/sdk/picker.mdx b/docs/pages/versions/v55.0.0/sdk/picker.mdx
index 03ac6f04dfb8a8..2af96fb7c468e6 100644
--- a/docs/pages/versions/v55.0.0/sdk/picker.mdx
+++ b/docs/pages/versions/v55.0.0/sdk/picker.mdx
@@ -12,8 +12,6 @@ import { BookOpen02Icon } from '@expo/styleguide-icons/outline/BookOpen02Icon';
import { APIInstallSection } from '~/components/plugins/InstallSection';
import { BoxLink } from '~/ui/components/BoxLink';
-> **important** [`@expo/ui` provides a drop-in replacement](./ui/drop-in-replacements/picker) for `@react-native-picker/picker`, powered by Jetpack Compose on Android and SwiftUI on iOS.
-
A component that provides access to the system UI for picking between several options.
## Installation
diff --git a/docs/pages/versions/v55.0.0/sdk/ui/drop-in-replacements/index.mdx b/docs/pages/versions/v55.0.0/sdk/ui/drop-in-replacements/index.mdx
index 39c64a125ecc9b..e49ac046b607a8 100644
--- a/docs/pages/versions/v55.0.0/sdk/ui/drop-in-replacements/index.mdx
+++ b/docs/pages/versions/v55.0.0/sdk/ui/drop-in-replacements/index.mdx
@@ -10,5 +10,4 @@ platforms: ['android', 'ios']
The following components provide API-compatible replacements for popular React Native community libraries, powered by `@expo/ui` native components (Jetpack Compose on Android and SwiftUI on iOS).
- **[DateTimePicker](datetimepicker)**: Compatible with `@react-native-community/datetimepicker`
-- **[Picker](picker)**: Compatible with `@react-native-picker/picker`
- **[SegmentedControl](segmentedcontrol)**: Compatible with `@react-native-segmented-control/segmented-control`
diff --git a/docs/pages/versions/v55.0.0/sdk/ui/drop-in-replacements/picker.mdx b/docs/pages/versions/v55.0.0/sdk/ui/drop-in-replacements/picker.mdx
deleted file mode 100644
index c97051763e8778..00000000000000
--- a/docs/pages/versions/v55.0.0/sdk/ui/drop-in-replacements/picker.mdx
+++ /dev/null
@@ -1,142 +0,0 @@
----
-title: Picker
-description: A picker compatible with @react-native-picker/picker.
-sourceCodeUrl: 'https://github.com/expo/expo/tree/main/packages/expo-ui'
-packageName: '@expo/ui'
-platforms: ['android', 'ios', 'web']
----
-
-import APISection from '~/components/plugins/APISection';
-import { APIInstallSection } from '~/components/plugins/InstallSection';
-
-A `Picker` component with an API compatible with `@react-native-picker/picker`. It uses a SwiftUI wheel `Picker` on iOS, a Material 3 `ExposedDropdownMenuBox` on Android, and a native ` |