Skip to content

feat(tabs): add 'programmatic-native' actionOrigin variant#3951

Open
kkafar wants to merge 2 commits intomainfrom
@kkafar/tabs-allow-for-programmatic-native-tab-changes-2
Open

feat(tabs): add 'programmatic-native' actionOrigin variant#3951
kkafar wants to merge 2 commits intomainfrom
@kkafar/tabs-allow-for-programmatic-native-tab-changes-2

Conversation

@kkafar
Copy link
Copy Markdown
Member

@kkafar kkafar commented Apr 29, 2026

Description

Adds a fourth value to the public TabSelectedEvent.actionOrigin union: 'user' | 'programmatic-js' | 'programmatic-native' | 'implicit'. The new origin is reserved for downstream libraries that integrate directly against TabsContainer (Android) or RNSTabBarController (iOS) — this library itself does not produce it.

The recent state/request split (PR #3950) already plumbs actionOrigin from the buffered request through to the emitted event, so adding the variant is mostly additive: union extension on the JS / codegen side, one enum case on each native platform, one switch arm in the iOS conversion helper.

iOS already exposed the surface that downstream needs (RNSTabBarController.setPendingNavigationStateUpdate: is on the public header). On Android, TabsContainer and its pending-op + flush methods were internal, so this PR also widens the Android surface to match: the class is public, the request-passing and flushing methods are public, and the rest stays internal. TabsHost.container stays private — downstream is expected to obtain the container reference through its own integration path, not via the React view.

Stacked on #3950.

Changes

  • Public TS TabSelectedEvent.actionOrigin union extended with 'programmatic-native'. Codegen specs (Android + iOS) mirror it.
  • iOS: RNSTabsActionOriginProgrammaticNative enum case + matching arm in RNSOnTabSelectedActionOriginFromRNSTabsActionOrigin mapping the Obj-C enum to the codegen C++ scoped enum.
  • Android: TabsActionOrigin.PROGRAMMATIC_NATIVE + toString() mapping.
  • Android visibility widening:
    • TabsContainer class is now public; constructor stays internal (it takes the still-internal TabsContainerDelegate).
    • setContainerOperation(TabsContainerOp) replaced with public setPendingNavigationStateUpdate(TabsNavStateUpdateRequest) — takes the request directly, wraps in TabSelectOp internally. Naming mirrors the iOS selector.
    • performContainerUpdateIfNeeded() is public — the flush trigger.
    • TabsContainerOp / TabSelectOp stay internal.
  • iOS doc comments in RNSTabsNavigationState.h reformatted: per-case /** … */ blocks for RNSTabsActionOrigin (and the rest of the file made consistent with /**\n * …\n */ style).

Test plan

  • yarn check-types clean.
  • yarn lint clean for changed files (only pre-existing warnings in unrelated code).
  • yarn android from FabricExample/ — Android codegen + Kotlin compile clean. App installs on emulator.
  • Grep sweep:
    • git grep -n "programmatic-native" -- src/ → 3 hits (public TS type + both codegen specs).
    • git grep -n "RNSTabsActionOriginProgrammaticNative" -- ios/ → hits in RNSTabsNavigationState.h and RNSConversions-Tabs.mm.
    • git grep -n "PROGRAMMATIC_NATIVE" -- android/ → hits in TabsActionOrigin.kt.
    • git grep -n "setContainerOperation" -- android/ → zero hits (renamed).
    • git grep -nE "internal class TabsContainer\b" -- android/ → zero hits (class made public).
  • iOS Xcode build via bundle exec pod install && yarn ios (regenerates iOS codegen with the extended union) — pending local Mac toolchain run.
  • Runtime smoke from a downstream consumer producing programmatic-native is out of scope — this library has no producer call site.

Checklist

  • Included code example that can be used to test this change.
  • For visual changes, included screenshots / GIFs / recordings documenting the change.
  • For API changes, updated relevant public types.
  • Ensured that CI passes

@kkafar kkafar force-pushed the @kkafar/tabs-allow-for-programmatic-native-tab-changes branch from fefd025 to 0d7d7fb Compare April 30, 2026 08:36
Base automatically changed from @kkafar/tabs-allow-for-programmatic-native-tab-changes to main April 30, 2026 08:58
kkafar added 2 commits April 30, 2026 10:59
Adds a fourth value to the public `TabSelectedEvent.actionOrigin` union:
`'user' | 'programmatic-js' | 'programmatic-native' | 'implicit'`. The
new origin is reserved for downstream libraries that integrate directly
against `TabsContainer` (Android) or `RNSTabBarController` (iOS) — this
library itself does not produce it.

Plumbed end-to-end:

- Public TS contract + both Android/iOS codegen specs.
- iOS: `RNSTabsActionOriginProgrammaticNative` enum case and the matching
  arm in the conversion helper that maps the Obj-C enum to the codegen
  C++ scoped enum.
- Android: `TabsActionOrigin.PROGRAMMATIC_NATIVE` + `toString()` mapping.

Android visibility widening to make the new origin reachable from
outside the library (iOS already exposes the equivalent surface):

- `TabsContainer` class is now public; constructor stays `internal`
  because it takes the still-`internal` `TabsContainerDelegate`.
  Downstream is expected to obtain the container reference via its own
  integration path (TabsHost.container stays `private`).
- `setContainerOperation(TabsContainerOp)` is replaced with a public
  `setPendingNavigationStateUpdate(TabsNavStateUpdateRequest)` that
  takes the request directly and wraps in `TabSelectOp` internally.
  Naming mirrors `RNSTabBarController.setPendingNavigationStateUpdate:`.
- `performContainerUpdateIfNeeded()` is now public — the flush trigger
  downstream calls after setting a pending update.
- Internal sealed-class machinery (`TabsContainerOp`, `TabSelectOp`)
  stays internal.

Also reformats iOS doc comments in `RNSTabsNavigationState.h` to
per-case style and the `/**\n * ...\n */` form consistently.
@kkafar kkafar force-pushed the @kkafar/tabs-allow-for-programmatic-native-tab-changes-2 branch from f09f976 to 5bc649e Compare April 30, 2026 09:19
@kkafar kkafar marked this pull request as ready for review April 30, 2026 09:20
@kkafar kkafar requested review from Copilot, kligarski, kmichalikk and t0maboro and removed request for Copilot and kligarski April 30, 2026 09:20
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Extends the public TabSelectedEvent.actionOrigin API to include a new 'programmatic-native' variant, enabling downstream native integrations to attribute tab changes initiated directly by native container/controller APIs.

Changes:

  • Extend the JS/TS and codegen event contract to include 'programmatic-native' for actionOrigin.
  • iOS: add RNSTabsActionOriginProgrammaticNative and map it to the codegen event enum in RNSConversions-Tabs.mm; reformat doc comments in RNSTabsNavigationState.h.
  • Android: add TabsActionOrigin.PROGRAMMATIC_NATIVE, and widen TabsContainer visibility plus expose public methods to set/flush pending navigation state updates.

Reviewed changes

Copilot reviewed 8 out of 8 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
src/fabric/tabs/TabsHostIOSNativeComponent.ts Extends codegen event union with 'programmatic-native'.
src/fabric/tabs/TabsHostAndroidNativeComponent.ts Extends codegen event union with 'programmatic-native'.
src/components/tabs/host/TabsHost.types.ts Extends public TS actionOrigin union and docs.
ios/tabs/host/RNSTabsNavigationState.h Adds iOS enum case and reformats doc comments.
ios/conversion/RNSConversions-Tabs.mm Maps Obj-C action origin enum to codegen event enum, adding native-programmatic arm.
android/src/main/java/com/swmansion/rnscreens/gamma/tabs/host/TabsHost.kt Updates host to use new TabsContainer API for pending updates.
android/src/main/java/com/swmansion/rnscreens/gamma/tabs/container/TabsContainer.kt Makes container public and exposes pending-update + flush entrypoints.
android/src/main/java/com/swmansion/rnscreens/gamma/tabs/container/TabsActionOrigin.kt Adds PROGRAMMATIC_NATIVE and string mapping.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread ios/tabs/host/RNSTabsNavigationState.h
@kkafar kkafar requested a review from kligarski April 30, 2026 09:25
Copy link
Copy Markdown
Contributor

@kligarski kligarski left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For iOS, what is the intended flow for programmatic native operation?

Is it setPendingNavigationStateUpdate and then updateSelectedViewControllerIfNeeded? But this way we're missing setting _isHandlingExplicitSelectionUpdate flag which is done in performContainerUpdate and performContainerUpdate is not public.

Currently, if we use updateSelectedViewControllerIfNeeded, reconcileNavigationStateWithUIKitState is called (because we're not setting _isHandlingExplicitSelectionUpdate). Fortunately, we're early-returning via [_navigationState.selectedScreenKey isEqualToString:selectedScreenKey] check but I'm not sure if this was intended?


I also thought whether we should allow anyone to use setPendingNavigationStateUpdate and set any actionOrigin or maybe only expose a method that will accept only the key (and baseProvenance?) that will enforce ProgrammaticNative origin.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants