Conversation
# Why
ExpoWidgetsTarget is a Swift-only app extension. It transitively links
libExpoModulesCore.a and libExpoModulesJSI.a, both of which compile C++
(common/cpp/**/*.cpp, ios/JSI/**/*.{cpp,mm}) and therefore reference
std::*, __cxa_*, and the C++ EH personality __gxx_personality_v0.
Because the widget target itself contains zero C++/Obj-C++ sources,
Xcode picks clang as the link driver rather than clang++, and libc++ /
the C++ ABI are never implicitly linked → every C++ runtime symbol is
undefined.
# How
Adding -lc++ to ExpoModulesCore.podspec fixes this.
user_target_xcconfig = { 'OTHER_LDFLAGS' => '$(inherited) -lc++' }
instructs CocoaPods to merge -lc++ into the OTHER_LDFLAGS of every
aggregate xcconfig that consumes ExpoModulesCore — including
Pods-ExpoWidgetsTarget.{debug,release}.xcconfig.
# Test-plan
Build test-widgets project in both xcframework and source - now works.
# 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)
- [x] This diff will work correctly for `npx expo prebuild` & EAS Build
(eg: updated a module plugin).
---------
Co-authored-by: Expo Bot <34669131+expo-bot@users.noreply.github.com>
…#44732) # Why The `splash`, `ios.splash` / `android.splash`, and `jsEngine` accessors on `expo-manifests` are dead: no runtime consumer reads them anymore. Expo Go was the last caller, and it was actually rendering a splash screen that actually doesn't use any value from it (the app icon and name on a white background). # How **`expo-manifests`**: Removed `iosSplashBackgroundColor` / `iosSplashImageUrl` / `iosSplashImageResizeMode` / `getAndroidSplashInfo` / `getRootSplashInfo` / `jsEngine`, and the matching expectations in `EmbeddedManifestSpec` + `ExpoUpdatesManifestSpec`. **`@expo/cli`**: Trimmed the `getExpoSchema` mock in `resolveAssets-test.ts` to drop the legacy splash entries. **Expo Go (Android)**: `ManagedAppSplashScreenConfiguration` is now just `imageUrl` (from `manifest.getIconUrl()`) + `appName`. Ripped out every `resizeMode` reference (the enum, the manifest keys, the singleton/provider/listener parameter, the string-resource lookup, and `Constants.SPLASH_SCREEN_IMAGE_RESIZE_MODE`). `SplashScreenView` composable swaps Coil's `AsyncImage` + `crossfade` for `rememberAsyncImagePainter` and fades the whole `Column` in via an alpha bound to the painter state. **Expo Go (iOS)**: `ManagedAppSplashScreenConfiguration` is `appName` + `imageUrl` (from `manifest.iosAppIconUrl()`); `SplashScreenImageResizeMode` and its use in the builder are gone. `ManagedSplashscreenViewProvider` dropped the dead `splashImageView` property and two unused params, and `createImageView` now takes an `onLoad` callback that fades the stack view in from `alpha = 0`. # Test Plan - Expo Go (Android + iOS): splash shows app icon + name and fades in once the icon loads; no placeholder flash. - `expo-manifests`: iOS test specs pass. - `@expo/cli`: `resolveAssets-test.ts` passes. # 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)
# Why To verify compatible versions. # How Added `expo-widgets` with main version. # Test Plan <!-- Please describe how you tested this change and how a reviewer could reproduce your test, especially if this PR does not include automated tests! If possible, please also provide terminal output and/or screenshots demonstrating your test/reproduction. --> # Checklist <!-- Please check the appropriate items below if they apply to your diff. --> - [ ] 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)
#44993) … as a public header # Why `et prebuild expo-modules-core` was failing at the SPM explicit-PCM step because `SwiftUIVirtualViewSharedImpl+Private.h` was leaking into the public ExpoModulesCore module umbrella. That file is not actually a standalone header — it's a code fragment that `SwiftUIVirtualViewObjC.mm` and `SwiftUIVirtualViewObjCDev.mm` `#include` inside their `@implementation` blocks (introduced in #44118 when the view was split into dev/release variants). When clang tried to compile the fragment as a standalone submodule header during PCM generation, it hit parse errors on the top-level method definitions and `react::` types, which only make sense inside an `@implementation` context. The visible diagnostics looked like `RCTBridgeModule` warnings, but the real fatal error was the fragment parse. CocoaPods builds never tripped on this because pods don't emit an umbrella-PCM over the public header tree — that's why the regression wasn't caught at PR review time. # How Fix: add a `fileMapping` entry in `packages/expo-modules-core/spm.config.json` that routes the fragment into the target source tree next to its including `.mm` files. The SPM generator skips files listed in `fileMapping` when populating the public `include/` umbrella, so the fragment no longer leaks in, and the relative `#include` still resolves because the file is symlinked alongside the `.mm` sources in the staging dir. No rename, no IDE-ergonomics regression on the `.h` file. Added a regression test at `tools/src/prebuilds/SPMGenerator.headerStaging.test.ts` that asserts no `@implementation` fragment leaks into the public include dir for the `ExpoModulesCore_ios_objc` target — would have caught this at PR time. # Test-plan Verified: `et prebuild expo-modules-core -f Debug --clean` now succeeds on both the ios-arm64 and ios-arm64_x86_64-simulator slices. # 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) - [x] This diff will work correctly for `npx expo prebuild` & EAS Build (eg: updated a module plugin). --------- Co-authored-by: Expo Bot <34669131+expo-bot@users.noreply.github.com>
# Why It is currently not possible to determine whether the currently playing media is HDR. # How Add `videoRange` field to `VideoTrack`, which contains information about HDR content. # Test Plan Tested on iOS and Android with a Dolby Vision HLS stream (iOS) and a locally recorded HDR video (Android)
…#44346) # Why `getLiveActivityUrl` in `Utils.swift` uses `WidgetsStorage.getData(forKey:)` to read the Live Activity URL, but the URL is stored as a `String` via `WidgetsStorage.set(_:forKey:)` in `LiveActivityFactory.swift`. `UserDefaults.data(forKey:)` returns `nil` for keys stored as strings, so the widget URL is always `nil` and the Dynamic Island's `.widgetURL()` never navigates anywhere. ## How to reproduce 1. Start a Live Activity with a URL: ```ts import { startActivity } from 'expo-widgets'; startActivity('MyActivity', { title: 'Hello' }, { url: 'myapp://details/123' }); ``` 2. Tap the Dynamic Island or expanded Live Activity 3. Nothing happens — the app does not navigate to the deep link URL # How Changed `getLiveActivityUrl` to use `WidgetsStorage.getString(forKey:)` instead of `getData(forKey:)`, matching how the URL is stored. ```diff func getLiveActivityUrl(forName name: String) -> URL? { - let data = WidgetsStorage.getData(forKey: "__expo_widgets_live_activity_\(name)_url") - if let data, let url = String(data: data, encoding: .utf8) { - return URL(string: url) + guard let urlString = WidgetsStorage.getString(forKey: "__expo_widgets_live_activity_\(name)_url") else { + return nil } - return nil + return URL(string: urlString) } ``` # Test Plan - Start a Live Activity with a URL via `startActivity(name, props, { url })` - Tap the Dynamic Island / expanded Live Activity - Verify the app opens to the specified deep link URL # 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) - [x] 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) Co-authored-by: Jakub Grzywacz <kontakt@jakubgrzywacz.pl>
To make sure we have smoke tested the prebuilds we run a short precompile of expo-modules-core. If any other packages have changes - they will also be rebuilt. Will be run for changes in the packages directory and the tools/prebuild.
# Why Fixes #44757 After entering PiP from a fullscreen video, on iOS, the player will return to the non-fullscreen video view after exiting PiP. # How Add three options of methods of returning to fullscreen from pip - depending on usage you may want something else - on Android we don't need this since PiP can't be used within the app there. - `'always'`: Always re-enter fullscreen when PiP stops. - `'autoEnter'`: Re-enter fullscreen only when PiP was started automatically by the app going to the background. (default) - `'never'`: Do not re-enter fullscreen when PiP stops. Adding this required a bit of work - when entering PiP `AVPlayer` will always exit fullscreen - but we can use `restoreUserInterfaceForPictureInPictureStopWithCompletionHandler` to re-enter Fullscreen before exiting PiP. # Test Plan Tested on iPhone 13 running iOS 18
# Why
It's useful in widgets (but not exclusively).
# How
Added `containerBackground` modifier.
# Test Plan
```tsx
import { Text, VStack } from '@expo/ui/swift-ui';
import { containerBackground } from '@expo/ui/swift-ui/modifiers';
import { createWidget, WidgetEnvironment } from 'expo-widgets';
type MyWidgetProps = {
emoji: string;
};
const MyWidget = (props: MyWidgetProps, env: WidgetEnvironment) => {
'widget';
return (
<VStack>
<Text>Time:</Text>
<Text>{env.date.toLocaleTimeString().split(' ')[0].split(':').slice(0, 2).join(':')}</Text>
<Text modifiers={[containerBackground('#ff0000', 'widget')]}>Emoji:</Text>
<Text>{props.emoji}</Text>
</VStack>
);
};
export default createWidget('Widget2', MyWidget);
```
<img width="300"
alt="SimShot_iPhone_17_Pro_with_Apple_Watch_2026-03-23_22-41-31"
src="https://github.com/user-attachments/assets/8f18ba1a-3c94-470a-b827-981feaee9e9b"
/>
# 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)
- [x] 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)
## Why
The `detect` step of the iOS XCFramework prebuild smoke test was failing
on PRs that touched no eligible packages. EAS's `set-output` helper
rejects an empty VALUE and exits 2:
```
Resolved packages: <none — skipping smoke test>
Usage: set-output NAME VALUE
... exited with non-zero code: 2
```
That turned the intended "nothing to smoke-test → skip" path into a hard
failure for every PR matching the workflow's `paths:` filters without
resolving packages.
## How
- `scripts/detect-prebuild-packages.sh`: when `$PKGS` is empty, emit the
sentinel `none` via `set-output packages "${PKGS:-none}"` instead of an
empty string.
- `.eas/workflows/ios-prebuild-xcframeworks-smoke.yml`: switch the three
gated steps from `packages != ''` to `packages != 'none'` so they still
skip in the no-packages case.
Net result: the job reports success with the three real-work steps
skipped (visible in the EAS UI), matching the comment at the top of the
script.
## Test plan
- [ ] This PR itself touches the script + workflow, so the `detect` step
should resolve to `expo-modules-core` (via the tools/workflow-changed
branch) and run the smoke test end-to-end.
- [ ] Confirm on a separate PR that touches no eligible packages, the
job goes green with `use_npm_token`, `install_node_modules`, and
`Prebuild XCFrameworks` marked skipped.
…for updates bandwidth estimates (#44946) Co-authored-by: Aman Mittal <amandeepmittal@live.com>
) # Why Adds real-time access to the microphone's PCM stream on iOS so apps can feed audio into speech recognition, custom VAD, streaming transcription, waveform visualizers, or any pipeline that needs samples as they're captured, not after a recording finishes. # How Introduces a new `AudioStream` `SharedObject` - Uses `AVAudioEngine` with a tap on `inputNode`. - Buffers are emitted as `audioStreamBuffer` events carrying a `NativeArrayBuffer` - `audioStreamStatus` events # Test Plan Bare expo
…44901) # Why Exposes real-time PCM stream on Android so consumers can feed the microphone into speech recognition, custom VAD, streaming transcription, or waveform UIs. # How Adds `AudioStream` `SharedObject` - Uses `AudioRecord` with `MediaRecorder.AudioSource.MIC`. - Events (`audioStreamBuffer`, `audioStreamStatus`) Ran into an issue with emitting `NativeArrayBuffer` from a sharedObject. The default converter doesn't know how to handle a `NativeArrayBuffer`, so emitted buffers arrive as `null`. Passing `useExperimentalConverter = true` routes the call through the path that handles `NativeArrayBuffer` correctly. cc @lukmccall # Test Plan Bare expo
# Why Adds the JS api for the audio stream feature and a demo screen. # How Adds `useAudioStream()` hook and supporting types # Test Plan Bare expo
# Why I noticed this TS error when wrapping Sentry + Observe <img width="1252" height="303" alt="Screenshot 2026-04-22 at 11 58 03" src="https://github.com/user-attachments/assets/7e2c916e-478e-4553-b794-ce55ed5b8b31" /> When `AppMetricsRoot.wrap` is called with a zero-prop component (e.g. an Expo Router `RootLayout`), TypeScript has no inference candidate for `P` and falls back to the generic's constraint, so `P` resolves to `object` and the return type becomes `ComponentType<object>`. That collapsed type does not compose with other HOCs such as `Sentry.wrap`, which expect `ComponentType<Record<string, unknown>>`, because `object` gives no guarantee about string-keyed access. <!-- Please describe the motivation for this PR, and link to relevant GitHub issues, forums posts, or feature requests. --> # How Adding a default of `Record<string, unknown>` (`P extends object = Record<string, unknown>`). This only changes the fallback when inference has no candidate, leaving prop-ful callers untouched, and makes zero-prop components resolve to `ComponentType<Record<string, unknown>>`, which composes cleanly with any HOC. <!-- How did you build this feature or fix this bug and why? --> # Test Plan Verified locally that the TS error went away. <!-- Please describe how you tested this change and how a reviewer could reproduce your test, especially if this PR does not include automated tests! If possible, please also provide terminal output and/or screenshots demonstrating your test/reproduction. --> # Checklist <!-- Please check the appropriate items below if they apply to your diff. --> - [ ] 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)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to subscribe to this conversation on GitHub.
Already have an account?
Sign in.
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
See Commits and Changes for more details.
Created by
pull[bot] (v2.0.0-alpha.4)
Can you help keep this open source service alive? 💖 Please sponsor : )