Hacktoberfest 2026: the issues maintainers tagged for October, open and beginner-friendly. Browse Hacktoberfest issues

[iOS] Script init caches the wrong view model when dataBind is applied after state-machine creation

Closed Beginner friendly
#414 1 comment 0 reactions 0 assignees View on GitHub

Maintainers usually reply within 1 day

A pull request for this has already been merged.

  • #415 by @mfazekas — merged
  • #421 by @rive-engineering — merged

Assessment

Difficulty
2/5
Estimated time
1-3 hours
Newbie friendliness
63/100
Issue type
Bug
Clarity
Clearly specified
Activity status
Active
Tech stack
react-native, swift, typescript
Domain
backend, mobile

Research direction

Read ios/new/RiveReactNativeView.swift, focusing on state-machine creation and dataBind resolution. Compare the current order with the supplied standalone Swift probe and proposed diff, then run the iOS reproduction with RiveRuntime 6.28.0. Done means the explicitly bound host instance is used at state-machine creation, so the probe reports isReady: 1 and observedSeed: 42; Android and the legacy backend are outside the reported validation.

Written by the indexing model from the issue text.

Description

bug iOS

Description

On the iOS new runtime, supplying an explicit view-model instance through <RiveView dataBind={instance}> can leave a Luau script reading and writing the authored default instance instead of the host instance.

The script caches context:viewModel() in init. The React Native wrapper creates the state machine before passing the explicit instance into Rive(...dataBind: .instance(...)). The script appears to initialize against the default instance during state-machine creation and retain that reference after the host model is bound.

This has a functional consequence beyond the deprecated-API warnings tracked in #403: host initialization values are invisible to the script, and script-written readiness/results never reach the host's instance. In a small probe, the host seeds seed = 42, the script sets isReady = 1 in init, and each advance copies its cached model's seed into observedSeed.

Value read from the host instance after 2 seconds Current order Bind at state-machine creation Expected
seed 42 42 42
isReady 0 1 1
observedSeed 0 42 42

The current-order rectangle renders red (script sees seed 0), while the early-binding rectangle renders green (script sees seed 42). There is no crash or script exception.

Minimal reproduction and source file

Complete standalone reproduction

The gist includes:

  • A 17,434-byte signed .riv fixture, base64-encoded because gists do not accept binary files, with a decode command and SHA-256 checksum. No CLI login is required to run the supplied file.
  • Complete scene.rml, probe.luau, and rive.yaml source: one artboard, one state machine, one script, three numbers; no application assets.
  • A runnable main.swift + run-ios.py probe that tests both initialization orders side by side using RiveRuntime 6.28.0. It prints results and asserts both outcomes.
  • Actual results.json, a minimal React Native App.tsx adapter, and proposed-fix.diff.
Verified native repro steps
  1. Clone/download the gist and decode binding-init-repro.riv.b64 using the command in its README.
  2. Download the official RiveRuntime 6.28.0 XCFramework.
  3. With an Apple Silicon iOS simulator booted, run python3 run-ios.py /path/to/RiveRuntime.xcframework.
  4. Observe lateBinding: {seed: 42, isReady: 0, observedSeed: 0} and bindingAtCreation: {seed: 42, isReady: 1, observedSeed: 42}.

Both cases create independent artboards and host model instances from the same file. Both seed the model before state-machine creation and use the same deprecated Rive(...dataBind: .instance(model)) initializer afterward. The only difference is supplying binding: model to createStateMachine.

React Native usage

The App.tsx adapter uses useViewModelInstance(riveFile, { async: true, viewModelName: 'ProbeModel', onInit }), seeds seed = 42 in onInit, and mounts RiveView only after the instance exists:

<RiveView
  file={riveFile}
  artboardName="Binding Probe"
  stateMachineName="Main"
  dataBind={instance}
  autoPlay
  fit={Fit.Layout}
  style={{ width: 160, height: 160 }}
/>

Use a native development build, add riv to Metro's assetExts, and leave the default iOS new backend enabled. The gist has the complete component and setup notes.

Likely cause / small workaround

The relevant current code is in ios/new/RiveReactNativeView.swift at v0.5.4. It calls createStateMachine(config.stateMachineName) before resolving dataBind and constructing Rive.

Moving state-machine creation after binding resolution and using the explicit-instance overload fixes the isolated probe:

let stateMachine: StateMachine
if case .instance(let vmi) = dataBind {
  stateMachine = try await artboard.createStateMachine(
    config.stateMachineName, binding: vmi
  )
} else {
  stateMachine = try await artboard.createStateMachine(config.stateMachineName)
}

The Rive iOS 6.28.0 release guidance also supplies the model at state-machine creation. The attached diff is a narrow workaround for initial explicit-instance binding, not a complete migration or a claim to fix later instance replacement. The broader auto/by-name/rebinding migration can stay under #403.

Environment / validation scope

  • Original affected application: @rive-app/react-native 0.5.3, React Native 0.86.0, Expo SDK 57, react-native-nitro-modules 0.36.5.
  • Runtime: iOS new backend, RiveRuntime 6.28.0.
  • Standalone Swift repro actually executed on iPhone 17 Pro simulator, iOS 26.5; recorded results are in the gist.
  • Latest 0.5.4 source inspected: the relevant initialization order is unchanged and its runtimeVersions.ios is also 6.28.0. I have not separately run a complete 0.5.4 React Native app.
  • The generic App.tsx adapter type-checks against 0.5.3; the executed isolated test is the Swift probe, which follows the wrapper's native initialization sequence.
  • Android and the legacy iOS backend: not tested for this issue.
  • Previous working version: not established.
  • Fixture authored with Rive CLI 1.3.0, signed and watermarked. It executes in both native cases, so this is not an unsigned-script rejection.

I searched existing issues and found #403 related, but did not find a report for this specific cached-script-model behavior. Happy to consolidate this reproduction into #403 if that is preferable.

Dominant language
TypeScript
Stars
155
Forks
17
Avg merge
1d 10h
Merged PRs (30d)
20

Getting set up

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

More from rive-app/rive-nitro-react-native

All issues in rive-app/rive-nitro-react-native

Similar issues

More TypeScript issues

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.