[ios][ui] Add defaultScrollAnchor modifier#43914
Merged
intergalacticspacehighway merged 1 commit intoexpo:mainfrom Mar 13, 2026
Merged
[ios][ui] Add defaultScrollAnchor modifier#43914intergalacticspacehighway merged 1 commit intoexpo:mainfrom
intergalacticspacehighway merged 1 commit intoexpo:mainfrom
Conversation
Contributor
|
Subscribed to pull request
Generated by CodeMention |
8779219 to
9e5b0ac
Compare
e423048 to
4a6c07d
Compare
intergalacticspacehighway
approved these changes
Mar 13, 2026
Contributor
intergalacticspacehighway
left a comment
There was a problem hiding this comment.
Thanks!
intergalacticspacehighway
pushed a commit
that referenced
this pull request
Mar 13, 2026
Exposes SwiftUI's `.defaultScrollAnchor(_:)` (iOS 17+) as a modifier.
This lets you control where a ScrollView or List starts its scroll
position without any geometric transform hacks.
```tsx
<ScrollView modifiers={[defaultScrollAnchor('bottom')]}>
{messages.map(msg => <Text key={msg.id}>{msg.text}</Text>)}
</ScrollView>
```
The scroll view starts at the bottom. Scroll up to see older content. No
`scaleEffect(y: -1)` flip, no reversed data, no inverted scroll
indicators.
Reuses the existing `UnitPointOptions` enum and its `toUnitPoint`
converter. Accepts any `UnitPoint` preset: `top`, `bottom`, `center`,
`leading`, `trailing`, `topLeading`, `topTrailing`, `bottomLeading`,
`bottomTrailing`, `zero`.
Falls back to a no-op on iOS < 17.
Same pattern as `scrollDismissesKeyboard`, `scrollDisabled`,
`scrollContentBackground`.
## Test plan
- Built ExpoUI target against iOS 26.2 SDK (Xcode 26, iPhone 17 Pro
sim). `BUILD SUCCEEDED`, zero errors.
- Added a test screen to `native-component-list` with a 50-item
`ScrollView` + `defaultScrollAnchor('bottom')`. Confirmed the view loads
scrolled to "Message 50" at the bottom. Scroll indicators point the
right way.
- `yarn build` passes.
- `yarn lint --fix` passes.
intergalacticspacehighway
pushed a commit
that referenced
this pull request
Mar 13, 2026
Exposes SwiftUI's `.defaultScrollAnchor(_:for:)` (iOS 18+) as a
modifier. This is the two-parameter overload that lets you set different
scroll anchors for different roles: where the scroll view starts, how it
handles content size changes, and how it aligns content smaller than its
container.
```tsx
<ScrollView modifiers={[
defaultScrollAnchor('bottom'),
defaultScrollAnchorForRole('top', 'initialOffset'),
]}>
{messages.map(msg => <Text key={msg.id}>{msg.text}</Text>)}
</ScrollView>
```
The scroll view anchors to bottom globally, but the `initialOffset` role
override makes it start at the top. Each role can be independently
controlled.
`ScrollAnchorRole` has three cases: `initialOffset` (where to start),
`sizeChanges` (how to reposition on resize), `alignment` (how to align
content smaller than the container). Passing `null` as the anchor opts
out of a specific role.
Also improves `defaultScrollAnchor` from #43914:
- Adds `null` support to match Apple's `UnitPoint?` signature (allows
resetting/opting out)
- Adds `@platform macos 14.0+` to JSDoc (was missing, Swift guard
already covered it)
- Extracts shared `UnitPointValue` type used by both modifiers
Reuses the existing `UnitPointOptions` enum. Adds a new
`ScrollAnchorRoleOptions` enum. Falls back to a no-op on iOS < 18.
Same pattern as `defaultScrollAnchor` from #43914.
## Test plan
- Built and ran on iPhone 17 Pro simulator (iOS 26.2, Xcode 26).
- Tested `defaultScrollAnchor('bottom')` - scroll view starts at bottom.
- Tested `defaultScrollAnchorForRole('top', 'initialOffset')` overriding
`defaultScrollAnchor('bottom')` - scroll view starts at top, confirming
per-role override works.
- Tested `defaultScrollAnchor(null)` - no-op, default top behavior.
- `yarn build` passes.
- `yarn tsc --noEmit` passes.
- `yarn lint --fix` passes.
- `yarn test` passes (30/30).
- Docs regenerated for `unversioned` and `v55.0.0`.
intergalacticspacehighway
pushed a commit
that referenced
this pull request
Mar 13, 2026
Exposes SwiftUI's `.defaultScrollAnchor(_:for:)` (iOS 18+) as a
modifier. This is the two-parameter overload that lets you set different
scroll anchors for different roles: where the scroll view starts, how it
handles content size changes, and how it aligns content smaller than its
container.
```tsx
<ScrollView modifiers={[
defaultScrollAnchor('bottom'),
defaultScrollAnchorForRole('top', 'initialOffset'),
]}>
{messages.map(msg => <Text key={msg.id}>{msg.text}</Text>)}
</ScrollView>
```
The scroll view anchors to bottom globally, but the `initialOffset` role
override makes it start at the top. Each role can be independently
controlled.
`ScrollAnchorRole` has three cases: `initialOffset` (where to start),
`sizeChanges` (how to reposition on resize), `alignment` (how to align
content smaller than the container). Passing `null` as the anchor opts
out of a specific role.
Also improves `defaultScrollAnchor` from #43914:
- Adds `null` support to match Apple's `UnitPoint?` signature (allows
resetting/opting out)
- Adds `@platform macos 14.0+` to JSDoc (was missing, Swift guard
already covered it)
- Extracts shared `UnitPointValue` type used by both modifiers
Reuses the existing `UnitPointOptions` enum. Adds a new
`ScrollAnchorRoleOptions` enum. Falls back to a no-op on iOS < 18.
Same pattern as `defaultScrollAnchor` from #43914.
## Test plan
- Built and ran on iPhone 17 Pro simulator (iOS 26.2, Xcode 26).
- Tested `defaultScrollAnchor('bottom')` - scroll view starts at bottom.
- Tested `defaultScrollAnchorForRole('top', 'initialOffset')` overriding
`defaultScrollAnchor('bottom')` - scroll view starts at top, confirming
per-role override works.
- Tested `defaultScrollAnchor(null)` - no-op, default top behavior.
- `yarn build` passes.
- `yarn tsc --noEmit` passes.
- `yarn lint --fix` passes.
- `yarn test` passes (30/30).
- Docs regenerated for `unversioned` and `v55.0.0`.
kristentr
added a commit
to kristentr/expo
that referenced
this pull request
Mar 19, 2026
* Create swift.yml (#24) * Create sonarcloud.yml (#25) * Create codeql.yml * Potential fix for code scanning alert no. 43: Workflow does not contain permissions Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> * [notifications]: handle Keychain unavailability during iOS background launch (expo#43829) # Why Fixes expo#43828 When iOS launches an app in the background (e.g. push notification delivery with the device locked), `getRegistrationInfoAsync` throws an unhandled promise rejection because: 1. The module-level `.then()` in `DevicePushTokenAutoRegistration.fx.ts` has no rejection handler 2. Registration info is stored with `kSecAttrAccessibleWhenUnlockedThisDeviceOnly`, which is inaccessible when the device is locked This causes `ERR_NOTIFICATIONS_KEYCHAIN_ACCESS` errors that surface as unhandled rejections in production (observed via Sentry across multiple users/devices). ## How **TypeScript** (`DevicePushTokenAutoRegistration.fx.ts`): - Add a rejection handler to the startup `.then(getRegistrationInfoAsync)` call, matching the `console.warn` pattern used by every other async path in the same file **Swift** (`ServerRegistrationModule.swift`): - Change `registrationSetQuery` accessibility from `kSecAttrAccessibleWhenUnlockedThisDeviceOnly` to `kSecAttrAccessibleAfterFirstUnlockThisDeviceOnly` - This matches what `installationIdSetQuery` already uses (same file, line 82) - Registration info is just `{"isEnabled":true}` — non-sensitive data that Apple recommends storing with `AfterFirstUnlock` for background-accessible items - Safe for existing users: the write path already does `SecItemDelete` + `SecItemAdd`, so the next successful write naturally uses the new protection class **Test** (`DevicePushTokenAutoRegistration-test.ts`): - Add test that mocks `getRegistrationInfoAsync` to reject and verifies the rejection is handled gracefully (no unhandled rejection, `console.warn` is called) ## Test Plan - [x] `yarn test` in `packages/expo-notifications` — all 46 tests pass - [x] `yarn lint` in `packages/expo-notifications` — clean - [x] New test verifies the rejection handler fires when `getRegistrationInfoAsync` rejects with a Keychain error --------- Co-authored-by: Vojtech Novak <[email protected]> * [babel-preset-expo] Fix test expectations for Hermes V1 class preservation (expo#43837) * [metro-config] update isHermesV1 detection (expo#43841) * [gl] Make msaaSamples and enableExperimentalWorkletSupport optional in GLViewProps (expo#43767) * [expo-ui][android] add AnimatedVisibility transition API (expo#43632) # Why To expose the entry/exit animation configuration to the JS side. **Note** that with the animations affecting layout (`expand`/`shrink`), it's better to leave the layout to compose. If `Host` with `matchesContent` is used, then the layout will jump, as there are no synchronous events in react native android yet. # How This PR adds an API similar to SwiftUI approach: - builder pattern for both entry and exit animations - passes them down as an array of configuration objects # Test Plan https://github.com/user-attachments/assets/112cf147-6d9e-4ea4-962b-4d30719f59aa # 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) * [docs] Update EAS CLI reference for 18.3.0 (expo#43875) # Why <!-- Please describe the motivation for this PR, and link to relevant GitHub issues, forums posts, or feature requests. --> Update EAS CLI reference for 18.3.0. # How <!-- How did you build this feature or fix this bug and why? --> - Run `yarn run eas-cli-sync` to sync to the latest changes and update the CLI version - Add normalization to parse broken external links in the script. (External Apple developers documentation link was broken after the sync). # 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. --> See preview. # 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) * [docs] Update manual install diffs for React Native 0.83 / SDK 55 (expo#43865) # Why <!-- Please describe the motivation for this PR, and link to relevant GitHub issues, forums posts, or feature requests. --> Fix ENG-18812 # How - Update `expo-android.diff` to use `ExpoReactHostFactory.getDefaultReactHost()` instead of the old `ReactNativeHostWrapper` pattern, matching what `install-expo-modules@latest` produces for RN 0.83. - Update `expo-ios.diff` to reflect RN 0.83's new `AppDelegate.swift` structure: `ExpoAppDelegate`, `ExpoReactNativeFactory`, `ExpoReactNativeFactoryDelegate`, and `internal import Expo`. - Bump version reference in `installing-expo-modules.mdx` from RN 0.81 to RN 0.83. - Update the Expo template GitHub link from `sdk-54` to `sdk-55`. # Test Plan - Create a fresh RN 0.83 project with `npx @react-native-community/cli@latest init myapp --version 0.83.0`. - Apply the diffs shown on the Install Expo modules page manually and verify the project builds on both Android and iOS. - Compare the manual diff output against running `npx install-expo-modules@latest` on a clean RN 0.83 project to confirm they match. For docs changes, see Preview: https://pr-43865.expo-docs.pages.dev/bare/installing-expo-modules/ # 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). - [x] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) * Bump react-native-maps to 1.27.2 (expo#43884) # Why Closes expo#42423 # How Bump react-native-maps to 1.27.2 # Test Plan Run NCL locally # 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) * [expo-ui][android] add `imePadding` modifier (expo#43652) # Why In order to show content above keyboard # How Expose `imePadding` modifier # Test Plan Bare expo https://github.com/user-attachments/assets/eab7b239-bd06-4cbd-a387-6dd1354046e4 # Checklist <!-- Please check the appropriate items below if they apply to your diff. --> - [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) * [create-expo-nightly][widgets] fix nightly tests (expo#43849) * [expo-go] Copy dev menu UI (expo#43686) # Why We want expo go to own it's own implementation of the dev menu. This makes it easier to make changes without impacting expo-dev-menu # How Copy over dev menus ui and integrate with the kernel # Test Plan Expo go * [expo-go][menu] Remove bridge dependency (expo#43687) # Why `currentBridge` is always nil on RN 0.84 so we need to remove references to it # How We can get everything we need from the manifest and the react host on the kernel # Test Plan Expo go * [2/n] Upgrade React Native to 0.85.0-rc.1 (expo#43817) # Why Align with latest React Native core version # How - Update gradle to 9.3.1 + fix `org.jetbrains:annotations` versions mismatch - Update package versions - `react-native 0.85-rc.0 -> 0.85-rc.1` - `@react-native/normalize-colors 0.85-rc.0 -> 0.85-rc.1` - `@react-native/babel-preset 0.85-rc.0 -> 0.85-rc.1` - `@react-native/dev-middleware 0.85-rc.0 -> 0.85-rc.1` # Test Plan - Bare Expo - Minimal tester # 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) * [expo-go] Add new server discovery (expo#43689) # Why Brings the same server discovery from expo-dev-launcher to expo go # Test Plan Expo go * fix(cli): avoid TTY init crash in non-interactive terminals (expo#43796) * [ui][android] - Match Chip with Compose API (expo#43900) # Why Match JS Chip API with Compose API. The old API used a single `Chip` component with a `variant` prop, which doesn't map cleanly to Material 3's distinct chip types that each have different slots, props, and behaviors. <!-- Please describe the motivation for this PR, and link to relevant GitHub issues, forums posts, or feature requests. --> # How - Split the single `Chip` component into four separate components: `AssistChip`, `FilterChip`, `InputChip`, and `SuggestionChip`, matching M3's chip taxonomy. - Replaced flat props (`label`, `icon`) with slot-based composition (`<Chip.Label>`, `<Chip.LeadingIcon>`, `<Chip.TrailingIcon>`, `<InputChip.Avatar>`). - Added `colors`, `elevation`, and `border` props to all chip types - Merged the standalone `FilterChip` module into the unified `Chip` module - Renamed `onPress` to `onClick` in the user API - Updated docs for all three versions with usage examples and chip type descriptions. <!-- How did you build this feature or fix this bug and why? --> # Test Plan Test Chip examples and docs <!-- 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) * fix(expo-updates): fix iOS build script failing silently on paths with spaces (expo#43497) Fixes expo#43500 ## Why When the Xcode project root lives in a path containing **spaces** (e.g. a USB volume mounted as `/Volumes/My Drive/MyApp`), the iOS build script `create-updates-resources-ios.sh` silently exits early and never generates `app.manifest`. ### Root cause ```bash # Before – unquoted, triggers word-splitting in bash PROJECT_DIR_BASENAME=$(basename $PROJECT_DIR) if [ "x$PROJECT_DIR_BASENAME" != "xPods" ]; then exit 0 # ← always taken when PROJECT_DIR contains spaces fi ``` `basename /Volumes/My Drive/App/ios/Pods` splits on the space, producing a multi-word token (e.g. `Drive/App/ios/Pods` as a separate word). The `[ … != xPods ]` check then always succeeds, causing the script to `exit 0` before generating any resources. The result is that `EXUpdates.bundle` is built with only `Info.plist` and no `app.manifest`. The app then crashes on launch inside `EmbeddedAppLoader.cachedEmbeddedManifest` with: ``` Assertion failed: The embedded update is invalid … ``` ### Fixes 1. **Quote `$PROJECT_DIR`** in the `basename` call so word-splitting cannot occur: ```bash PROJECT_DIR_BASENAME=$(basename "$PROJECT_DIR") ``` 2. **`mkdir -p "$RESOURCE_DEST"`** for the `shallow` bundle format branch. The `deep` branch already had this; without it a clean build writes `app.manifest` into a directory that doesn't exist yet, producing an `ENOENT` from `createUpdatesResources.js`. ## Test plan - Build an iOS archive with the project root on a volume/directory whose path contains spaces. - Verify `EXUpdates.bundle/app.manifest` is present in the built `.app`. - Confirm the app launches without crashing. * docs(mcp): update tools table to match current server capabilities Remove generate_claude_md and generate_agents_md (moved to plugin-based installation), add read_documentation, testflight_crashes, and testflight_feedback. Add builds/workflows section to intro examples. * fix(cli): Remove -quiet flag from Xcode build args (expo#43906) Revert the previous addition of the -quiet argument to xcodebuild so build environment variables and full output are always printed. Remove '-quiet' from getXcodeBuildArgsAsync, update unit tests to no longer expect the flag, and add a changelog entry noting the revert. --------- Co-authored-by: Expo Bot <[email protected]> * [ios][ui] Add defaultScrollAnchor modifier (expo#43914) Exposes SwiftUI's `.defaultScrollAnchor(_:)` (iOS 17+) as a modifier. This lets you control where a ScrollView or List starts its scroll position without any geometric transform hacks. ```tsx <ScrollView modifiers={[defaultScrollAnchor('bottom')]}> {messages.map(msg => <Text key={msg.id}>{msg.text}</Text>)} </ScrollView> ``` The scroll view starts at the bottom. Scroll up to see older content. No `scaleEffect(y: -1)` flip, no reversed data, no inverted scroll indicators. Reuses the existing `UnitPointOptions` enum and its `toUnitPoint` converter. Accepts any `UnitPoint` preset: `top`, `bottom`, `center`, `leading`, `trailing`, `topLeading`, `topTrailing`, `bottomLeading`, `bottomTrailing`, `zero`. Falls back to a no-op on iOS < 17. Same pattern as `scrollDismissesKeyboard`, `scrollDisabled`, `scrollContentBackground`. ## Test plan - Built ExpoUI target against iOS 26.2 SDK (Xcode 26, iPhone 17 Pro sim). `BUILD SUCCEEDED`, zero errors. - Added a test screen to `native-component-list` with a 50-item `ScrollView` + `defaultScrollAnchor('bottom')`. Confirmed the view loads scrolled to "Message 50" at the bottom. Scroll indicators point the right way. - `yarn build` passes. - `yarn lint --fix` passes. * fix(task-manager): Keep JS timers alive during background task execution on Android (expo#43821) # Why On Android, background task JS execution hangs indefinitely when the app is backgrounded. All async operations (promises, `setTimeout`, etc.) never resolve, causing background tasks to time out without completing any work. This is because React Native's `JavaTimerManager` deliberately stops processing JS timers when the Activity is paused (`isPaused=true`) **unless** `HeadlessJsTaskContext` reports active headless tasks (`isRunningTasks=true`). The guard at [`JavaTimerManager.kt:291`](https://github.com/facebook/react-native/blob/main/packages/react-native/ReactAndroid/src/main/java/com/facebook/react/modules/core/JavaTimerManager.kt#L291): ```kotlin if (isPaused.get() && !isRunningTasks.get()) { return // timers never fire } ``` `expo-task-manager`'s `TaskService` dispatches task events to JS but never registers with `HeadlessJsTaskContext`. So `isRunningTasks` stays `false`, and all JS timers (which back promises, `setTimeout`, etc.) are paused. This affects any package built on `expo-task-manager`, including `expo-background-task`. # How In `TaskService.executeTask()`, when the first event is dispatched for an `appScopeKey`, we call `HeadlessJsTaskContext.startTask()` to register an active headless task. This signals to `JavaTimerManager` that JS timers should keep firing. When the last event for that `appScopeKey` completes in `notifyTaskFinished()`, we call `finishTask()` to clean up. On the JS side, a no-op headless task is registered via `AppRegistry.registerHeadlessTask('expo-task-manager', ...)` to suppress the warning that `startHeadlessTask` logs when no JS handler is found. ### Event tracking bug fix This PR also fixes a pre-existing bug in `TaskService.executeTask()` where subsequent events for the same `appScopeKey` were not being tracked in `sEvents`. The original code created a new disconnected `ArrayList` in the `else` branch without adding it back to the map: ```java // Before (bug): } else { appEvents = new ArrayList<>(); // new list, never stored in sEvents appEvents.add(eventId); // eventId is lost } // After (fix): } else { appEvents = sEvents.get(appScopeKey); // get the existing tracked list appEvents.add(eventId); // eventId is properly tracked } ``` This meant only the first event per `appScopeKey` was tracked, so `notifyTaskFinished()` could not properly remove subsequent event IDs. This fix is needed for the headless task lifecycle to work correctly when multiple tasks run concurrently. # Test Plan The background task example app (`native-component-list`) includes timer tick logs that verify JS timers work while backgrounded. A `DebugBgTaskReceiver` is included in `bare-expo` to trigger tasks via ADB. 1. Build bare-expo: `cd apps/bare-expo && npx expo run:android` 2. Open the app, navigate to Background Task screen, tap **Schedule Background Task** 3. Background the app (press Home) 4. Trigger via ADB: ```bash adb shell am broadcast -a dev.expo.payments.DEBUG_BG_TASK -n dev.expo.payments/.DebugBgTaskReceiver ``` 5. Watch logcat: ```bash adb logcat -s ReactNativeJS DebugBgTaskReceiver TaskService ``` **Without fix:** Only `TASK RUNNING` and `tick 1/5` appear (before first `setTimeout`). Subsequent ticks never fire. **With fix:** All 5 ticks appear at 2s intervals, followed by `all ticks completed, saving timestamp`. # 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) * [expo-router] fix zoom transition to prefetched routes (expo#43852) # Why When screen was preloaded the zoom transition was flaky. The reason for this is: 1. When screen was preloaded, it was added on the native side, but `ZoomTransitionEnabler` was not mounted 2. When the navigation happened from JS side, `ZoomTransitionEnabler` was mounted, but because the screen was preloaded it happened only after the transition started - no enter zoom transition This was timing issue and was not happening in all cases. There is another similar issue regarding timing, when `ZoomTransitionEnabler` is mounted too late when there is a lot of processing involved. I want to solve it in the follow-up PR, by prefetching the zoomed page, before the navigation. # How Add `INTERNAL_EXPO_ROUTER_ZOOM_TRANSITION_SCREEN_ID_PARAM_NAME` param in the `PRELOAD` stack operation similar to navigation operation. This will mount the `ZoomTransitionEnabler` in case of prefetching as well. # Test Plan 1. Unit tests 2. Manual testing in link-preview app with prefetch enabled # 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) * [ui] add date and time picker dialogs, extra props (expo#43895) # Why Add `DatePickerDialog` and `TimePickerDialog` components to `expo-ui` on Android, and support `selectableDates` to constrain the date range. Also refactors color-building logic into shared helpers. # How - Added `DatePickerDialogView` and `TimePickerDialogView` native Compose views backed by Material 3 `DatePickerDialog` and `AlertDialog`. - Introduced `selectableDates` prop (`{ start?: Date; end?: Date }`) for both inline and dialog pickers. - Extracted `buildDatePickerColors` / `buildTimePickerColors` helpers to deduplicate color overrides. - Exported `DatePickerDialog`, `TimePickerDialog` components and types from the JS layer with Date-to-timestamp conversion. - Added NCL examples for both dialogs and selectable dates. # Test Plan 1. Open the DateTimePicker screen in NCL on Android. 2. Tap "Show Date Dialog" — verify the dialog opens with selectable dates constrained to -5d…+30d, confirm/dismiss work. 3. Tap "Show Time Dialog" — verify 24h time picker dialog, confirm returns selected time, dismiss fires alert. 4. Scroll to the "With selectableDates" section — verify dates outside the range are greyed out. 5. Verify the existing inline date/time pickers still function as before. # 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) * [device][ios] Update Apple devices (expo#43644) # Why newer Apple devices were missing, so `Device.modelName` was not behaving as expected for these models # How synced with the latest Xcode 26.3 database # 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) * [widgets] Pass environment to AppIntent (expo#43925) # Why It's first step to remove `target` requirement on `Button`. # How Pass stringified JSON widget environment to `AppIntent` # Test Plan Inspect environment in AppIntent execution. * [ios][ui] Add defaultScrollAnchorForRole modifier (expo#43923) Exposes SwiftUI's `.defaultScrollAnchor(_:for:)` (iOS 18+) as a modifier. This is the two-parameter overload that lets you set different scroll anchors for different roles: where the scroll view starts, how it handles content size changes, and how it aligns content smaller than its container. ```tsx <ScrollView modifiers={[ defaultScrollAnchor('bottom'), defaultScrollAnchorForRole('top', 'initialOffset'), ]}> {messages.map(msg => <Text key={msg.id}>{msg.text}</Text>)} </ScrollView> ``` The scroll view anchors to bottom globally, but the `initialOffset` role override makes it start at the top. Each role can be independently controlled. `ScrollAnchorRole` has three cases: `initialOffset` (where to start), `sizeChanges` (how to reposition on resize), `alignment` (how to align content smaller than the container). Passing `null` as the anchor opts out of a specific role. Also improves `defaultScrollAnchor` from expo#43914: - Adds `null` support to match Apple's `UnitPoint?` signature (allows resetting/opting out) - Adds `@platform macos 14.0+` to JSDoc (was missing, Swift guard already covered it) - Extracts shared `UnitPointValue` type used by both modifiers Reuses the existing `UnitPointOptions` enum. Adds a new `ScrollAnchorRoleOptions` enum. Falls back to a no-op on iOS < 18. Same pattern as `defaultScrollAnchor` from expo#43914. ## Test plan - Built and ran on iPhone 17 Pro simulator (iOS 26.2, Xcode 26). - Tested `defaultScrollAnchor('bottom')` - scroll view starts at bottom. - Tested `defaultScrollAnchorForRole('top', 'initialOffset')` overriding `defaultScrollAnchor('bottom')` - scroll view starts at top, confirming per-role override works. - Tested `defaultScrollAnchor(null)` - no-op, default top behavior. - `yarn build` passes. - `yarn tsc --noEmit` passes. - `yarn lint --fix` passes. - `yarn test` passes (30/30). - Docs regenerated for `unversioned` and `v55.0.0`. * [docs][config-types] sync xdl schemas (expo#43929) * [prebuild-config] remove deprecated plugins (expo#43918) * [web][camera] Fix `isAvailableAsync` on web (expo#43932) * [ui][android] - Refactor `Button` and `IconButton` (expo#43859) # Why Matches Button and IconButton to native APIs. Removes custom props. <!-- Please describe the motivation for this PR, and link to relevant GitHub issues, forums posts, or feature requests. --> # How - Removed `variant` prop and split `Button` and `IconButton` into 5 separate components matching M3 `Button`, `FilledTonalButton`, `OutlinedButton`, `ElevatedButton`, `TextButton`. Removed `text`, `leadingIcon`, `trailingIcon`, `color`, `elementColors`, `disabled` props. Added `onClick`, `enabled`, `colors`, `shape`, `modifiers`. `Content` is now composed via children instead of string props. Followed [Compose Button docs](https://developer.android.com/develop/ui/compose/components/button) for Expo UI docs. - Same approach in `IconButton`, split into `IconButton`, `FilledIconButton`, `FilledTonalIconButton`, `OutlinedIconButton`. Removed `variant`, `color`, `elementColors`, `disabled`, `onPress`. Followed [Compose IconButton docs](https://developer.android.com/develop/ui/compose/components/icon-button) for Expo UI docs. <!-- How did you build this feature or fix this bug and why? --> # Test Plan - Updated ButtonScreen.android.tsx NCL example - Created `IconButtonScreen.android.tsx` NCL example - Updated `button.mdx` and `iconbutton.mdx` docs with usage examples. https://github.com/user-attachments/assets/c444331a-b57f-4356-a791-9b82cb4e61e3 <!-- 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. --> - [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) * Fix capitalization in screens.mdx documentation (expo#43936) # Why Fix grammar in docs * [docs] merge skills plugin into one (expo#43938) * [config-types] revert update (expo#43940) * [router] Fix `Stack.Protected` not applying to index routes (expo#43769) * fix(cli): support files >= 2 GiB in AFC device upload (expo#43755) Fixes `RangeError [ERR_FS_FILE_TOO_LARGE]` when installing apps that contain files >= 2 GiB onto physical iOS devices via `npx expo run:ios`. `AFCClient.uploadFile()` used `fs.readFile()` to read the entire source file into a `Buffer` before uploading. Node.js `Buffer` has a hard 2 GiB limit (`kMaxLength`), so any individual file in the `.app` bundle exceeding this size causes the install to fail with: ``` RangeError [ERR_FS_FILE_TOO_LARGE]: File size (2472937860) is greater than 2 GiB at FSReqCallback.readFileAfterStat [as oncomplete] (node:fs:316:11) ``` This is increasingly common with apps bundling on-device ML models (e.g. LLaMA, Stable Diffusion). ## Fix Replace `fs.readFile()` with chunked reading via `fs.openSync`/`fs.readSync` in 8 MiB chunks. Each chunk is written to the device using the existing `FILE_WRITE` AFC operation. The AFC protocol already supports multiple sequential writes to an open file descriptor, so no protocol-level changes are needed. - Removed unused `promisify` import from `util` - Added tests for chunked upload behavior, empty files, and error handling (remote fd cleanup) --------- Co-authored-by: Cedric van Putten <[email protected]> * [ui][android] - Match Card with compose API (expo#43896) * fix(modules-core): serialize PersistentFileLog reads on the dispatch queue (expo#43958) * fix(autolinking): respect NO_COLOR and handle async errors in CLI entry (expo#43915) * [expo-router] fix regex for routes with multiple spaces (expo#43935) # Why The regex for `getStateForPath` was only covering routes with single space (e.g. `my route.tsx`). This PR adds support for multiple spaces (e.g. `my super route.tsx`) # How Add `g` to `replace` function # Test Plan 1. Unit tests 2. Manual testing **Before** <img width="1140" height="605" alt="Screenshot 2026-03-16 at 10 16 28" src="proxy.php?url=https%3A%2F%2Fgithub.com%2F%3Ca+href%3D"https://github.com/user-attachments/assets/4d3bd834-fb27-4f40-8618-2f738dc983c3">https://github.com/user-attachments/assets/4d3bd834-fb27-4f40-8618-2f738dc983c3" /> **After** <img width="1140" height="605" alt="Screenshot 2026-03-16 at 10 16 46" src="proxy.php?url=https%3A%2F%2Fgithub.com%2F%3Ca+href%3D"https://github.com/user-attachments/assets/b4f11f4f-7bd3-450d-868c-dbec73ff0eec">https://github.com/user-attachments/assets/b4f11f4f-7bd3-450d-868c-dbec73ff0eec" /> # Checklist <!-- Please check the appropriate items below if they apply to your diff. --> - [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) * [ui][android] - Match Progress API with Compose (expo#43907) # Why Match Progress Indicator API closely with the native Jetpack Compose API. # How - Split single `ProgressView` with variant enum into 4 separate native views: `LinearProgressIndicator`, `CircularProgressIndicator`, `LinearWavyProgressIndicator`, `CircularWavyProgressIndicator` - Flattened `elementColors: { trackColor }` → direct `trackColor` prop to match native - Added native props: `strokeCap`, `gapSize` (linear/circular), `strokeWidth` (circular only) - Merged separate `linearprogress.mdx` and `circularprogress.mdx` docs into single `progress.mdx` - Added redirects from old doc paths - Updated NCL example screen to use `LazyColumn` + `Card` layout # Test Plan Test `ProgressViewScreen.android.tsx` screen in NCL app. # 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) * [expo-router] fix params mutation in getPathDataFromState (expo#43934) # Why `getPathDataFromState` was deleting params from the object passed as argument. Since this function should be pure this could cause unexpected bugs. The upstream react-navigation function does not mutate the original object, but rather modifies the shallow copy. # How Shallow copy the params object before deleting properties # Test Plan 1. Unit tests 2. Manual testing # 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) * [android][updates] Add correct cancellation handling (expo#43966) * [expo-navigation-bar] Add style and hidden properties to the config plugin (expo#43978) * [expo-status-bar] Add config plugin (expo#43968) * [docs] Document EAS_BUILD_DISABLE_BUNDLE_JAVASCRIPT_STEP env var * [widgets] Remove unused `Compression` related code (expo#43981) # Why Fixes expo#43956 # How Remove unused code from initial implementation related to `Compression` # Test Plan Compile the app and see if it 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). - [x] Conforms with the [Documentation Writing Style Guide](https://github.com/expo/expo/blob/main/guides/Expo%20Documentation%20Writing%20Style%20Guide.md) * [ui][android] - Match Switch API with native (expo#43887) # Why Matches `Switch` to Compose API and adds [Checkbox](https://composables.com/docs/androidx.compose.material3/material3/components/Checkbox) and [TriStateCheckbox](https://composables.com/docs/androidx.compose.material3/material3/components/TriStateCheckbox) components <!-- Please describe the motivation for this PR, and link to relevant GitHub issues, forums posts, or feature requests. --> # How - Matches prop names to native API - Split Switch into `Switch` and `Checkbox` - Added examples - Added accessibility modifier and example for checkbox. <!-- How did you build this feature or fix this bug and why? --> # Test Plan Added/tested example in NCL and docs. https://github.com/user-attachments/assets/ac18ec48-9c9b-4100-a9e2-2a52f1ea0345 <!-- 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. --> - [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) * [expo-router] fix hash order to be RFC compliant (expo#43933) # Why Fixes: expo#43735 # How Parse `hash` correctly from the url # Test Plan 1. Unit tests 2. Manual testing **Before** https://github.com/user-attachments/assets/0280c9dc-9555-4c89-8605-9992852d7c46 **After** https://github.com/user-attachments/assets/846ce243-0300-426a-8964-55f65dfdae35 # Checklist <!-- Please check the appropriate items below if they apply to your diff. --> - [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) * [ui][android] - Match `ToggleButton` with Compose API (expo#43974) # Why Match the `ToggleButton` JS component to the native Jetpack Compose API, following the same pattern used for Button, IconButton, Card, and Chip refactors. # How - Split the single `ToggleButton` component (with `variant` prop) into four separate components that map 1:1 to their Material 3 Compose counterparts: `ToggleButton`, `IconToggleButton`, `FilledIconToggleButton`, `OutlinedIconToggleButton`. - Removed `variant`, `text`, `color`, and `disabled` props. Content is now passed via `children`, and `colors` (with checked/unchecked variants) replaces `color`. `enabled` replaces `disabled`. - Updated docs and added a `ToggleButtonScreen` example in native-component-list. # Test Plan - Open native-component-list → Expo UI → ToggleButton component screen on Android. - Verify all four variants render and toggle correctly. - Verify custom colors apply to checked/unchecked states. # 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) * [ui] Add `Link` component (expo#43983) # Why It's needed for widgets, but also works in a standalone app # How Add `Link` view according to apple documentation https://developer.apple.com/documentation/swiftui/link # Test Plan I've added Link screen to the NCL * [ui][android] - Picker refactor (expo#43809) # Why Removes Picker in order to map JS component API with platform API for better composition. <!-- Please describe the motivation for this PR, and link to relevant GitHub issues, forums posts, or feature requests. --> # How - Removes custom Picker. - Adds RadioButton and Segmented Button as separate components. - Adds recommended accessibility modifiers - Updates docs with accessibility practices. Took example reference from https://composables.com/docs/androidx.compose.material3/material3/components/RadioButton <!-- How did you build this feature or fix this bug and why? --> # Test Plan Added/tested examples in NCL <!-- 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. --> - [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) * [docs] Add Expo Agent intro video to additional resources (expo#43990) # Why The Expo team published a new video, "Introducing Expo Agent (beta)", on the @ExpoDevelopers YouTube channel on March 10, 2026. It is not yet listed on the additional resources page. # How - Add the "Introducing Expo Agent (beta)" video entry to the top of the `YOUTUBE_VIDEOS` array in `docs/public/static/talks.ts`. - Update `modificationDate` in `docs/pages/additional-resources/index.mdx` to reflect the current date. <!-- How did you build this feature or fix this bug and why? --> # 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. --> <img width="2776" height="844" alt="CleanShot 2026-03-17 at 15 35 11@2x" src="proxy.php?url=https%3A%2F%2Fgithub.com%2F%3Ca+href%3D"https://github.com/user-attachments/assets/fb4dc5c5-e8d1-4a32-a85e-9d85cd9a3b0b">https://github.com/user-attachments/assets/fb4dc5c5-e8d1-4a32-a85e-9d85cd9a3b0b" /> # 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) * [widgets] Automatically add `target` for `Button` (expo#43977) # Why Next step to remove `target` prop requirement on `Button`. expo#43925 # How Add a unique `target` prop to every `Button`. If a parent element has a `key` prop, it will be appended to help uniquely identify each button (e.g., when rendering buttons inside a `.map()`). # Test Plan I've added a few tests that hopefully covers all possible use cases. * [ios][expo-ui] Add date and timer support to Text component (expo#43552) * [ui] Add `widgetURL` modifier (expo#43984) # Why Widgets # How Added widget-specific modifier and moved existing widget-one to separate file. # Test Plan Tested in widgets-tester * [widgets] Add support for `Link` view from `@expo/ui` (expo#43985) # Why Add support for expo#43983 # How Add case for `Link` view in `DynamicView` # Test Plan Tested in widget-tester. * merge commit update (#40) * Create objective-c-xcode.yml (#1) Signed-off-by: Kristen T. Tran <[email protected]> * Create swift.yml (#24) * Create sonarcloud.yml (#25) * Create codeql.yml --------- Signed-off-by: Kristen T. Tran <[email protected]> --------- Signed-off-by: Kristen T. Tran <[email protected]> Co-authored-by: Copilot Autofix powered by AI <62310815+github-advanced-security[bot]@users.noreply.github.com> Co-authored-by: Jakob Malmo <[email protected]> Co-authored-by: Vojtech Novak <[email protected]> Co-authored-by: Kudo Chien <[email protected]> Co-authored-by: Carlos Roldán <[email protected]> Co-authored-by: Jakub Tkacz <[email protected]> Co-authored-by: Aman Mittal <[email protected]> Co-authored-by: Gabriel Donadel Dall'Agnol <[email protected]> Co-authored-by: Alan Hughes <[email protected]> Co-authored-by: Mathieu Acthernoene <[email protected]> Co-authored-by: Krystof Woldrich <[email protected]> Co-authored-by: nishan (o^▽^o) <[email protected]> Co-authored-by: ciospettw <[email protected]> Co-authored-by: Brent Vatne <[email protected]> Co-authored-by: Evan Bacon <[email protected]> Co-authored-by: Expo Bot <[email protected]> Co-authored-by: Ray <[email protected]> Co-authored-by: Janic Duplessis <[email protected]> Co-authored-by: Pavol Rusnak <[email protected]> Co-authored-by: Jakub Grzywacz <[email protected]> Co-authored-by: Zachary Ebenfeld <[email protected]> Co-authored-by: contra <[email protected]> Co-authored-by: Cedric van Putten <[email protected]> Co-authored-by: Louis <[email protected]>
3 tasks
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 join this conversation on GitHub.
Already have an account?
Sign in to comment
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.
Exposes SwiftUI's
.defaultScrollAnchor(_:)(iOS 17+) as a modifier. This lets you control where a ScrollView or List starts its scroll position without any geometric transform hacks.The scroll view starts at the bottom. Scroll up to see older content. No
scaleEffect(y: -1)flip, no reversed data, no inverted scroll indicators.Reuses the existing
UnitPointOptionsenum and itstoUnitPointconverter. Accepts anyUnitPointpreset:top,bottom,center,leading,trailing,topLeading,topTrailing,bottomLeading,bottomTrailing,zero.Falls back to a no-op on iOS < 17.
Same pattern as
scrollDismissesKeyboard,scrollDisabled,scrollContentBackground.Test plan
BUILD SUCCEEDED, zero errors.native-component-listwith a 50-itemScrollView+defaultScrollAnchor('bottom'). Confirmed the view loads scrolled to "Message 50" at the bottom. Scroll indicators point the right way.yarn buildpasses.yarn lint --fixpasses.