Skip to content

Fix native coverage for universal binaries#281

Open
mfazekas wants to merge 121 commits into
mainfrom
fix/native-coverage-patches
Open

Fix native coverage for universal binaries#281
mfazekas wants to merge 121 commits into
mainfrom
fix/native-coverage-patches

Conversation

@mfazekas

@mfazekas mfazekas commented Jun 1, 2026

Copy link
Copy Markdown
Collaborator

Patches @react-native-harness 1.3.0 to fix two issues blocking native iOS coverage:

  1. Universal binary arch detection: llvm-cov export fails on universal binaries without -arch. Patch detects multi-arch binaries via lipo -archs and passes the host architecture (uname -m), works on both Apple Silicon and Intel.

  2. Missing podspec fields: coverage-ios podspec lacks homepage and author, causing pod install validation errors. Patch adds fallback values.

Also enables native coverage in rn-harness.config.mjs and adds @react-native-harness/coverage-ios dependency.

mfazekas and others added 30 commits June 1, 2026 11:19
Add new experimental iOS backend (ios/new/) with synchronous API,
move legacy backend files to ios/legacy/, add getEnums() support,
retry listener streams on missingData, and restore TestComponentOverlay.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
getEnums() in legacy now throws directing users to the experimental
backend instead of creating throwaway Worker+File instances.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)
… binding

Each Worker has its own C++ command server with its own m_artboards handle map.
Creating separate Workers per file meant artboard handles from one file were
invalid on another file's server. Using a shared singleton Worker fixes cross-file
artboard property set. Also wires fit/alignment through experimental Fit enum and
improves asset type detection with audio/font magic bytes.

Generated with [Claude Code](https://claude.ai/code)
via [Happy](https://happy.engineering)

Co-Authored-By: Happy <yesreply@happy.engineering>
Passing fit to the Rive() constructor breaks layout mode because
the MTKView drawable isn't ready yet. Set rive.fit after
setupRiveUIView() instead.
Port Flutter data binding tests for VM access, enums, creation
variants, list properties, artboard and image properties. Includes
new .riv test assets and react-native-harness upgrade to alpha.25.
…ceByIndex

Prevents fatal crash when passing negative numbers to Swift APIs
expecting unsigned integers.
CocoaPods doesn't embed SPM-resolved dynamic frameworks automatically.
Patches the embed script to include RiveRuntime when USE_RIVE_SPM=1.
Move 19 backend-specific files to src/legacy/java/, add Gradle sourceSets
switching via USE_RIVE_NEW_API property, prepare empty experimental dirs.
Uses the new handle-based Rive Android SDK (app.rive.*) with CommandQueue,
path-based ViewModelInstance property access, and Kotlin Flows for reactivity.
Throws UnsupportedOperationException for SMI inputs, text runs, and events.
Also fixes Gradle property resolution to use rootProject.findProperty.
Custom TextureView renderer with Choreographer render loop, CommandQueue
polling infrastructure, ViewModel resolution and property validation,
and example Compose test activities for manual testing.
Also fix viewModelByIndex/ByName on Android legacy to catch SDK
exceptions instead of letting them propagate as JniExceptions.
…fferences

Fix colorProperty setter Double→Int overflow on Android legacy
(toLong().toInt()). Make enum and replaceViewModel tests tolerant
of Android SDK behavioral differences.
Use .viewModelDefault(from: .name(vmName)) for default instance
creation instead of artboard-based lookup that only worked for
the default artboard's VM. Skip instanceName, color get/set, and
non-existent property checks on experimental iOS where the SDK
doesn't support them.
…anceName

Add async variants for viewModelByName, defaultArtboardViewModel, and
ViewModel create methods. Pass instanceName through at creation time on
both iOS and Android experimental backends as a workaround for the SDK
not exposing ViewModelInstance.name.
…rimental iOS

Color.argbValue is now public in rive-ios 6.15.2 — implement getValue via
blockingAsync, addListener via valueStream, and fix setter crash by using
UInt32(bitPattern:) for negative ARGB values from JS.
- Always use SPM for RiveRuntime (remove CocoaPods fallback)
- Add RiveSPMEmbedFix module to auto-embed RiveRuntime.framework
- Pin SPM to exactVersion instead of upToNextMajorVersion
- Replace USE_RIVE_SPM with USE_RIVE_EXPERIMENTAL_RUNTIME to select
  legacy vs experimental backend independently of SPM
…erimental iOS test issues

- Rename USE_RIVE_EXPERIMENTAL_RUNTIME to USE_RIVE_NEW_API (matches Android)
- Remove USE_RIVE_SPM and all SPM embedding hacks from podspec/Podfile
- Use standard CocoaPods dependency for RiveRuntime
- Emit initial value in experimental addListener (number/string/bool/enum/color)
- Guard tests that crash on experimental iOS (list ops, autoPlay, artboard/image loading)
- Handle createInstanceByName throwing on experimental backend
mfazekas added 29 commits June 1, 2026 11:19
The experimental factory already has `var backend: String {
"experimental" }`. This adds the matching property to the legacy side so
`RiveFileFactory.backend` works for both backends.
`Int32(value)` traps when ARGB color values exceed `Int32.max` (e.g.
`0xFF0000FF` = opaque blue). Uses `Int64` + `truncatingIfNeeded` for
safe conversion.
- Pass `fit:` directly in `Rive()` constructor instead of setting it
post-creation. Removes the `layoutSubviews`/`pendingFit` workaround
since rive-ios 6.19.1 handles this (rive-ios#443)
- Warn when `updateReferencedAssets` is called on experimental backend
(not supported — concurrency API can't update already-bound artboard
assets)
- Switch asset registration from parallel `TaskGroup` to sequential to
ensure command queue ordering
- Add debug logging to `ExperimentalAssetLoader`
- Add explicit `type` fields to OutOfBandAssets example
- Add `expo-font` plugin for `kanit_regular.ttf` in expo examples

## Test plan
- QuickStart + DataBindingArtboards render correctly with `Fit.Layout`
on first mount
- Out-of-Band Assets example loads initial assets correctly
- `updateReferencedAssets` logs a warning instead of silently failing
Add addInstanceAsync, addInstanceAtAsync, removeInstanceAsync,
removeInstanceAtAsync, swapAsync. Deprecate sync versions that
return before knowing if the operation succeeded.
The library module already depends on the correct version via
package.json runtimeVersions. The pin caused version conflicts.
… app

Debug activities that import app.rive.* directly, causing the example
app to need an explicit rive-android dependency. Easy to recreate
when needed for native SDK debugging.
Removes the workaround that stripped the `RiveRuntime.Swift` submodule
from modulemaps to avoid ODR conflicts. No longer needed since the
upstream XCFramework switched from C++/ObjC++ interop to C/ObjC interop
— the generated Swift header no longer emits `swift::` namespace types.

Tested building with both Xcode 16.4 and Xcode 26.2 with the unstripped
modulemap — no ODR errors.

**Note:** This PR depends on a RiveRuntime XCFramework built with C/ObjC
interop. If the build fails with ODR errors like
`'swift::Optional::init' has different definitions in different
modules`, the workaround still needs to be re-added (revert this PR).
You can verify by checking `grep -c 'namespace swift'
RiveRuntime-Swift.h` — it should be 0.
- Rename `ExperimentalViewConfiguration` → `ViewConfiguration`,
`ExperimentalBindData` → `BindData`, `ExperimentalAssetLoader` →
`AssetLoader`
- Rename `toExperimentalFit` → `toRiveFit`, `toExperimentalAlignment` →
`toRiveAlignment`
- Remove `RCTLog` debug lines from `RiveReactNativeView` and
`HybridRiveFileFactory`
- Remove `RCTLogInfo` from `AssetLoader` — error/warn logs are
sufficient
- Update "Experimental API" comments to "concurrency API"
Navigating away from a Rive screen produced:
```
Draw failed: Artboard instance is null
Failed to swap EGL buffers: EGL_BAD_SURFACE
```

The command queue processes commands in order: `close(artboard)` then
`draw(artboard)`. The draw finds the artboard already closed.

Fix: don't call `.close()` in `dispose()` — just null the handles and
stop the render loop. The command queue drains naturally and resources
are cleaned up by GC.

## Test plan
- Open any Rive example → navigate back → no errors in logcat
…race (#244)

- **OOB assets not showing**: `registerAssets` launched fire-and-forget
coroutines — file was loaded before assets finished registering. Now
`suspend` with `awaitAll()`.
- **updateReferencedAssets**: logs warning (same as iOS — not supported
in experimental backend).
- **NestedViewModel example**: fixed layout so Rive view doesn't get
squeezed to zero when log entries appear.

## Test plan
- Out-of-Band Assets: image, font, audio all load on Android
- NestedViewModel: Rive view stays visible after pressing Replace
Add coverage-ios package, configure RNRive + RiveRuntime pod
instrumentation, and document the integration process.
Remove stale `isExperimentalIOS` test skips and fix Android ViewModel
name resolution.

- **Test skips removed:** Color property (`argbValue` now public in
rive-ios 6.19.2), artboard/list/image databinding (crashes fixed in
experimental renderer)
- **Android fix:** Use `getDefaultViewModelInfo()` (available since
rive-android 11.3.2) to resolve ViewModel name for
`defaultArtboardViewModel()`, enabling `createInstanceByName` and other
name-dependent operations

All 105 harness tests pass on both iOS experimental and Android.
The rive-android API is no longer experimental in 11.4.1. Matches the
iOS rename done in #240.
Font file needed by the font fallback example. Referenced in
app.config.js via expo-font plugin.
Bump RiveRuntime from 6.19.2 to 6.20.0. Fixes Swift/ObjC interop issue
in the released version.
…248)

Adds \`getPropertiesAsync()\` to \`ViewModel\` and \`ViewModelInstance\`
for the experimental backend (iOS + Android).

```ts
const vm = file.viewModelByName('Person');
const props = await vm.getPropertiesAsync();
// => [{ name: 'age', type: 'number' }, { name: 'name', type: 'string' }, ...]
```

New types: \`ViewModelPropertyType\` and \`ViewModelPropertyInfo\`.
Both platforms use the async rive SDK APIs (\`File.getProperties(of:)\`
/ \`RiveFile.getViewModelProperties(name)\`).

Mirrors the legacy backend PR #98.
Bump example app from RN 0.79.2 to 0.80.3. This unblocks upgrading to
reanimated 4.2+ and worklets 0.7+ for Nitro auto-box support.

- react 19.0.0 → 19.1.0
- react-native 0.79.2 → 0.80.3
- @react-native-community/cli 18.0.0 → 19.1.2
- @react-native/* packages 0.79.2 → 0.80.3
- Kotlin 2.0.21 → 2.1.20, Gradle 8.13 → 8.14.1
- Removed compose-compiler-gradle-plugin (no longer needed)
Bump react-native-harness and platform packages from 1.0.0 to 1.1.0.
## Summary

Adds 31 harness tests for the non-deprecated async API that previously
had zero test coverage. Existing tests only exercise the deprecated sync
methods (`.value`, `viewModelByName`, `createInstanceByName`, etc.) —
this fills the gap for the intended async replacements.

## What's covered

- `createDefaultInstanceAsync`, `createInstanceByNameAsync`,
`createBlankInstanceAsync`
- `defaultArtboardViewModelAsync`, `getArtboardCountAsync`,
`getArtboardNamesAsync`
- `getPropertiesAsync`, `getPropertyCountAsync`, `getInstanceCountAsync`
- `getValueAsync()` + `set()` round-trip for all 5 property types
(number, string, boolean, color, enum)
- `viewModelAsync` for nested ViewModel access
- Async list operations: `getLengthAsync`, `getInstanceAtAsync`,
`addInstanceAsync`, `removeInstanceAtAsync`, `swapAsync`

## Test plan

- [x] `yarn test:harness:ios -- __tests__/async-api.harness.ts` — 31/31
pass
- [x] Full suite `yarn test:harness:ios` — 136 pass (no regressions)
coverage-ios is now integrated in the harness packages directly,
removing the local file: dependency.
Fixes flaky autoplay harness tests. The 6.20.1 fix for "massive
timestamp delta on first frame" was causing the animation to jump
through its entire cycle in one frame, making ypos property change
detection unreliable. Verified with 100 consecutive test runs (0
failures).
Adds 15 new harness tests covering listener callback invocation, set() +
getValueAsync() round-trips, and removeListeners() for all ViewModel
property types. iOS native coverage goes from 41% to 50%.

Target types: BooleanProperty (16→84%), ColorProperty (14→84%),
EnumProperty (16→84%), TriggerProperty (0→81%), StringProperty (37→84%),
NumberProperty (85→90%).
Adds harness tests for two previously untested areas:

**Font config** (6 tests): loadFont by system name, loadFont by URL,
systemFallback, setFallbackFonts with default/weight-specific fonts,
clearFallbackFonts, error on invalid font name.

**Asset loading** (4 tests): referencedAssets with font type, image URL
type, multiple asset types, and undefined assets.

Covers HybridRiveFontConfig.swift (was 0%), AssetLoader.swift (was 1%),
DataSourceResolver.swift (was 0%), HTTPDataLoader.swift (was 43%).
Adds 7 new tests covering previously untested native code paths:

- **fromBytes**: load .riv from raw ArrayBuffer (HybridRiveFileFactory)
- **RiveImages.loadFromURLAsync**: load image and verify byteSize
(HybridRiveImageFactory + HybridRiveImage)
- **RiveLog.setLogLevel**: exercise all 4 log levels (HybridRiveLogger)
- **RiveView pause/play/reset**: exercise view control methods with a
rendered RiveView (HybridRiveView)
Upgrades harness from 1.2.0 to 1.3.0. Key improvements from harness PRs
#129 and #132:

- Bridge RPC closes immediately on app disconnect instead of waiting for
timeout
- New internal RPC transport with heartbeat-based liveness detection
- Dead app connections fail fast with `AppBridgeDisconnectedError`
instead of ambiguous "device did not respond" after 90s

Should reduce the intermittent CI flakiness we've been seeing on legacy
iOS.
…c fields

Two patches for @react-native-harness 1.3.0:

1. platform-apple: pass -arch flag to llvm-cov export for universal binaries.
   Uses lipo -archs to detect multi-arch binaries and uname -m for host arch,
   so it works on both Apple Silicon (arm64) and Intel (x86_64).

2. coverage-ios: add fallback values for missing homepage/author in podspec
   so pod install doesn't fail validation.
Base automatically changed from feat/rive-ios-experimental to main July 20, 2026 13:19
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.

1 participant