From 176082e08cb9409f29b38d45fd40e8d080637621 Mon Sep 17 00:00:00 2001
From: Alan Hughes <30924086+alanjhughes@users.noreply.github.com>
Date: Tue, 3 Mar 2026 16:32:08 +0000
Subject: [PATCH 1/7] [ios][audio] Improve looping support (#43600)
---
.../src/screens/Audio/AudioEventsScreen.tsx | 7 ++
packages/expo-audio/CHANGELOG.md | 1 +
packages/expo-audio/ios/AudioModule.swift | 4 +-
packages/expo-audio/ios/AudioPlayer.swift | 106 +++++++++++++++---
packages/expo-audio/ios/AudioUtils.swift | 4 +-
5 files changed, 104 insertions(+), 18 deletions(-)
diff --git a/apps/native-component-list/src/screens/Audio/AudioEventsScreen.tsx b/apps/native-component-list/src/screens/Audio/AudioEventsScreen.tsx
index 7a5b021d39b7d9..84d7390612d95b 100644
--- a/apps/native-component-list/src/screens/Audio/AudioEventsScreen.tsx
+++ b/apps/native-component-list/src/screens/Audio/AudioEventsScreen.tsx
@@ -115,6 +115,7 @@ function AudioEvents() {
highlight={status.didJustFinish}
/>
+
@@ -128,6 +129,12 @@ function AudioEvents() {
+
diff --git a/packages/expo-audio/CHANGELOG.md b/packages/expo-audio/CHANGELOG.md
index 1ba4d9467bd9b0..5aa41fa6a03a8d 100644
--- a/packages/expo-audio/CHANGELOG.md
+++ b/packages/expo-audio/CHANGELOG.md
@@ -9,6 +9,7 @@
### 🐛 Bug fixes
- [iOS] Fix crash during seek. ([#43564](https://github.com/expo/expo/pull/43564) by [@alanjhughes](https://github.com/alanjhughes))
+- [iOS] Improve looping support. ([#43600](https://github.com/expo/expo/pull/43600) by [@alanjhughes](https://github.com/alanjhughes))
### 💡 Others
diff --git a/packages/expo-audio/ios/AudioModule.swift b/packages/expo-audio/ios/AudioModule.swift
index d2046b5185ff05..7a5ab43f5c7ae1 100644
--- a/packages/expo-audio/ios/AudioModule.swift
+++ b/packages/expo-audio/ios/AudioModule.swift
@@ -656,7 +656,7 @@ public class AudioModule: Module {
player.ref.volume = originalVolume
}
case .doNotMix, .mixWithOthers:
- player.ref.play()
+ player.play(at: player.currentRate > 0 ? player.currentRate : 1.0)
}
}
}
@@ -705,7 +705,7 @@ public class AudioModule: Module {
private func resumeAllPlayers() {
registry.allPlayers.values.forEach { player in
if player.wasPlaying {
- player.ref.play()
+ player.play(at: player.currentRate > 0 ? player.currentRate : 1.0)
player.wasPlaying = false
}
}
diff --git a/packages/expo-audio/ios/AudioPlayer.swift b/packages/expo-audio/ios/AudioPlayer.swift
index 6d7cc7b3c76785..4af9b864a95d06 100644
--- a/packages/expo-audio/ios/AudioPlayer.swift
+++ b/packages/expo-audio/ios/AudioPlayer.swift
@@ -8,12 +8,15 @@ private enum AudioConstants {
public class AudioPlayer: SharedRef {
let id = UUID().uuidString
- var isLooping = false
var shouldCorrectPitch = true
var pitchCorrectionQuality: AVAudioTimePitchAlgorithm = .timeDomain
var isActiveForLockScreen = false
var metadata: Metadata?
- var currentRate: Float = 0.0
+ var currentRate: Float = 1.0 {
+ didSet {
+ currentRate = max(0, currentRate)
+ }
+ }
let interval: Double
var wasPlaying = false
var isPaused: Bool {
@@ -22,6 +25,22 @@ public class AudioPlayer: SharedRef {
var samplingEnabled = false
var keepAudioSessionActive = false
+ var isLooping = false {
+ didSet {
+ guard isLooping != oldValue else {
+ return
+ }
+ if isLooping {
+ ref.actionAtItemEnd = .advance
+ enqueueNextLoopItem()
+ } else {
+ removeQueuedLoopItems()
+ ref.actionAtItemEnd = .pause
+ }
+ updateStatus(with: [:])
+ }
+ }
+
private var source: AudioSource?
// MARK: Observers
@@ -65,7 +84,15 @@ public class AudioPlayer: SharedRef {
playerIsBuffering()
}
+ private var effectiveRate: Float {
+ currentRate > 0 ? currentRate : 1.0
+ }
+
func play(at rate: Float) {
+ ref.actionAtItemEnd = isLooping ? .advance : .pause
+ if isLooping {
+ enqueueNextLoopItem()
+ }
addPlaybackEndNotification()
registerTimeObserver()
ref.playImmediately(atRate: rate)
@@ -165,6 +192,9 @@ public class AudioPlayer: SharedRef {
self.updateStatus(with: [
"isLoaded": true
])
+ if self.isLooping {
+ self.enqueueNextLoopItem()
+ }
if shouldInstallAudioTap || samplingEnabled {
installTap()
shouldInstallAudioTap = false
@@ -178,7 +208,12 @@ public class AudioPlayer: SharedRef {
guard let self else {
return
}
+ if self.isLooping {
+ self.enqueueNextLoopItem()
+ self.addPlaybackEndNotification()
+ }
if self.samplingEnabled && self.isLoaded {
+ self.uninstallTap()
self.installTap()
}
}
@@ -188,19 +223,20 @@ public class AudioPlayer: SharedRef {
func replaceWithPreloadedItem(_ item: AVPlayerItem?) {
let wasPlaying = ref.timeControlStatus == .playing
let wasSamplingEnabled = samplingEnabled
+ removeQueuedLoopItems()
ref.pause()
if samplingEnabled {
uninstallTap()
}
- ref.replaceCurrentItem(with: item)
+ replacePlayerItem(with: item)
if wasSamplingEnabled {
shouldInstallAudioTap = true
}
if wasPlaying {
- ref.play()
+ play(at: effectiveRate)
}
}
@@ -208,13 +244,14 @@ public class AudioPlayer: SharedRef {
self.source = source
let wasPlaying = ref.timeControlStatus == .playing
let wasSamplingEnabled = samplingEnabled
+ removeQueuedLoopItems()
ref.pause()
if samplingEnabled {
uninstallTap()
}
let item = AudioUtils.createAVPlayerItem(from: source)
- ref.replaceCurrentItem(with: item)
+ replacePlayerItem(with: item)
if wasSamplingEnabled {
shouldInstallAudioTap = true
@@ -222,7 +259,8 @@ public class AudioPlayer: SharedRef {
if wasPlaying {
onReady { [weak self] in
- self?.ref.play()
+ guard let self else { return }
+ self.play(at: self.effectiveRate)
}
}
}
@@ -261,6 +299,7 @@ public class AudioPlayer: SharedRef {
}
private func teardownPlayer() {
+ removeQueuedLoopItems()
if samplingEnabled {
uninstallTap()
}
@@ -360,6 +399,41 @@ public class AudioPlayer: SharedRef {
audioProcessor?.sampleBufferCallback = nil
}
+ private func replacePlayerItem(with item: AVPlayerItem?) {
+ if let queuePlayer = ref as? AVQueuePlayer {
+ queuePlayer.removeAllItems()
+ if let item {
+ queuePlayer.insert(item, after: nil)
+ }
+ } else {
+ ref.replaceCurrentItem(with: item)
+ }
+ }
+
+ private func enqueueNextLoopItem() {
+ guard let queuePlayer = ref as? AVQueuePlayer,
+ let currentItem = queuePlayer.currentItem else {
+ return
+ }
+
+ guard queuePlayer.items().count <= 1 else {
+ return
+ }
+ let nextItem = AVPlayerItem(asset: currentItem.asset)
+ nextItem.audioTimePitchAlgorithm = currentItem.audioTimePitchAlgorithm
+ queuePlayer.insert(nextItem, after: currentItem)
+ }
+
+ private func removeQueuedLoopItems() {
+ guard let queuePlayer = ref as? AVQueuePlayer else {
+ return
+ }
+
+ for item in queuePlayer.items() where item != queuePlayer.currentItem {
+ queuePlayer.remove(item)
+ }
+ }
+
private func addPlaybackEndNotification() {
if let endObserver {
NotificationCenter.default.removeObserver(endObserver)
@@ -367,20 +441,24 @@ public class AudioPlayer: SharedRef {
endObserver = NotificationCenter.default.addObserver(
forName: .AVPlayerItemDidPlayToEndTime,
- object: ref.currentItem,
+ object: nil,
queue: nil
- ) { [weak self] _ in
- guard let self else {
+ ) { [weak self] notification in
+ guard let self,
+ let finishedItem = notification.object as? AVPlayerItem else {
return
}
- if self.isLooping {
- self.ref.seek(to: CMTime.zero)
- self.ref.play()
- } else {
+ guard let queuePlayer = self.ref as? AVQueuePlayer,
+ finishedItem == queuePlayer.currentItem || queuePlayer.items().contains(finishedItem) else {
+ return
+ }
+
+ if !self.isLooping {
+ let currentTime = finishedItem.duration.seconds
self.updateStatus(with: [
"playing": false,
- "currentTime": self.duration,
+ "currentTime": currentTime.isNaN ? 0.0 : currentTime,
"didJustFinish": true
])
self.onPlaybackComplete?()
diff --git a/packages/expo-audio/ios/AudioUtils.swift b/packages/expo-audio/ios/AudioUtils.swift
index 66916fb77c5f24..7cd7b300a0d24d 100644
--- a/packages/expo-audio/ios/AudioUtils.swift
+++ b/packages/expo-audio/ios/AudioUtils.swift
@@ -87,9 +87,9 @@ struct AudioUtils {
static func createAVPlayer(from source: AudioSource?) -> AVPlayer {
if let item = createAVPlayerItem(from: source) {
- return AVPlayer(playerItem: item)
+ return AVQueuePlayer(items: [item])
}
- return AVPlayer()
+ return AVQueuePlayer()
}
static func createAVPlayerItem(from source: AudioSource?) -> AVPlayerItem? {
From bd312dbc1e8fed1ecb285bd743b1406028f1a861 Mon Sep 17 00:00:00 2001
From: Alan Hughes
Date: Tue, 3 Mar 2026 16:58:35 +0000
Subject: [PATCH 2/7] podfile.lock
---
apps/bare-expo/ios/Podfile.lock | 8 ++++----
apps/expo-go/ios/Podfile.lock | 2 +-
2 files changed, 5 insertions(+), 5 deletions(-)
diff --git a/apps/bare-expo/ios/Podfile.lock b/apps/bare-expo/ios/Podfile.lock
index f5e702eba1eb52..a4f3ec64f6ff27 100644
--- a/apps/bare-expo/ios/Podfile.lock
+++ b/apps/bare-expo/ios/Podfile.lock
@@ -758,7 +758,7 @@ PODS:
- React-utils
- ReactNativeDependencies
- Yoga
- - React-Core-prebuilt (0.84.0):
+ - React-Core-prebuilt (0.84.1):
- ReactNativeDependencies
- React-Core/CoreModulesHeaders (0.84.1):
- hermes-engine
@@ -3900,7 +3900,7 @@ SPEC CHECKSUMS:
EXUpdates: c5a64985f393cf4f8beb4463f86a885c90b4fccc
EXUpdatesInterface: 26412751a0f7a7130614655929e316f684552aab
FBLazyVector: e97c19a5a442429d1988f182a1940fb08df514da
- hermes-engine: f17b9ba9fc7fc0b2418d3f51964ef51edd76cb49
+ hermes-engine: 2322b1974d93af0d15d30d5bafd5886cd7475677
libavif: 84bbb62fb232c3018d6f1bab79beea87e35de7b7
libdav1d: 23581a4d8ec811ff171ed5e2e05cd27bad64c39f
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
@@ -3918,7 +3918,7 @@ SPEC CHECKSUMS:
React: 1ba7d364ade7d883a1ec055bfc3606f35fdee17b
React-callinvoker: bc2a26f8d84fb01f003fc6de6c9337b64715f95b
React-Core: bdaa87b276ca31877632a982ecf7c36f8c826414
- React-Core-prebuilt: 8d3b8ca928d5ec4acbd02a85ad31693483710ca8
+ React-Core-prebuilt: 3a18d3c3d7f34cd70923b0b359b41e78163d9877
React-CoreModules: b24989f62d56390ae08ca4f65e6f38fe6802de42
React-cxxreact: 1a2dfcbc18a6b610664dba152adf327f063a0d12
React-debug: 755200a6e7f5e6e0a40ff8d215493d43cce285fc
@@ -3988,7 +3988,7 @@ SPEC CHECKSUMS:
ReactAppDependencyProvider: 26bbf1e26768d08dd965a2b5e372e53f67b21fee
ReactCodegen: 3737b2746b303ce8636a461f8ee4cb2525078267
ReactCommon: 309419492d417c4cbb87af06f67735afa40ecb9d
- ReactNativeDependencies: 73db4772e50c35a1670768d3694ce715638d3b5f
+ ReactNativeDependencies: e7a1b2c1da219bfbb50fab4af2d5a18d2ddbf770
RNCAsyncStorage: e85a99325df9eb0191a6ee2b2a842644c7eb29f4
RNCMaskedView: 3c9d7586e2b9bbab573591dcb823918bc4668005
RNCPicker: e0149590451d5eae242cf686014a6f6d808f93c7
diff --git a/apps/expo-go/ios/Podfile.lock b/apps/expo-go/ios/Podfile.lock
index 40b438a0831874..ecc873614fced4 100644
--- a/apps/expo-go/ios/Podfile.lock
+++ b/apps/expo-go/ios/Podfile.lock
@@ -4770,7 +4770,7 @@ SPEC CHECKSUMS:
GoogleAppMeasurement: 8a82b93a6400c8e6551c0bcd66a9177f2e067aed
GoogleDataTransport: aae35b7ea0c09004c3797d53c8c41f66f219d6a7
GoogleUtilities: 26a3abef001b6533cf678d3eb38fd3f614b7872d
- hermes-engine: bfb748a8f1f841309151b371e25491acbdf94a8a
+ hermes-engine: 00d01f3145a01aca9d0940d02c403832806eaf82
libavif: 84bbb62fb232c3018d6f1bab79beea87e35de7b7
libdav1d: 23581a4d8ec811ff171ed5e2e05cd27bad64c39f
libwebp: 02b23773aedb6ff1fd38cec7a77b81414c6842a8
From 9bb34d6650fd9b5df0947061c1a9863215b4aed9 Mon Sep 17 00:00:00 2001
From: Jacob Clausen <13820994+entiendoNull@users.noreply.github.com>
Date: Tue, 3 Mar 2026 18:15:10 +0100
Subject: [PATCH 3/7] [docs] Fix Prerequisites anchor link scroll (#43615)
# Why
When a page has multiple `` components (e.g.,
https://docs.expo.dev/submit/ios/ or
https://docs.expo.dev/deploy/submit-to-app-stores), navigating to the
anchor link of any instance other than the first does not scroll to the
correct position.
The anchor link URL itself was correct (e.g., #prerequisites-1), but the
`` element's id was hardcoded to "prerequisites" for every
instance. Since no DOM element had the matching id, the browser couldn't
scroll to it and the hash-based auto-expansion didn't trigger.
# How
Changed the id attribute on the `` element in
`ui/components/Prerequisites/index.tsx` from the hardcoded `anchorId`
("prerequisites") to heading.current.slug, which is the unique slug
already used by the anchor link href.
# Test Plan
Tested on a few pages locally to see that it works as intended.
# 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).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
---
docs/ui/components/Prerequisites/index.tsx | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/docs/ui/components/Prerequisites/index.tsx b/docs/ui/components/Prerequisites/index.tsx
index 0c6febae6e23c5..6ddd8d8118d337 100644
--- a/docs/ui/components/Prerequisites/index.tsx
+++ b/docs/ui/components/Prerequisites/index.tsx
@@ -58,7 +58,7 @@ const Prerequisites: ComponentType = withHeadingManager(
}, [open]);
return (
Date: Tue, 3 Mar 2026 18:17:16 +0000
Subject: [PATCH 4/7] test(autolinking): Add test coverage for more isolated
dependencies edge cases (#43610)
# Why
Adding tests demonstrating that `path` and `version` are stable across
dependency orderings when multiple parents share a pnpm-isolated
dependency. The `_visitedPackagePaths` race in
`scanDependenciesRecursively` determines which parent sets the version,
but `mergeWithDuplicate` correctly transfers it in all cases. The sole
non-deterministic field is `originPath`, which is internal and unused
outside diagnostics.
This is based on tests in an issue to validate that the output is
deterministic for (non-corrupted) isolated dependency installations
Related #43517
# How
- Add a few more tests from issue analysis
# Test Plan
# 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).
- [ ] Conforms with the [Documentation Writing Style
Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md)
---
.../dependencies/__tests__/resolution-test.ts | 258 ++++++++++++++++++
1 file changed, 258 insertions(+)
diff --git a/packages/expo-modules-autolinking/src/dependencies/__tests__/resolution-test.ts b/packages/expo-modules-autolinking/src/dependencies/__tests__/resolution-test.ts
index f4397e34d9acd0..020a13f2ca24cd 100644
--- a/packages/expo-modules-autolinking/src/dependencies/__tests__/resolution-test.ts
+++ b/packages/expo-modules-autolinking/src/dependencies/__tests__/resolution-test.ts
@@ -475,6 +475,264 @@ describe(scanDependenciesRecursively, () => {
`);
});
+ it('resolves isolated duplicates with stable version regardless of dependency ordering', async () => {
+ const runWithOrder = async (first: string, second: string) => {
+ const result = await createMemoizer().withMemoizer(async () => {
+ vol.fromNestedJSON(
+ {
+ ...mockedNodeModule('root', {
+ pkgDependencies: { [first]: '*', [second]: '*' },
+ }),
+ node_modules: {
+ '.pnpm': {
+ 'parent-a@1.0.0/node_modules': {
+ 'parent-a': mockedNodeModule('parent-a', {
+ pkgVersion: '1.0.0',
+ pkgDependencies: { dep: '*' },
+ }),
+ },
+ 'parent-b@1.0.0/node_modules': {
+ 'parent-b': mockedNodeModule('parent-b', {
+ pkgVersion: '1.0.0',
+ pkgDependencies: { dep: '*' },
+ }),
+ },
+ 'dep@2.0.0/node_modules': {
+ dep: mockedNodeModule('dep', { pkgVersion: '2.0.0' }),
+ },
+ },
+ },
+ },
+ projectRoot
+ );
+ symlinkMany({
+ 'node_modules/parent-a': 'node_modules/.pnpm/parent-a@1.0.0/node_modules/parent-a',
+ 'node_modules/parent-b': 'node_modules/.pnpm/parent-b@1.0.0/node_modules/parent-b',
+ 'node_modules/.pnpm/parent-a@1.0.0/node_modules/dep':
+ 'node_modules/.pnpm/dep@2.0.0/node_modules/dep',
+ 'node_modules/.pnpm/parent-b@1.0.0/node_modules/dep':
+ 'node_modules/.pnpm/dep@2.0.0/node_modules/dep',
+ });
+ return await scanDependenciesRecursively(projectRoot);
+ });
+ expect(_verifyMemoizerFreed()).toBe(true);
+ return result;
+ };
+
+ const resultAB = await runWithOrder('parent-a', 'parent-b');
+ vol.reset();
+ const resultBA = await runWithOrder('parent-b', 'parent-a');
+
+ for (const key of Object.keys(resultAB)) {
+ expect(resultAB[key]?.path).toBe(resultBA[key]?.path);
+ expect(resultAB[key]?.version).toBe(resultBA[key]?.version);
+ }
+ expect(resultAB['dep']?.version).toBe('2.0.0');
+ });
+
+ it('resolves multi-level isolated duplicates with stable path and version, unstable originPath', async () => {
+ const runWithOrder = async (first: string, second: string) => {
+ const result = await createMemoizer().withMemoizer(async () => {
+ vol.fromNestedJSON(
+ {
+ ...mockedNodeModule('root', {
+ pkgDependencies: { [first]: '*', [second]: '*' },
+ }),
+ node_modules: {
+ '.pnpm': {
+ 'parent-a@1.0.0/node_modules': {
+ 'parent-a': mockedNodeModule('parent-a', {
+ pkgVersion: '1.0.0',
+ pkgDependencies: { 'shared-dep': '*' },
+ }),
+ },
+ 'parent-b@1.0.0/node_modules': {
+ 'parent-b': mockedNodeModule('parent-b', {
+ pkgVersion: '1.0.0',
+ pkgDependencies: { 'shared-dep': '*' },
+ }),
+ },
+ 'shared-dep@2.0.0/node_modules': {
+ 'shared-dep': mockedNodeModule('shared-dep', {
+ pkgVersion: '2.0.0',
+ pkgDependencies: { grandchild: '*' },
+ }),
+ },
+ 'grandchild@3.0.0/node_modules': {
+ grandchild: mockedNodeModule('grandchild', { pkgVersion: '3.0.0' }),
+ },
+ },
+ },
+ },
+ projectRoot
+ );
+ symlinkMany({
+ 'node_modules/parent-a': 'node_modules/.pnpm/parent-a@1.0.0/node_modules/parent-a',
+ 'node_modules/parent-b': 'node_modules/.pnpm/parent-b@1.0.0/node_modules/parent-b',
+ 'node_modules/.pnpm/parent-a@1.0.0/node_modules/shared-dep':
+ 'node_modules/.pnpm/shared-dep@2.0.0/node_modules/shared-dep',
+ 'node_modules/.pnpm/parent-b@1.0.0/node_modules/shared-dep':
+ 'node_modules/.pnpm/shared-dep@2.0.0/node_modules/shared-dep',
+ 'node_modules/.pnpm/shared-dep@2.0.0/node_modules/grandchild':
+ 'node_modules/.pnpm/grandchild@3.0.0/node_modules/grandchild',
+ });
+ return await scanDependenciesRecursively(projectRoot);
+ });
+ expect(_verifyMemoizerFreed()).toBe(true);
+ return result;
+ };
+
+ const resultAB = await runWithOrder('parent-a', 'parent-b');
+ vol.reset();
+ const resultBA = await runWithOrder('parent-b', 'parent-a');
+
+ for (const key of Object.keys(resultAB)) {
+ expect(resultAB[key]?.path).toBe(resultBA[key]?.path);
+ expect(resultAB[key]?.version).toBe(resultBA[key]?.version);
+ expect(resultAB[key]?.depth).toBe(resultBA[key]?.depth);
+ expect(resultAB[key]?.source).toBe(resultBA[key]?.source);
+ expect(resultAB[key]?.name).toBe(resultBA[key]?.name);
+ }
+ expect(resultAB['shared-dep']?.version).toBe('2.0.0');
+ expect(resultAB['grandchild']?.version).toBe('3.0.0');
+ expect(resultAB['shared-dep']?.originPath).not.toBe(resultBA['shared-dep']?.originPath);
+ });
+
+ it('resolves mixed-depth diamond with stable path and version', async () => {
+ const runWithOrder = async (first: string, second: string) => {
+ const result = await createMemoizer().withMemoizer(async () => {
+ vol.fromNestedJSON(
+ {
+ ...mockedNodeModule('root', {
+ pkgDependencies: { [first]: '*', [second]: '*' },
+ }),
+ node_modules: {
+ '.pnpm': {
+ 'parent-a@1.0.0/node_modules': {
+ 'parent-a': mockedNodeModule('parent-a', {
+ pkgVersion: '1.0.0',
+ pkgDependencies: { 'shared-dep': '*' },
+ }),
+ },
+ 'parent-b@1.0.0/node_modules': {
+ 'parent-b': mockedNodeModule('parent-b', {
+ pkgVersion: '1.0.0',
+ pkgDependencies: { 'mid-b': '*' },
+ }),
+ },
+ 'mid-b@1.5.0/node_modules': {
+ 'mid-b': mockedNodeModule('mid-b', {
+ pkgVersion: '1.5.0',
+ pkgDependencies: { 'shared-dep': '*' },
+ }),
+ },
+ 'shared-dep@2.0.0/node_modules': {
+ 'shared-dep': mockedNodeModule('shared-dep', {
+ pkgVersion: '2.0.0',
+ pkgDependencies: { grandchild: '*' },
+ }),
+ },
+ 'grandchild@3.0.0/node_modules': {
+ grandchild: mockedNodeModule('grandchild', { pkgVersion: '3.0.0' }),
+ },
+ },
+ },
+ },
+ projectRoot
+ );
+ symlinkMany({
+ 'node_modules/parent-a': 'node_modules/.pnpm/parent-a@1.0.0/node_modules/parent-a',
+ 'node_modules/parent-b': 'node_modules/.pnpm/parent-b@1.0.0/node_modules/parent-b',
+ 'node_modules/.pnpm/parent-b@1.0.0/node_modules/mid-b':
+ 'node_modules/.pnpm/mid-b@1.5.0/node_modules/mid-b',
+ 'node_modules/.pnpm/parent-a@1.0.0/node_modules/shared-dep':
+ 'node_modules/.pnpm/shared-dep@2.0.0/node_modules/shared-dep',
+ 'node_modules/.pnpm/mid-b@1.5.0/node_modules/shared-dep':
+ 'node_modules/.pnpm/shared-dep@2.0.0/node_modules/shared-dep',
+ 'node_modules/.pnpm/shared-dep@2.0.0/node_modules/grandchild':
+ 'node_modules/.pnpm/grandchild@3.0.0/node_modules/grandchild',
+ });
+ return await scanDependenciesRecursively(projectRoot);
+ });
+ expect(_verifyMemoizerFreed()).toBe(true);
+ return result;
+ };
+
+ const resultAB = await runWithOrder('parent-a', 'parent-b');
+ vol.reset();
+ const resultBA = await runWithOrder('parent-b', 'parent-a');
+
+ for (const key of Object.keys(resultAB)) {
+ expect(resultAB[key]?.path).toBe(resultBA[key]?.path);
+ expect(resultAB[key]?.version).toBe(resultBA[key]?.version);
+ }
+ expect(resultAB['shared-dep']?.depth).toBe(1);
+ });
+
+ it('resolves isolated duplicates with stable version when three parents share a dependency', async () => {
+ const runWithOrder = async (first: string, second: string) => {
+ const result = await createMemoizer().withMemoizer(async () => {
+ vol.fromNestedJSON(
+ {
+ ...mockedNodeModule('root', {
+ pkgDependencies: { [first]: '*', [second]: '*', 'parent-c': '*' },
+ }),
+ node_modules: {
+ '.pnpm': {
+ 'parent-a@1.0.0/node_modules': {
+ 'parent-a': mockedNodeModule('parent-a', {
+ pkgVersion: '1.0.0',
+ pkgDependencies: { dep: '*' },
+ }),
+ },
+ 'parent-b@1.0.0/node_modules': {
+ 'parent-b': mockedNodeModule('parent-b', {
+ pkgVersion: '1.0.0',
+ pkgDependencies: { dep: '*' },
+ }),
+ },
+ 'parent-c@1.0.0/node_modules': {
+ 'parent-c': mockedNodeModule('parent-c', {
+ pkgVersion: '1.0.0',
+ pkgDependencies: { dep: '*' },
+ }),
+ },
+ 'dep@2.0.0/node_modules': {
+ dep: mockedNodeModule('dep', { pkgVersion: '2.0.0' }),
+ },
+ },
+ },
+ },
+ projectRoot
+ );
+ symlinkMany({
+ 'node_modules/parent-a': 'node_modules/.pnpm/parent-a@1.0.0/node_modules/parent-a',
+ 'node_modules/parent-b': 'node_modules/.pnpm/parent-b@1.0.0/node_modules/parent-b',
+ 'node_modules/parent-c': 'node_modules/.pnpm/parent-c@1.0.0/node_modules/parent-c',
+ 'node_modules/.pnpm/parent-a@1.0.0/node_modules/dep':
+ 'node_modules/.pnpm/dep@2.0.0/node_modules/dep',
+ 'node_modules/.pnpm/parent-b@1.0.0/node_modules/dep':
+ 'node_modules/.pnpm/dep@2.0.0/node_modules/dep',
+ 'node_modules/.pnpm/parent-c@1.0.0/node_modules/dep':
+ 'node_modules/.pnpm/dep@2.0.0/node_modules/dep',
+ });
+ return await scanDependenciesRecursively(projectRoot);
+ });
+ expect(_verifyMemoizerFreed()).toBe(true);
+ return result;
+ };
+
+ const resultAB = await runWithOrder('parent-a', 'parent-b');
+ vol.reset();
+ const resultBA = await runWithOrder('parent-b', 'parent-a');
+
+ for (const key of Object.keys(resultAB)) {
+ expect(resultAB[key]?.path).toBe(resultBA[key]?.path);
+ expect(resultAB[key]?.version).toBe(resultBA[key]?.version);
+ }
+ expect(resultAB['dep']?.version).toBe('2.0.0');
+ });
+
itWithMemoize('ignores transitive optional peer dependencies', async () => {
vol.fromNestedJSON(
{
From 9f5530f208ee211636d37641b032e476775b1168 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?nishan=20=28o=5E=E2=96=BD=5Eo=29?=
Date: Wed, 4 Mar 2026 00:13:57 +0530
Subject: [PATCH 5/7] [ui][iOS] - Add `Slot` component (#43607)
---
packages/expo-ui/CHANGELOG.md | 2 +
.../ConfirmationDialog/index.d.ts.map | 2 +-
.../build/swift-ui/ContextMenu/index.d.ts.map | 2 +-
.../swift-ui/ControlGroup/index.d.ts.map | 2 +-
.../build/swift-ui/Gauge/index.d.ts.map | 2 +-
.../build/swift-ui/Label/index.d.ts.map | 2 +-
.../swift-ui/LabeledContent/index.d.ts.map | 2 +-
.../build/swift-ui/Menu/index.d.ts.map | 2 +-
.../build/swift-ui/Picker/index.d.ts.map | 2 +-
.../build/swift-ui/Popover/index.d.ts.map | 2 +-
.../build/swift-ui/Section/index.d.ts.map | 2 +-
.../build/swift-ui/Slider/index.d.ts.map | 2 +-
packages/expo-ui/build/swift-ui/SlotView.d.ts | 5 ++
.../expo-ui/build/swift-ui/SlotView.d.ts.map | 1 +
.../ConfirmationDialog.swift | 12 ++---
.../ConfirmationDialogComponents.swift | 26 ----------
.../ConfirmationDialogProps.swift | 5 --
.../expo-ui/ios/ContextMenu/ContextMenu.swift | 49 ++++++++++---------
.../ContextMenu/ContextMenuComponents.swift | 37 --------------
.../ios/ContextMenu/ContextMenuRecords.swift | 6 ---
packages/expo-ui/ios/ControlGroupView.swift | 14 +-----
packages/expo-ui/ios/ExpoUIModule.swift | 27 +---------
packages/expo-ui/ios/GaugeView.swift | 30 ++----------
packages/expo-ui/ios/Label.swift | 19 +------
packages/expo-ui/ios/LabeledContentView.swift | 30 ++----------
.../expo-ui/ios/Menu/MenuComponents.swift | 12 -----
packages/expo-ui/ios/Menu/MenuRecords.swift | 2 -
packages/expo-ui/ios/Menu/MenuView.swift | 7 +--
.../expo-ui/ios/Picker/PickerComponents.swift | 24 ---------
packages/expo-ui/ios/Picker/PickerView.swift | 8 +--
.../ios/Popover/PopoverComponents.swift | 18 -------
.../expo-ui/ios/Popover/PopoverProps.swift | 4 --
.../expo-ui/ios/Popover/PopoverView.swift | 8 +--
packages/expo-ui/ios/SectionComponents.swift | 34 -------------
packages/expo-ui/ios/SectionView.swift | 18 +++----
packages/expo-ui/ios/SliderView.swift | 28 ++---------
packages/expo-ui/ios/SlotView.swift | 38 ++++++++++++++
.../src/swift-ui/ConfirmationDialog/index.tsx | 16 ++----
.../src/swift-ui/ContextMenu/index.tsx | 26 +++-------
.../src/swift-ui/ControlGroup/index.tsx | 10 +---
packages/expo-ui/src/swift-ui/Gauge/index.tsx | 25 ++--------
packages/expo-ui/src/swift-ui/Label/index.tsx | 7 +--
.../src/swift-ui/LabeledContent/index.tsx | 15 ++----
packages/expo-ui/src/swift-ui/Menu/index.tsx | 8 +--
.../expo-ui/src/swift-ui/Picker/index.tsx | 15 ++----
.../expo-ui/src/swift-ui/Popover/index.tsx | 15 ++----
.../expo-ui/src/swift-ui/Section/index.tsx | 13 ++---
.../expo-ui/src/swift-ui/Slider/index.tsx | 16 ++----
packages/expo-ui/src/swift-ui/SlotView.tsx | 8 +++
49 files changed, 161 insertions(+), 499 deletions(-)
create mode 100644 packages/expo-ui/build/swift-ui/SlotView.d.ts
create mode 100644 packages/expo-ui/build/swift-ui/SlotView.d.ts.map
delete mode 100644 packages/expo-ui/ios/ConfirmationDialog/ConfirmationDialogComponents.swift
delete mode 100644 packages/expo-ui/ios/ContextMenu/ContextMenuComponents.swift
delete mode 100644 packages/expo-ui/ios/Menu/MenuComponents.swift
delete mode 100644 packages/expo-ui/ios/Picker/PickerComponents.swift
delete mode 100644 packages/expo-ui/ios/Popover/PopoverComponents.swift
delete mode 100644 packages/expo-ui/ios/SectionComponents.swift
create mode 100644 packages/expo-ui/ios/SlotView.swift
create mode 100644 packages/expo-ui/src/swift-ui/SlotView.tsx
diff --git a/packages/expo-ui/CHANGELOG.md b/packages/expo-ui/CHANGELOG.md
index cf7225d26f7a8a..e4f38d3c404bea 100644
--- a/packages/expo-ui/CHANGELOG.md
+++ b/packages/expo-ui/CHANGELOG.md
@@ -16,6 +16,8 @@
### 💡 Others
+- [iOS] Introduce `SlotView` to replace structural child view types with a single generic slot. ([#43607](https://github.com/expo/expo/pull/43607) by [@nishan](https://github.com/intergalacticspacehighway))
+
## 55.0.1 — 2026-02-25
### 🎉 New features
diff --git a/packages/expo-ui/build/swift-ui/ConfirmationDialog/index.d.ts.map b/packages/expo-ui/build/swift-ui/ConfirmationDialog/index.d.ts.map
index 3ea95004f6700d..4252b576e3d075 100644
--- a/packages/expo-ui/build/swift-ui/ConfirmationDialog/index.d.ts.map
+++ b/packages/expo-ui/build/swift-ui/ConfirmationDialog/index.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/ConfirmationDialog/index.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC;;;OAGG;IACH,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;OAEG;IACH,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,OAAO,KAAK,IAAI,CAAC;IACrD;;;OAGG;IACH,eAAe,CAAC,EAAE,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;CACtD,GAAG,uBAAuB,CAAC;AAuC5B;;;;GAIG;AACH,iBAAS,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,+BAezD;kBAfQ,kBAAkB;yBAvBH;QAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;KAAE;yBAO7B;QAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;KAAE;yBAO7B;QAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;KAAE;;AA8BrD,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
\ No newline at end of file
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/ConfirmationDialog/index.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD;;GAEG;AACH,MAAM,MAAM,uBAAuB,GAAG;IACpC;;;OAGG;IACH,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;OAEG;IACH,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,OAAO,KAAK,IAAI,CAAC;IACrD;;;OAGG;IACH,eAAe,CAAC,EAAE,WAAW,GAAG,SAAS,GAAG,QAAQ,CAAC;CACtD,GAAG,uBAAuB,CAAC;AA8B5B;;;;GAIG;AACH,iBAAS,kBAAkB,CAAC,KAAK,EAAE,uBAAuB,+BAezD;kBAfQ,kBAAkB;yBAvBH;QAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;KAAE;yBAO7B;QAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;KAAE;yBAO7B;QAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;KAAE;;AA8BrD,OAAO,EAAE,kBAAkB,EAAE,CAAC"}
\ No newline at end of file
diff --git a/packages/expo-ui/build/swift-ui/ContextMenu/index.d.ts.map b/packages/expo-ui/build/swift-ui/ContextMenu/index.d.ts.map
index 1922da8ded9656..7840fbd5217916 100644
--- a/packages/expo-ui/build/swift-ui/ContextMenu/index.d.ts.map
+++ b/packages/expo-ui/build/swift-ui/ContextMenu/index.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/ContextMenu/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAEhD,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAqBhD;;GAEG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,+BAEzD;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,+BAE3D;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,+BAE3D;AAED;;GAEG;AACH,iBAAS,WAAW,CAAC,KAAK,EAAE,gBAAgB,+BAE3C;kBAFQ,WAAW;;;;;AAQpB,OAAO,EAAE,WAAW,EAAE,CAAC"}
\ No newline at end of file
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/ContextMenu/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAGhD,OAAO,EAAE,KAAK,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAMhD;;GAEG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,+BAEzD;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,+BAE3D;AAED;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,+BAE3D;AAED;;GAEG;AACH,iBAAS,WAAW,CAAC,KAAK,EAAE,gBAAgB,+BAE3C;kBAFQ,WAAW;;;;;AAQpB,OAAO,EAAE,WAAW,EAAE,CAAC"}
\ No newline at end of file
diff --git a/packages/expo-ui/build/swift-ui/ControlGroup/index.d.ts.map b/packages/expo-ui/build/swift-ui/ControlGroup/index.d.ts.map
index 2966e2c099f624..651b5e0cced82a 100644
--- a/packages/expo-ui/build/swift-ui/ControlGroup/index.d.ts.map
+++ b/packages/expo-ui/build/swift-ui/ControlGroup/index.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/ControlGroup/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAsB,MAAM,OAAO,CAAC;AAC3D,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGtD,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD,MAAM,WAAW,iBAAkB,SAAQ,uBAAuB;IAChE;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB;;;;;OAKG;IACH,QAAQ,EAAE,SAAS,CAAC;CACrB;AAgBD,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,+BAkBpD"}
\ No newline at end of file
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/ControlGroup/index.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,KAAK,SAAS,EAAsB,MAAM,OAAO,CAAC;AAC3D,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAItD,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD,MAAM,WAAW,iBAAkB,SAAQ,uBAAuB;IAChE;;;;;;OAMG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,SAAS,CAAC;IAC3B;;;;;OAKG;IACH,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB;;;;;OAKG;IACH,QAAQ,EAAE,SAAS,CAAC;CACrB;AAWD,wBAAgB,YAAY,CAAC,KAAK,EAAE,iBAAiB,+BAgBpD"}
\ No newline at end of file
diff --git a/packages/expo-ui/build/swift-ui/Gauge/index.d.ts.map b/packages/expo-ui/build/swift-ui/Gauge/index.d.ts.map
index 7220eee605bb89..74700fbaeda866 100644
--- a/packages/expo-ui/build/swift-ui/Gauge/index.d.ts.map
+++ b/packages/expo-ui/build/swift-ui/Gauge/index.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/Gauge/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAOxD,MAAM,MAAM,UAAU,GAAG;IACvB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B;;OAEG;IACH,iBAAiB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACpC;;OAEG;IACH,iBAAiB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACpC;;OAEG;IACH,iBAAiB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACrC,GAAG,uBAAuB,CAAC;AAmB5B;;GAEG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,+BA2BtC"}
\ No newline at end of file
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/Gauge/index.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD,MAAM,MAAM,UAAU,GAAG;IACvB;;OAEG;IACH,KAAK,EAAE,MAAM,CAAC;IACd;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;;OAGG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAC3B;;OAEG;IACH,iBAAiB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACpC;;OAEG;IACH,iBAAiB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACpC;;OAEG;IACH,iBAAiB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CACrC,GAAG,uBAAuB,CAAC;AAc5B;;GAEG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,+BAqBtC"}
\ No newline at end of file
diff --git a/packages/expo-ui/build/swift-ui/Label/index.d.ts.map b/packages/expo-ui/build/swift-ui/Label/index.d.ts.map
index 37a72fe0483d0c..0a790c975219a4 100644
--- a/packages/expo-ui/build/swift-ui/Label/index.d.ts.map
+++ b/packages/expo-ui/build/swift-ui/Label/index.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/Label/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGtD,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD,MAAM,MAAM,UAAU,GAAG;IACvB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,WAAW,CAAC,EAAE,QAAQ,CAAC;IAEvB;;;OAGG;IACH,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAEvB;;;OAGG;IACH,KAAK,CAAC,EAAE,UAAU,CAAC;CACpB,GAAG,uBAAuB,CAAC;AAS5B;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,+BAUtC"}
\ No newline at end of file
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/Label/index.tsx"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,cAAc,CAAC;AAC/C,OAAO,EAAE,KAAK,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAItD,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD,MAAM,MAAM,UAAU,GAAG;IACvB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;OAEG;IACH,WAAW,CAAC,EAAE,QAAQ,CAAC;IAEvB;;;OAGG;IACH,IAAI,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IAEvB;;;OAGG;IACH,KAAK,CAAC,EAAE,UAAU,CAAC;CACpB,GAAG,uBAAuB,CAAC;AAK5B;;;;;GAKG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,UAAU,+BAUtC"}
\ No newline at end of file
diff --git a/packages/expo-ui/build/swift-ui/LabeledContent/index.d.ts.map b/packages/expo-ui/build/swift-ui/LabeledContent/index.d.ts.map
index 76040f264b0f19..bc79b0d2f6a4bc 100644
--- a/packages/expo-ui/build/swift-ui/LabeledContent/index.d.ts.map
+++ b/packages/expo-ui/build/swift-ui/LabeledContent/index.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/LabeledContent/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD,MAAM,MAAM,mBAAmB,GAAG;IAChC;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC;IACjC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,GAAG,uBAAuB,CAAC;AAiB5B;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,+BAexD"}
\ No newline at end of file
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/LabeledContent/index.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD,MAAM,MAAM,mBAAmB,GAAG;IAChC;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC;IACjC,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;CAC3B,GAAG,uBAAuB,CAAC;AAO5B;;;;;GAKG;AACH,wBAAgB,cAAc,CAAC,KAAK,EAAE,mBAAmB,+BAexD"}
\ No newline at end of file
diff --git a/packages/expo-ui/build/swift-ui/Menu/index.d.ts.map b/packages/expo-ui/build/swift-ui/Menu/index.d.ts.map
index 7b872e5c2cfc68..73a0dd452d2d87 100644
--- a/packages/expo-ui/build/swift-ui/Menu/index.d.ts.map
+++ b/packages/expo-ui/build/swift-ui/Menu/index.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/Menu/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AAezC;;GAEG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,SAAS,+BAgBpC"}
\ No newline at end of file
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/Menu/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AAGzC,OAAO,EAAE,KAAK,SAAS,EAAE,MAAM,SAAS,CAAC;AAUzC;;GAEG;AACH,wBAAgB,IAAI,CAAC,KAAK,EAAE,SAAS,+BAgBpC"}
\ No newline at end of file
diff --git a/packages/expo-ui/build/swift-ui/Picker/index.d.ts.map b/packages/expo-ui/build/swift-ui/Picker/index.d.ts.map
index 7b3d7859f5e532..bd333ea6d1606f 100644
--- a/packages/expo-ui/build/swift-ui/Picker/index.d.ts.map
+++ b/packages/expo-ui/build/swift-ui/Picker/index.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/Picker/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAGtD,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD,KAAK,kBAAkB,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;AACjD,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,kBAAkB,GAAG,GAAG,IAAI;IAC5D;;;OAGG;IACH,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC;IACjC;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,CAAC;IACd;;;OAGG;IACH,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,KAAK,IAAI,CAAC;IAE3C;;OAEG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,GAAG,uBAAuB,CAAC;AAoC5B;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,kBAAkB,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,+BAgBzE"}
\ No newline at end of file
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/Picker/index.tsx"],"names":[],"mappings":"AAEA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAC;AAItD,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD,KAAK,kBAAkB,GAAG,MAAM,GAAG,MAAM,GAAG,IAAI,CAAC;AACjD,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,kBAAkB,GAAG,GAAG,IAAI;IAC5D;;;OAGG;IACH,WAAW,CAAC,EAAE,QAAQ,CAAC;IACvB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,GAAG,KAAK,CAAC,SAAS,CAAC;IACjC;;OAEG;IACH,SAAS,CAAC,EAAE,CAAC,CAAC;IACd;;;OAGG;IACH,iBAAiB,CAAC,EAAE,CAAC,SAAS,EAAE,CAAC,KAAK,IAAI,CAAC;IAE3C;;OAEG;IACH,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;CAC5B,GAAG,uBAAuB,CAAC;AA4B5B;;;;;;;;;GASG;AACH,wBAAgB,MAAM,CAAC,CAAC,SAAS,kBAAkB,EAAE,KAAK,EAAE,WAAW,CAAC,CAAC,CAAC,+BAgBzE"}
\ No newline at end of file
diff --git a/packages/expo-ui/build/swift-ui/Popover/index.d.ts.map b/packages/expo-ui/build/swift-ui/Popover/index.d.ts.map
index f7d2f5636ff488..5a1fa88a994322 100644
--- a/packages/expo-ui/build/swift-ui/Popover/index.d.ts.map
+++ b/packages/expo-ui/build/swift-ui/Popover/index.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/Popover/index.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;OAEG;IACH,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,OAAO,KAAK,IAAI,CAAC;IACrD;;OAEG;IACH,gBAAgB,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,QAAQ,GAAG,KAAK,GAAG,QAAQ,CAAC;IACxE;;;OAGG;IACH,SAAS,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;CAChE,GAAG,uBAAuB,CAAC;AAqB5B,iBAAS,cAAc,CAAC,KAAK,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,+BAE3D;AAED,iBAAS,cAAc,CAAC,KAAK,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,+BAE3D;AAKD,wBAAgB,OAAO,CAAC,KAAK,EAAE,gBAAgB,+BAgB9C;yBAhBe,OAAO"}
\ No newline at end of file
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/Popover/index.tsx"],"names":[],"mappings":"AAKA,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD,MAAM,MAAM,gBAAgB,GAAG;IAC7B,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;OAEG;IACH,WAAW,CAAC,EAAE,OAAO,CAAC;IACtB;;OAEG;IACH,mBAAmB,CAAC,EAAE,CAAC,WAAW,EAAE,OAAO,KAAK,IAAI,CAAC;IACrD;;OAEG;IACH,gBAAgB,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,QAAQ,GAAG,KAAK,GAAG,QAAQ,CAAC;IACxE;;;OAGG;IACH,SAAS,CAAC,EAAE,SAAS,GAAG,UAAU,GAAG,KAAK,GAAG,QAAQ,GAAG,MAAM,CAAC;CAChE,GAAG,uBAAuB,CAAC;AAW5B,iBAAS,cAAc,CAAC,KAAK,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,+BAE3D;AAED,iBAAS,cAAc,CAAC,KAAK,EAAE;IAAE,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,+BAE3D;AAKD,wBAAgB,OAAO,CAAC,KAAK,EAAE,gBAAgB,+BAgB9C;yBAhBe,OAAO"}
\ No newline at end of file
diff --git a/packages/expo-ui/build/swift-ui/Section/index.d.ts.map b/packages/expo-ui/build/swift-ui/Section/index.d.ts.map
index bba991fb28d516..ce61711440da5d 100644
--- a/packages/expo-ui/build/swift-ui/Section/index.d.ts.map
+++ b/packages/expo-ui/build/swift-ui/Section/index.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/Section/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD,MAAM,MAAM,YAAY,GAAG;IACzB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB;;OAEG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB;;OAEG;IACH,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,IAAI,CAAC;CACpD,GAAG,uBAAuB,CAAC;AAiB5B;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,+BAgB1C"}
\ No newline at end of file
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/Section/index.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD,MAAM,MAAM,YAAY,GAAG;IACzB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB;;OAEG;IACH,MAAM,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACzB;;OAEG;IACH,QAAQ,EAAE,KAAK,CAAC,SAAS,CAAC;IAC1B;;;;;;OAMG;IACH,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB;;;;OAIG;IACH,kBAAkB,CAAC,EAAE,CAAC,UAAU,EAAE,OAAO,KAAK,IAAI,CAAC;CACpD,GAAG,uBAAuB,CAAC;AAW5B;;GAEG;AACH,wBAAgB,OAAO,CAAC,KAAK,EAAE,YAAY,+BAgB1C"}
\ No newline at end of file
diff --git a/packages/expo-ui/build/swift-ui/Slider/index.d.ts.map b/packages/expo-ui/build/swift-ui/Slider/index.d.ts.map
index c85ca620facef3..aae978915076b8 100644
--- a/packages/expo-ui/build/swift-ui/Slider/index.d.ts.map
+++ b/packages/expo-ui/build/swift-ui/Slider/index.d.ts.map
@@ -1 +1 @@
-{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/Slider/index.tsx"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD,MAAM,MAAM,WAAW,GAAG;IACxB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB;;OAEG;IACH,iBAAiB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACpC;;OAEG;IACH,iBAAiB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACpC;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;OAEG;IACH,gBAAgB,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;CACjD,GAAG,uBAAuB,CAAC;AA6C5B,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,+BAcxC"}
\ No newline at end of file
+{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/swift-ui/Slider/index.tsx"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,uBAAuB,EAAE,MAAM,UAAU,CAAC;AAExD,MAAM,MAAM,WAAW,GAAG;IACxB;;OAEG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IACf;;OAEG;IACH,IAAI,CAAC,EAAE,MAAM,CAAC;IACd;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,GAAG,CAAC,EAAE,MAAM,CAAC;IACb;;OAEG;IACH,KAAK,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACxB;;OAEG;IACH,iBAAiB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACpC;;OAEG;IACH,iBAAiB,CAAC,EAAE,KAAK,CAAC,SAAS,CAAC;IACpC;;OAEG;IACH,aAAa,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACxC;;OAEG;IACH,gBAAgB,CAAC,EAAE,CAAC,SAAS,EAAE,OAAO,KAAK,IAAI,CAAC;CACjD,GAAG,uBAAuB,CAAC;AAwC5B,wBAAgB,MAAM,CAAC,KAAK,EAAE,WAAW,+BAUxC"}
\ No newline at end of file
diff --git a/packages/expo-ui/build/swift-ui/SlotView.d.ts b/packages/expo-ui/build/swift-ui/SlotView.d.ts
new file mode 100644
index 00000000000000..28cff5320df4ab
--- /dev/null
+++ b/packages/expo-ui/build/swift-ui/SlotView.d.ts
@@ -0,0 +1,5 @@
+export declare function Slot({ name, children }: {
+ name: string;
+ children?: React.ReactNode;
+}): import("react").JSX.Element;
+//# sourceMappingURL=SlotView.d.ts.map
\ No newline at end of file
diff --git a/packages/expo-ui/build/swift-ui/SlotView.d.ts.map b/packages/expo-ui/build/swift-ui/SlotView.d.ts.map
new file mode 100644
index 00000000000000..3b79a7980ef948
--- /dev/null
+++ b/packages/expo-ui/build/swift-ui/SlotView.d.ts.map
@@ -0,0 +1 @@
+{"version":3,"file":"SlotView.d.ts","sourceRoot":"","sources":["../../src/swift-ui/SlotView.tsx"],"names":[],"mappings":"AAKA,wBAAgB,IAAI,CAAC,EAAE,IAAI,EAAE,QAAQ,EAAE,EAAE;IAAE,IAAI,EAAE,MAAM,CAAC;IAAC,QAAQ,CAAC,EAAE,KAAK,CAAC,SAAS,CAAA;CAAE,+BAEpF"}
\ No newline at end of file
diff --git a/packages/expo-ui/ios/ConfirmationDialog/ConfirmationDialog.swift b/packages/expo-ui/ios/ConfirmationDialog/ConfirmationDialog.swift
index 5972354c68b515..2a64d87fc88232 100644
--- a/packages/expo-ui/ios/ConfirmationDialog/ConfirmationDialog.swift
+++ b/packages/expo-ui/ios/ConfirmationDialog/ConfirmationDialog.swift
@@ -33,9 +33,7 @@ internal struct ConfirmationDialogView: ExpoSwiftUI.View {
@ViewBuilder
private var triggerContent: some View {
- if let trigger = props.children?
- .compactMap({ $0.childView as? ConfirmationDialogTrigger })
- .first {
+ if let trigger = props.children?.slot("trigger") {
trigger
} else {
EmptyView()
@@ -47,18 +45,14 @@ internal struct ConfirmationDialogView: ExpoSwiftUI.View {
@ViewBuilder
private var actionsContent: some View {
- if let actions = props.children?
- .compactMap({ $0.childView as? ConfirmationDialogActions })
- .first {
+ if let actions = props.children?.slot("actions") {
actions
}
}
@ViewBuilder
private var messageContent: some View {
- if let message = props.children?
- .compactMap({ $0.childView as? ConfirmationDialogMessage })
- .first {
+ if let message = props.children?.slot("message") {
message
}
}
diff --git a/packages/expo-ui/ios/ConfirmationDialog/ConfirmationDialogComponents.swift b/packages/expo-ui/ios/ConfirmationDialog/ConfirmationDialogComponents.swift
deleted file mode 100644
index 730ab3b9bf205e..00000000000000
--- a/packages/expo-ui/ios/ConfirmationDialog/ConfirmationDialogComponents.swift
+++ /dev/null
@@ -1,26 +0,0 @@
-import SwiftUI
-import ExpoModulesCore
-
-internal struct ConfirmationDialogTrigger: ExpoSwiftUI.View {
- @ObservedObject var props: ConfirmationDialogTriggerProps
-
- var body: some View {
- Children()
- }
-}
-
-internal struct ConfirmationDialogActions: ExpoSwiftUI.View {
- @ObservedObject var props: ConfirmationDialogActionsProps
-
- var body: some View {
- Children()
- }
-}
-
-internal struct ConfirmationDialogMessage: ExpoSwiftUI.View {
- @ObservedObject var props: ConfirmationDialogMessageProps
-
- var body: some View {
- Children()
- }
-}
diff --git a/packages/expo-ui/ios/ConfirmationDialog/ConfirmationDialogProps.swift b/packages/expo-ui/ios/ConfirmationDialog/ConfirmationDialogProps.swift
index d8986bf1b1c4c8..2e96bc978a5252 100644
--- a/packages/expo-ui/ios/ConfirmationDialog/ConfirmationDialogProps.swift
+++ b/packages/expo-ui/ios/ConfirmationDialog/ConfirmationDialogProps.swift
@@ -8,8 +8,3 @@ internal final class ConfirmationDialogProps: UIBaseViewProps {
var onIsPresentedChange = EventDispatcher()
}
-internal final class ConfirmationDialogTriggerProps: ExpoSwiftUI.ViewProps {}
-
-internal final class ConfirmationDialogActionsProps: ExpoSwiftUI.ViewProps {}
-
-internal final class ConfirmationDialogMessageProps: ExpoSwiftUI.ViewProps {}
diff --git a/packages/expo-ui/ios/ContextMenu/ContextMenu.swift b/packages/expo-ui/ios/ContextMenu/ContextMenu.swift
index 634fe123e97b6b..e6fdb04f021c52 100644
--- a/packages/expo-ui/ios/ContextMenu/ContextMenu.swift
+++ b/packages/expo-ui/ios/ContextMenu/ContextMenu.swift
@@ -21,33 +21,38 @@ struct ContextMenuWithPreview: View {
+ let activationElement: ActivationElement
+ let menuContent: MenuContent
var body: some View {
- let activationElement = (props.children?
- .compactMap { $0.childView as? ContextMenuActivationElement }
- .first) ?? ContextMenuActivationElement(props: ContextMenuActivationElementProps())
+ activationElement.contextMenu(menuItems: {
+ menuContent
+ })
+ }
+}
- let menuContent = (props.children?
- .compactMap { $0.childView as? ContextMenuContent }
- .first) ?? ContextMenuContent(props: ContextMenuContentProps())
+struct ContextMenu: ExpoSwiftUI.View {
+ @ObservedObject var props: ContextMenuProps
- let preview = props.children?
- .compactMap { $0.childView as? ContextMenuPreview }
- .first
+ var body: some View {
+ let activationElement = props.children?.slot("trigger")
+ let menuContent = props.children?.slot("items")
+ let preview = props.children?.slot("preview")
- if let preview {
- ContextMenuWithPreview(
- activationElement: activationElement,
- preview: preview,
- menuContent: menuContent
- )
- } else {
- LongPressContextMenu(
- activationElement: activationElement,
- menuContent: menuContent
- )
+ if let activationElement {
+ if let preview {
+ ContextMenuWithPreview(
+ activationElement: activationElement,
+ preview: preview,
+ menuContent: menuContent
+ )
+ } else {
+ LongPressContextMenu(
+ activationElement: activationElement,
+ menuContent: menuContent
+ )
+ }
}
}
}
diff --git a/packages/expo-ui/ios/ContextMenu/ContextMenuComponents.swift b/packages/expo-ui/ios/ContextMenu/ContextMenuComponents.swift
deleted file mode 100644
index 7b7aa3f8bdda21..00000000000000
--- a/packages/expo-ui/ios/ContextMenu/ContextMenuComponents.swift
+++ /dev/null
@@ -1,37 +0,0 @@
-import SwiftUI
-import ExpoModulesCore
-
-internal struct LongPressContextMenu: View {
- let activationElement: ActivationElement
- let menuContent: MenuContent
-
- var body: some View {
- activationElement.contextMenu(menuItems: {
- menuContent
- })
- }
-}
-
-internal struct ContextMenuPreview: ExpoSwiftUI.View {
- @ObservedObject var props: ContextMenuPreviewProps
-
- var body: some View {
- Children()
- }
-}
-
-internal struct ContextMenuContent: ExpoSwiftUI.View {
- @ObservedObject var props: ContextMenuContentProps
-
- var body: some View {
- Children()
- }
-}
-
-internal struct ContextMenuActivationElement: ExpoSwiftUI.View {
- @ObservedObject var props: ContextMenuActivationElementProps
-
- var body: some View {
- Children()
- }
-}
diff --git a/packages/expo-ui/ios/ContextMenu/ContextMenuRecords.swift b/packages/expo-ui/ios/ContextMenu/ContextMenuRecords.swift
index 80b6d5553dbcb5..c20b5dd0227069 100644
--- a/packages/expo-ui/ios/ContextMenu/ContextMenuRecords.swift
+++ b/packages/expo-ui/ios/ContextMenu/ContextMenuRecords.swift
@@ -1,9 +1,3 @@
import ExpoModulesCore
internal final class ContextMenuProps: UIBaseViewProps {}
-
-internal final class ContextMenuPreviewProps: ExpoSwiftUI.ViewProps {}
-
-internal final class ContextMenuActivationElementProps: ExpoSwiftUI.ViewProps {}
-
-internal final class ContextMenuContentProps: ExpoSwiftUI.ViewProps {}
diff --git a/packages/expo-ui/ios/ControlGroupView.swift b/packages/expo-ui/ios/ControlGroupView.swift
index a0a444e216b782..52b1b2bf2acb9a 100644
--- a/packages/expo-ui/ios/ControlGroupView.swift
+++ b/packages/expo-ui/ios/ControlGroupView.swift
@@ -8,25 +8,13 @@ internal final class ControlGroupViewProps: UIBaseViewProps {
@Field var systemImage: String?
}
-internal final class ControlGroupLabelProps: ExpoSwiftUI.ViewProps {}
-
-internal struct ControlGroupLabel: ExpoSwiftUI.View {
- @ObservedObject var props: ControlGroupLabelProps
-
- var body: some View {
- Children()
- }
-}
-
internal struct ControlGroupView: ExpoSwiftUI.View {
@ObservedObject var props: ControlGroupViewProps
var body: some View {
if #available(iOS 15.0, macOS 12.0, tvOS 17.0, *) {
if #available(iOS 16.0, macOS 13.0, tvOS 17.0, *) {
- let labelContent = props.children?
- .compactMap { $0.childView as? ControlGroupLabel }
- .first
+ let labelContent = props.children?.slot("label")
if let systemImage = props.systemImage, let label = props.label {
ControlGroup(label, systemImage: systemImage) { Children() }
diff --git a/packages/expo-ui/ios/ExpoUIModule.swift b/packages/expo-ui/ios/ExpoUIModule.swift
index a42baec322a5b3..6aa80ca6794eaf 100644
--- a/packages/expo-ui/ios/ExpoUIModule.swift
+++ b/packages/expo-ui/ios/ExpoUIModule.swift
@@ -63,22 +63,9 @@ public final class ExpoUIModule: Module {
// MARK: - Views don't support common view modifiers
- View(ContextMenuActivationElement.self)
- View(ContextMenuPreview.self)
- View(ContextMenuContent.self)
- View(ConfirmationDialogTrigger.self)
- View(ConfirmationDialogActions.self)
- View(ConfirmationDialogMessage.self)
+ View(SlotView.self)
View(NamespaceView.self)
- View(PopoverViewContent.self)
- View(PopoverViewPopContent.self)
- View(SectionContent.self)
- View(SectionHeader.self)
- View(SectionFooter.self)
View(GridRowView.self)
- View(LabeledContentLabel.self)
- View(LabeledContentContent.self)
- View(LabelIcon.self)
View(HostView.self)
View(TextView.self)
@@ -94,17 +81,12 @@ public final class ExpoUIModule: Module {
ExpoUIView(ConfirmationDialogView.self)
ExpoUIView(ExpoUI.ContextMenu.self)
- // ControlGroup component
ExpoUIView(ControlGroupView.self)
- View(ControlGroupLabel.self)
-
- // Menu component
+
ExpoUIView(MenuView.self)
- View(MenuLabel.self)
ExpoUIView(FormView.self)
ExpoUIView(GaugeView.self)
- View(GaugeLabelView.self)
ExpoUIView(GroupView.self)
ExpoUIView(HStackView.self)
ExpoUIView(ImageView.self)
@@ -112,17 +94,12 @@ public final class ExpoUIModule: Module {
ExpoUIView(ListView.self)
ExpoUIView(ListForEachView.self)
- // Picker
ExpoUIView(PickerView.self)
- View(PickerContentView.self)
- View(PickerLabelView.self)
ExpoUIView(ExpoUI.ProgressView.self)
ExpoUIView(SectionView.self)
- // Slider
ExpoUIView(SliderView.self)
- View(SliderLabelView.self)
ExpoUIView(SpacerView.self)
ExpoUIView(StepperView.self)
diff --git a/packages/expo-ui/ios/GaugeView.swift b/packages/expo-ui/ios/GaugeView.swift
index fa127f093040fe..be46e3dd541f63 100644
--- a/packages/expo-ui/ios/GaugeView.swift
+++ b/packages/expo-ui/ios/GaugeView.swift
@@ -31,14 +31,10 @@ public struct GaugeView: ExpoSwiftUI.View {
@ViewBuilder
private var gaugeContent: some View {
let range = (props.min ?? 0.0)...(props.max ?? 1.0)
- let label = props.children?.compactMap { $0.childView as? GaugeLabelView }
- .first { $0.props.kind == .label }
- let currentValueLabel = props.children?.compactMap { $0.childView as? GaugeLabelView }
- .first { $0.props.kind == .currentValue }
- let minimumValueLabel = props.children?.compactMap { $0.childView as? GaugeLabelView }
- .first { $0.props.kind == .minimumValue }
- let maximumValueLabel = props.children?.compactMap { $0.childView as? GaugeLabelView }
- .first { $0.props.kind == .maximumValue }
+ let label = props.children?.slot("label")
+ let currentValueLabel = props.children?.slot("currentValue")
+ let minimumValueLabel = props.children?.slot("minimumValue")
+ let maximumValueLabel = props.children?.slot("maximumValue")
Gauge(value: props.value, in: range) {
label
@@ -53,21 +49,3 @@ public struct GaugeView: ExpoSwiftUI.View {
#endif
}
-internal enum GaugeLabelKind: String, Enumerable {
- case label
- case currentValue
- case minimumValue
- case maximumValue
-}
-
-internal final class GaugeLabelProps: ExpoSwiftUI.ViewProps {
- @Field var kind: GaugeLabelKind = .label
-}
-
-internal struct GaugeLabelView: ExpoSwiftUI.View {
- @ObservedObject var props: GaugeLabelProps
-
- var body: some View {
- Children()
- }
-}
diff --git a/packages/expo-ui/ios/Label.swift b/packages/expo-ui/ios/Label.swift
index 04340eb446ab71..53334649eebbc6 100644
--- a/packages/expo-ui/ios/Label.swift
+++ b/packages/expo-ui/ios/Label.swift
@@ -8,19 +8,6 @@ public final class LabelViewProps: UIBaseViewProps {
@Field var systemImage: String?
}
-public final class LabelIconProps: ExpoSwiftUI.ViewProps {}
-public struct LabelIcon: ExpoSwiftUI.View {
- @ObservedObject public var props: LabelIconProps
-
- public init(props: LabelIconProps) {
- self.props = props
- }
-
- public var body: some View {
- Children()
- }
-}
-
public struct LabelView: ExpoSwiftUI.View {
@ObservedObject public var props: LabelViewProps
@@ -44,9 +31,7 @@ public struct LabelView: ExpoSwiftUI.View {
}
}
- private var customIcon: LabelIcon? {
- props.children?
- .compactMap({ $0.childView as? LabelIcon })
- .first
+ private var customIcon: SlotView? {
+ props.children?.slot("icon")
}
}
diff --git a/packages/expo-ui/ios/LabeledContentView.swift b/packages/expo-ui/ios/LabeledContentView.swift
index 54612d69addb15..753a16dc30155a 100644
--- a/packages/expo-ui/ios/LabeledContentView.swift
+++ b/packages/expo-ui/ios/LabeledContentView.swift
@@ -7,24 +7,6 @@ final class LabeledContentProps: UIBaseViewProps {
@Field var label: String?
}
-internal final class LabeledContentLabelProps: ExpoSwiftUI.ViewProps {}
-internal struct LabeledContentLabel: ExpoSwiftUI.View {
- @ObservedObject var props: LabeledContentLabelProps
-
- var body: some View {
- Children()
- }
-}
-
-internal final class LabeledContentContentProps: ExpoSwiftUI.ViewProps {}
-internal struct LabeledContentContent: ExpoSwiftUI.View {
- @ObservedObject var props: LabeledContentContentProps
-
- var body: some View {
- Children()
- }
-}
-
internal struct LabeledContentView: ExpoSwiftUI.View {
@ObservedObject var props: LabeledContentProps
@@ -45,25 +27,19 @@ internal struct LabeledContentView: ExpoSwiftUI.View {
}
private var hasCustomLabel: Bool {
- props.children?
- .compactMap({ $0.childView as? LabeledContentLabel })
- .first != nil
+ props.children?.slot("label") != nil
}
@ViewBuilder
private var contentChildren: some View {
- if let content = props.children?
- .compactMap({ $0.childView as? LabeledContentContent })
- .first {
+ if let content = props.children?.slot("content") {
content
}
}
@ViewBuilder
private var customLabelContent: some View {
- if let labelContent = props.children?
- .compactMap({ $0.childView as? LabeledContentLabel })
- .first {
+ if let labelContent = props.children?.slot("label") {
labelContent
}
}
diff --git a/packages/expo-ui/ios/Menu/MenuComponents.swift b/packages/expo-ui/ios/Menu/MenuComponents.swift
deleted file mode 100644
index 300713e3fb93a6..00000000000000
--- a/packages/expo-ui/ios/Menu/MenuComponents.swift
+++ /dev/null
@@ -1,12 +0,0 @@
-// Copyright 2025-present 650 Industries. All rights reserved.
-
-import SwiftUI
-import ExpoModulesCore
-
-internal struct MenuLabel: ExpoSwiftUI.View {
- @ObservedObject var props: MenuLabelProps
-
- var body: some View {
- Children()
- }
-}
diff --git a/packages/expo-ui/ios/Menu/MenuRecords.swift b/packages/expo-ui/ios/Menu/MenuRecords.swift
index 0c2146f7ec032c..e33084be9d9587 100644
--- a/packages/expo-ui/ios/Menu/MenuRecords.swift
+++ b/packages/expo-ui/ios/Menu/MenuRecords.swift
@@ -8,5 +8,3 @@ internal final class MenuProps: UIBaseViewProps {
@Field var hasPrimaryAction: Bool = false
var onPrimaryAction = EventDispatcher()
}
-
-internal final class MenuLabelProps: ExpoSwiftUI.ViewProps {}
diff --git a/packages/expo-ui/ios/Menu/MenuView.swift b/packages/expo-ui/ios/Menu/MenuView.swift
index c5d7c0c176f379..a4a35701d41f6e 100644
--- a/packages/expo-ui/ios/Menu/MenuView.swift
+++ b/packages/expo-ui/ios/Menu/MenuView.swift
@@ -9,8 +9,7 @@ internal struct MenuView: ExpoSwiftUI.View {
// If label is a component, it is passed as a child, so we need to exclude it in order to display the menu content
@ViewBuilder
func ChildrenWithoutLabel() -> some View {
- let labelView = props.children?.first(where: { $0.childView is MenuLabel })
- ForEach(props.children?.filter { $0.id != labelView?.id } ?? [], id: \.id) { child in
+ ForEach(props.children?.withoutSlot("label") ?? [], id: \.id) { child in
let view: any View = child.childView
AnyView(view)
}
@@ -18,9 +17,7 @@ internal struct MenuView: ExpoSwiftUI.View {
var body: some View {
if #available(iOS 14.0, tvOS 17.0, *) {
- let labelContent = props.children?
- .compactMap { $0.childView as? MenuLabel }
- .first
+ let labelContent = props.children?.slot("label")
if props.hasPrimaryAction {
// With primaryAction, tap triggers callback and long-press shows menu
diff --git a/packages/expo-ui/ios/Picker/PickerComponents.swift b/packages/expo-ui/ios/Picker/PickerComponents.swift
deleted file mode 100644
index fb57869d2c244e..00000000000000
--- a/packages/expo-ui/ios/Picker/PickerComponents.swift
+++ /dev/null
@@ -1,24 +0,0 @@
-// Copyright 2025-present 650 Industries. All rights reserved.
-
-import SwiftUI
-import ExpoModulesCore
-
-internal final class PickerContentProps: ExpoSwiftUI.ViewProps {}
-
-internal final class PickerLabelProps: ExpoSwiftUI.ViewProps {}
-
-internal struct PickerContentView: ExpoSwiftUI.View {
- @ObservedObject var props: PickerContentProps
-
- var body: some View {
- Children()
- }
-}
-
-internal struct PickerLabelView: ExpoSwiftUI.View {
- @ObservedObject var props: PickerLabelProps
-
- var body: some View {
- Children()
- }
-}
diff --git a/packages/expo-ui/ios/Picker/PickerView.swift b/packages/expo-ui/ios/Picker/PickerView.swift
index 8ee184687e64c8..4f551f74e77a25 100644
--- a/packages/expo-ui/ios/Picker/PickerView.swift
+++ b/packages/expo-ui/ios/Picker/PickerView.swift
@@ -24,13 +24,9 @@ internal struct PickerView: ExpoSwiftUI.View {
@ViewBuilder
private func makePicker() -> some View {
- let content = (props.children?
- .compactMap { $0.childView as? PickerContentView }
- .first)
+ let content = props.children?.slot("content")
- let labelContent = props.children?
- .compactMap { $0.childView as? PickerLabelView }
- .first
+ let labelContent = props.children?.slot("label")
if let systemImage = props.systemImage, let label = props.label {
Picker(label, systemImage: systemImage, selection: $selection) { content }
diff --git a/packages/expo-ui/ios/Popover/PopoverComponents.swift b/packages/expo-ui/ios/Popover/PopoverComponents.swift
deleted file mode 100644
index 268baac33d9ca4..00000000000000
--- a/packages/expo-ui/ios/Popover/PopoverComponents.swift
+++ /dev/null
@@ -1,18 +0,0 @@
-import ExpoModulesCore
-import SwiftUI
-
-internal struct PopoverViewContent: ExpoSwiftUI.View {
- @ObservedObject var props: PopoverViewContentPorps
-
- var body: some View {
- Children()
- }
-}
-
-internal struct PopoverViewPopContent: ExpoSwiftUI.View {
- @ObservedObject var props: PopoverViewPopContentPorps
-
- var body: some View {
- Children()
- }
-}
diff --git a/packages/expo-ui/ios/Popover/PopoverProps.swift b/packages/expo-ui/ios/Popover/PopoverProps.swift
index 448875ea00e404..17384b57f29cb4 100644
--- a/packages/expo-ui/ios/Popover/PopoverProps.swift
+++ b/packages/expo-ui/ios/Popover/PopoverProps.swift
@@ -8,10 +8,6 @@ internal class PopoverViewProps: UIBaseViewProps {
@Field var arrowEdge: PopoverArrowEdgeOption?
}
-internal final class PopoverViewContentPorps: ExpoSwiftUI.ViewProps {}
-
-internal final class PopoverViewPopContentPorps: ExpoSwiftUI.ViewProps {}
-
internal enum PopoverAttachmentAnchorOption: String, Enumerable {
case top
case center
diff --git a/packages/expo-ui/ios/Popover/PopoverView.swift b/packages/expo-ui/ios/Popover/PopoverView.swift
index 97ee7c5231cb26..14a74db3c655be 100644
--- a/packages/expo-ui/ios/Popover/PopoverView.swift
+++ b/packages/expo-ui/ios/Popover/PopoverView.swift
@@ -45,18 +45,14 @@ internal struct PopoverView: ExpoSwiftUI.View {
@ViewBuilder
private var triggerContent: some View {
- if let content = props.children?
- .compactMap({ $0.childView as? PopoverViewContent })
- .first {
+ if let content = props.children?.slot("trigger") {
content
}
}
@ViewBuilder
private var popoverContent: some View {
- if let content = props.children?
- .compactMap({ $0.childView as? PopoverViewPopContent })
- .first {
+ if let content = props.children?.slot("popover") {
content
}
}
diff --git a/packages/expo-ui/ios/SectionComponents.swift b/packages/expo-ui/ios/SectionComponents.swift
deleted file mode 100644
index a49918d9db1ef4..00000000000000
--- a/packages/expo-ui/ios/SectionComponents.swift
+++ /dev/null
@@ -1,34 +0,0 @@
-// Copyright 2025-present 650 Industries. All rights reserved.
-
-import SwiftUI
-import ExpoModulesCore
-
-internal final class SectionHeaderProps: ExpoSwiftUI.ViewProps {}
-
-internal final class SectionFooterProps: ExpoSwiftUI.ViewProps {}
-
-internal final class SectionContentProps: ExpoSwiftUI.ViewProps {}
-
-internal struct SectionHeader: ExpoSwiftUI.View {
- @ObservedObject var props: SectionHeaderProps
-
- var body: some View {
- Children()
- }
-}
-
-internal struct SectionFooter: ExpoSwiftUI.View {
- @ObservedObject var props: SectionFooterProps
-
- var body: some View {
- Children()
- }
-}
-
-internal struct SectionContent: ExpoSwiftUI.View {
- @ObservedObject var props: SectionContentProps
-
- var body: some View {
- Children()
- }
-}
diff --git a/packages/expo-ui/ios/SectionView.swift b/packages/expo-ui/ios/SectionView.swift
index d373f1fea13f27..3eacd882848500 100644
--- a/packages/expo-ui/ios/SectionView.swift
+++ b/packages/expo-ui/ios/SectionView.swift
@@ -95,21 +95,15 @@ internal struct SectionView: ExpoSwiftUI.View {
}
}
- private var contentChildren: SectionContent? {
- props.children?
- .compactMap({ $0.childView as? SectionContent })
- .first
+ private var contentChildren: SlotView? {
+ props.children?.slot("content")
}
- private var headerView: SectionHeader? {
- props.children?
- .compactMap({ $0.childView as? SectionHeader })
- .first
+ private var headerView: SlotView? {
+ props.children?.slot("header")
}
- private var footerView: SectionFooter? {
- props.children?
- .compactMap({ $0.childView as? SectionFooter })
- .first
+ private var footerView: SlotView? {
+ props.children?.slot("footer")
}
}
diff --git a/packages/expo-ui/ios/SliderView.swift b/packages/expo-ui/ios/SliderView.swift
index 4edcbf9b007833..8470fb4bf1bcb3 100644
--- a/packages/expo-ui/ios/SliderView.swift
+++ b/packages/expo-ui/ios/SliderView.swift
@@ -34,14 +34,9 @@ struct SliderView: ExpoSwiftUI.View {
#if !os(tvOS)
@ViewBuilder
private var sliderContent: some View {
- let label = props.children?.compactMap({ $0.childView as? SliderLabelView })
- .first(where: { $0.props.kind == .label })
- let minimumValueLabel = props.children?
- .compactMap({ $0.childView as? SliderLabelView })
- .first(where: { $0.props.kind == .minimum })
- let maximumValueLabel = props.children?
- .compactMap({ $0.childView as? SliderLabelView })
- .first(where: { $0.props.kind == .maximum })
+ let label = props.children?.slot("label")
+ let minimumValueLabel = props.children?.slot("minimum")
+ let maximumValueLabel = props.children?.slot("maximum")
if let min = props.min, let max = props.max, let step = props.step {
Slider(
@@ -98,20 +93,3 @@ final class SliderProps: UIBaseViewProps {
var onEditingChanged = EventDispatcher()
}
-internal enum SliderLabelKind: String, Enumerable {
- case label
- case minimum
- case maximum
-}
-
-internal final class SliderLabelProps: ExpoSwiftUI.ViewProps {
- @Field var kind: SliderLabelKind = .minimum
-}
-
-internal struct SliderLabelView: ExpoSwiftUI.View {
- @ObservedObject var props: SliderLabelProps
-
- var body: some View {
- Children()
- }
-}
diff --git a/packages/expo-ui/ios/SlotView.swift b/packages/expo-ui/ios/SlotView.swift
new file mode 100644
index 00000000000000..2fba7021914973
--- /dev/null
+++ b/packages/expo-ui/ios/SlotView.swift
@@ -0,0 +1,38 @@
+// Copyright 2025-present 650 Industries. All rights reserved.
+
+import SwiftUI
+import ExpoModulesCore
+
+internal final class SlotViewProps: ExpoSwiftUI.ViewProps {
+ @Field var name: String = ""
+}
+
+internal struct SlotView: ExpoSwiftUI.View {
+ @ObservedObject var props: SlotViewProps
+
+ init(props: SlotViewProps) {
+ self.props = props
+ }
+
+ var body: some View {
+ Children()
+ }
+}
+
+extension [any ExpoSwiftUI.AnyChild] {
+ func slot(_ name: String) -> SlotView? {
+ compactMap { $0.childView as? SlotView }
+ .first { $0.props.name == name }
+ }
+
+ func withoutSlot(_ name: String) -> [any ExpoSwiftUI.AnyChild] {
+ filter {
+ guard let slot = $0.childView as? SlotView else { return true }
+ return slot.props.name != name
+ }
+ }
+
+ func withoutSlots() -> [any ExpoSwiftUI.AnyChild] {
+ filter { !($0.childView is SlotView) }
+ }
+}
diff --git a/packages/expo-ui/src/swift-ui/ConfirmationDialog/index.tsx b/packages/expo-ui/src/swift-ui/ConfirmationDialog/index.tsx
index bdf15140234c7f..d91244cf2315d9 100644
--- a/packages/expo-ui/src/swift-ui/ConfirmationDialog/index.tsx
+++ b/packages/expo-ui/src/swift-ui/ConfirmationDialog/index.tsx
@@ -1,6 +1,7 @@
import { requireNativeView } from 'expo';
import { NativeSyntheticEvent } from 'react-native';
+import { Slot } from '../SlotView';
import { createViewModifierEventListener } from '../modifiers/utils';
import { type CommonViewModifierProps } from '../types';
@@ -39,34 +40,25 @@ type NativeConfirmationDialogProps = Omit =
requireNativeView('ExpoUI', 'ConfirmationDialogView');
-const ConfirmationDialogNativeTrigger: React.ComponentType<{ children: React.ReactNode }> =
- requireNativeView('ExpoUI', 'ConfirmationDialogTrigger');
-
-const ConfirmationDialogNativeActions: React.ComponentType<{ children: React.ReactNode }> =
- requireNativeView('ExpoUI', 'ConfirmationDialogActions');
-
-const ConfirmationDialogNativeMessage: React.ComponentType<{ children: React.ReactNode }> =
- requireNativeView('ExpoUI', 'ConfirmationDialogMessage');
-
/**
* The component visible all the time that triggers the confirmation dialog presentation.
*/
function Trigger(props: { children: React.ReactNode }) {
- return ;
+ return {props.children};
}
/**
* The action buttons displayed in the confirmation dialog. Use `Button` components from `@expo/ui/swift-ui` as children.
*/
function Actions(props: { children: React.ReactNode }) {
- return ;
+ return {props.children};
}
/**
* An optional message displayed below the title in the confirmation dialog.
*/
function Message(props: { children: React.ReactNode }) {
- return ;
+ return {props.children};
}
/**
diff --git a/packages/expo-ui/src/swift-ui/ContextMenu/index.tsx b/packages/expo-ui/src/swift-ui/ContextMenu/index.tsx
index 1c15d3d7ae7836..a967567c2e46e4 100644
--- a/packages/expo-ui/src/swift-ui/ContextMenu/index.tsx
+++ b/packages/expo-ui/src/swift-ui/ContextMenu/index.tsx
@@ -2,47 +2,33 @@ import { requireNativeView } from 'expo';
import { ComponentType } from 'react';
import { type ContextMenuProps } from './types';
+import { Slot } from '../SlotView';
export { type ContextMenuProps } from './types';
-const MenuNativeView: ComponentType = requireNativeView('ExpoUI', 'ContextMenu');
-
-const MenuNativeTriggerView: ComponentType