Skip to content

Commit e45f11c

Browse files
authored
fix(ios): remove stale experimental iOS test skips (#246)
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.
1 parent 9299651 commit e45f11c

7 files changed

Lines changed: 11 additions & 89 deletions

File tree

android/src/new/java/com/margelo/nitro/rive/HybridRiveFile.kt

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -99,10 +99,8 @@ class HybridRiveFile(
9999
Artboard.fromFile(file)
100100
}
101101
val vmSource = ViewModelSource.DefaultForArtboard(artboard)
102-
// Name is null because the Rive Android SDK does not expose the ViewModel name
103-
// from a ViewModelInstance — name-dependent operations will throw UnsupportedOperationException.
104-
// Track upstream: https://github.com/rive-app/rive-android/issues/XXX
105-
return HybridViewModel(file, riveWorker, null, this, vmSource)
102+
val vmInfo = file.getDefaultViewModelInfo(artboard)
103+
return HybridViewModel(file, riveWorker, vmInfo.viewModelName, this, vmSource)
106104
}
107105

108106
// Deprecated: Use defaultArtboardViewModelAsync instead

android/src/new/java/com/margelo/nitro/rive/HybridViewModel.kt

Lines changed: 9 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -14,26 +14,18 @@ import kotlinx.coroutines.runBlocking
1414
class HybridViewModel(
1515
private val riveFile: RiveFile,
1616
private val riveWorker: CommandQueue,
17-
// Null when constructed via DefaultForArtboard — the Rive Android SDK does not expose
18-
// the ViewModel name from a ViewModelInstance, so name-dependent operations are unavailable.
19-
// Track: https://github.com/rive-app/rive-android/issues/XXX
2017
private val viewModelName: String?,
2118
private val parentFile: HybridRiveFile,
2219
private val vmSource: ViewModelSource
2320
) : HybridViewModelSpec() {
2421
companion object {
2522
private const val TAG = "HybridViewModel"
26-
private const val NO_NAME_ERROR =
27-
"This operation requires the ViewModel name, which is unavailable for ViewModels " +
28-
"obtained via defaultArtboardViewModel(). The Rive Android SDK does not yet expose " +
29-
"the ViewModel name from a ViewModelInstance. Use a named ViewModel instead, or " +
30-
"track the upstream fix: https://github.com/rive-app/rive-android/issues/XXX"
3123
}
3224

3325
override val propertyCount: Double
3426
get() {
3527
DeprecationWarning.warn("propertyCount", "getPropertyCountAsync")
36-
val name = viewModelName ?: throw UnsupportedOperationException(NO_NAME_ERROR)
28+
val name = viewModelName ?: throw UnsupportedOperationException("ViewModel name is unavailable")
3729
return try {
3830
runBlocking { riveFile.getViewModelProperties(name) }.size.toDouble()
3931
} catch (e: Exception) {
@@ -45,7 +37,7 @@ class HybridViewModel(
4537
override val instanceCount: Double
4638
get() {
4739
DeprecationWarning.warn("instanceCount", "getInstanceCountAsync")
48-
val name = viewModelName ?: throw UnsupportedOperationException(NO_NAME_ERROR)
40+
val name = viewModelName ?: throw UnsupportedOperationException("ViewModel name is unavailable")
4941
return try {
5042
runBlocking { riveFile.getViewModelInstanceNames(name) }.size.toDouble()
5143
} catch (e: Exception) {
@@ -55,22 +47,22 @@ class HybridViewModel(
5547
}
5648

5749
override val modelName: String
58-
get() = viewModelName ?: throw UnsupportedOperationException(NO_NAME_ERROR)
50+
get() = viewModelName ?: throw UnsupportedOperationException("ViewModel name is unavailable")
5951

6052
override fun getPropertyCountAsync(): Promise<Double> {
61-
val name = viewModelName ?: return Promise.rejected(UnsupportedOperationException(NO_NAME_ERROR))
53+
val name = viewModelName ?: return Promise.rejected(UnsupportedOperationException("ViewModel name is unavailable"))
6254
return Promise.async { riveFile.getViewModelProperties(name).size.toDouble() }
6355
}
6456

6557
override fun getInstanceCountAsync(): Promise<Double> {
66-
val name = viewModelName ?: return Promise.rejected(UnsupportedOperationException(NO_NAME_ERROR))
58+
val name = viewModelName ?: return Promise.rejected(UnsupportedOperationException("ViewModel name is unavailable"))
6759
return Promise.async { riveFile.getViewModelInstanceNames(name).size.toDouble() }
6860
}
6961

7062
// Deprecated: Use createInstanceByNameAsync instead
7163
override fun createInstanceByIndex(index: Double): HybridViewModelInstanceSpec? {
7264
DeprecationWarning.warn("createInstanceByIndex", "createInstanceByNameAsync")
73-
val name = viewModelName ?: throw UnsupportedOperationException(NO_NAME_ERROR)
65+
val name = viewModelName ?: throw UnsupportedOperationException("ViewModel name is unavailable")
7466
return try {
7567
val idx = index.toInt()
7668
val instanceNames = runBlocking { riveFile.getViewModelInstanceNames(name) }
@@ -86,7 +78,7 @@ class HybridViewModel(
8678
}
8779

8880
private suspend fun createInstanceByNameImpl(instanceName: String): HybridViewModelInstanceSpec? {
89-
val name = viewModelName ?: throw UnsupportedOperationException(NO_NAME_ERROR)
81+
val name = viewModelName ?: throw UnsupportedOperationException("ViewModel name is unavailable")
9082
val instanceNames = riveFile.getViewModelInstanceNames(name)
9183
if (!instanceNames.contains(instanceName)) return null
9284
val source = vmSource.namedInstance(instanceName)
@@ -97,7 +89,7 @@ class HybridViewModel(
9789
// Deprecated: Use createInstanceByNameAsync instead
9890
override fun createInstanceByName(name: String): HybridViewModelInstanceSpec? {
9991
DeprecationWarning.warn("createInstanceByName", "createInstanceByNameAsync")
100-
if (viewModelName == null) throw UnsupportedOperationException(NO_NAME_ERROR)
92+
if (viewModelName == null) throw UnsupportedOperationException("ViewModel name is unavailable")
10193
return try {
10294
runBlocking { createInstanceByNameImpl(name) }
10395
} catch (e: UnsupportedOperationException) {
@@ -109,7 +101,7 @@ class HybridViewModel(
109101
}
110102

111103
override fun createInstanceByNameAsync(name: String): Promise<HybridViewModelInstanceSpec?> {
112-
if (viewModelName == null) return Promise.rejected(UnsupportedOperationException(NO_NAME_ERROR))
104+
if (viewModelName == null) return Promise.rejected(UnsupportedOperationException("ViewModel name is unavailable"))
113105
return Promise.async { createInstanceByNameImpl(name) }
114106
}
115107

example/__tests__/databinding-advanced.harness.ts

Lines changed: 0 additions & 31 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
import { describe, it, expect } from 'react-native-harness';
2-
import { Platform } from 'react-native';
32
import type {
43
ViewModelInstance,
54
ViewModelStringProperty,
@@ -11,9 +10,6 @@ const DATABINDING_LISTS = require('../assets/rive/databinding_lists.riv');
1110
const DATABINDING_IMAGES = require('../assets/rive/databinding_images.riv');
1211
const ARTBOARD_DB_TEST = require('../assets/rive/artboard_db_test.riv');
1312

14-
const isExperimentalIOS =
15-
Platform.OS === 'ios' && RiveFileFactory.getBackend() === 'experimental';
16-
1713
function expectDefined<T>(value: T): asserts value is NonNullable<T> {
1814
expect(value).toBeDefined();
1915
}
@@ -193,9 +189,6 @@ describe('List Properties', () => {
193189
});
194190

195191
it('getInstanceAt returns ViewModelInstances with correct names', async () => {
196-
if (isExperimentalIOS) {
197-
return; // getInstanceAt crashes experimental iOS renderer (rive::CommandQueue::processMessages)
198-
}
199192
const file = await loadFile(DATABINDING_LISTS);
200193
const vm = file.viewModelByName('DevRel');
201194
expectDefined(vm);
@@ -216,9 +209,6 @@ describe('List Properties', () => {
216209
});
217210

218211
it('addInstance increases length', async () => {
219-
if (isExperimentalIOS) {
220-
return; // list mutations crash experimental iOS renderer (rive::CommandQueue::processMessages)
221-
}
222212
const file = await loadFile(DATABINDING_LISTS);
223213
const devRelVM = file.viewModelByName('DevRel');
224214
expectDefined(devRelVM);
@@ -248,9 +238,6 @@ describe('List Properties', () => {
248238
});
249239

250240
it('removeInstanceAt decreases length', async () => {
251-
if (isExperimentalIOS) {
252-
return; // list mutations crash experimental iOS renderer (rive::CommandQueue::processMessages)
253-
}
254241
const file = await loadFile(DATABINDING_LISTS);
255242
const vm = file.viewModelByName('DevRel');
256243
expectDefined(vm);
@@ -266,9 +253,6 @@ describe('List Properties', () => {
266253
});
267254

268255
it('swap reorders items', async () => {
269-
if (isExperimentalIOS) {
270-
return; // list mutations crash experimental iOS renderer (rive::CommandQueue::processMessages)
271-
}
272256
const file = await loadFile(DATABINDING_LISTS);
273257
const vm = file.viewModelByName('DevRel');
274258
expectDefined(vm);
@@ -292,9 +276,6 @@ describe('List Properties', () => {
292276
});
293277

294278
it('addInstanceAt inserts at position', async () => {
295-
if (isExperimentalIOS) {
296-
return; // list mutations crash experimental iOS renderer (rive::CommandQueue::processMessages)
297-
}
298279
const file = await loadFile(DATABINDING_LISTS);
299280
const devRelVM = file.viewModelByName('DevRel');
300281
expectDefined(devRelVM);
@@ -322,9 +303,6 @@ describe('List Properties', () => {
322303

323304
describe('Artboard Properties', () => {
324305
it('artboardProperty returns defined properties', async () => {
325-
if (isExperimentalIOS) {
326-
return; // artboard_db_test.riv crashes experimental iOS renderer on load
327-
}
328306
const file = await loadFile(ARTBOARD_DB_TEST);
329307
const vm = file.defaultArtboardViewModel();
330308
expectDefined(vm);
@@ -339,9 +317,6 @@ describe('Artboard Properties', () => {
339317
});
340318

341319
it('getBindableArtboard returns a BindableArtboard with correct name', async () => {
342-
if (isExperimentalIOS) {
343-
return;
344-
}
345320
const file = await loadFile(ARTBOARD_DB_TEST);
346321
const artboardNames = file.artboardNames;
347322
expect(artboardNames.length).toBeGreaterThan(0);
@@ -352,9 +327,6 @@ describe('Artboard Properties', () => {
352327
});
353328

354329
it('artboardProperty.set(bindable) does not throw', async () => {
355-
if (isExperimentalIOS) {
356-
return;
357-
}
358330
const file = await loadFile(ARTBOARD_DB_TEST);
359331
const vm = file.defaultArtboardViewModel();
360332
expectDefined(vm);
@@ -373,9 +345,6 @@ describe('Artboard Properties', () => {
373345

374346
describe('Image Properties', () => {
375347
it('imageProperty("bound_image") returns defined property', async () => {
376-
if (isExperimentalIOS) {
377-
return; // databinding_images.riv crashes experimental iOS renderer on load
378-
}
379348
const file = await loadFile(DATABINDING_IMAGES);
380349
const vm = file.viewModelByName('MyViewModel');
381350
expectDefined(vm);

example/__tests__/viewmodel-properties.harness.ts

Lines changed: 0 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -66,14 +66,6 @@ describe('ViewModel Properties', () => {
6666
});
6767

6868
it('colorProperty get/set works', async () => {
69-
if (
70-
Platform.OS === 'ios' &&
71-
RiveFileFactory.getBackend() === 'experimental'
72-
) {
73-
// rive-ios experimental: Color.argbValue is internal, getter returns 0
74-
return;
75-
}
76-
7769
const instance = await createGordonInstance();
7870
const colorProperty = instance.colorProperty('favourite_color');
7971
expectDefined(colorProperty);
@@ -205,14 +197,6 @@ describe('Property Listeners', () => {
205197
});
206198

207199
it('colorProperty addListener returns cleanup function', async () => {
208-
if (
209-
Platform.OS === 'ios' &&
210-
RiveFileFactory.getBackend() === 'experimental'
211-
) {
212-
// rive-ios experimental: Color.argbValue is internal, addListener not supported
213-
return;
214-
}
215-
216200
const instance = await createGordonInstance();
217201
const prop = instance.colorProperty('favourite_color');
218202
expectDefined(prop);

example/package.json

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,6 @@
3434
"@react-native-community/cli": "18.0.0",
3535
"@react-native-community/cli-platform-android": "18.0.0",
3636
"@react-native-community/cli-platform-ios": "18.0.0",
37-
"@react-native-harness/coverage-ios": "file:/Users/boga/Work/Margelo/react-native-harness/packages/coverage-ios",
3837
"@react-native-harness/platform-android": "1.0.0",
3938
"@react-native-harness/platform-apple": "1.0.0",
4039
"@react-native/babel-preset": "0.79.2",

example/rn-harness.config.mjs

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -24,12 +24,4 @@ export default {
2424
}),
2525
],
2626
defaultRunner: 'ios',
27-
28-
coverage: {
29-
native: {
30-
ios: {
31-
pods: ['RNRive', 'RiveRuntime'],
32-
},
33-
},
34-
},
3527
};

yarn.lock

Lines changed: 0 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -4431,17 +4431,6 @@ __metadata:
44314431
languageName: node
44324432
linkType: hard
44334433

4434-
"@react-native-harness/coverage-ios@file:/Users/boga/Work/Margelo/react-native-harness/packages/coverage-ios::locator=react-native-rive-example%40workspace%3Aexample":
4435-
version: 1.0.0
4436-
resolution: "@react-native-harness/coverage-ios@file:/Users/boga/Work/Margelo/react-native-harness/packages/coverage-ios#/Users/boga/Work/Margelo/react-native-harness/packages/coverage-ios::hash=aee41f&locator=react-native-rive-example%40workspace%3Aexample"
4437-
dependencies:
4438-
tslib: ^2.3.0
4439-
peerDependencies:
4440-
react-native: "*"
4441-
checksum: c44138fb2bbeafef88ca9469f2b46901428b7aa51736b903142e4025124d0df11ef720182835178b035a36e3ad843d82dd6ad3cdbe9e1e84640b3dd26195ee05
4442-
languageName: node
4443-
linkType: hard
4444-
44454434
"@react-native-harness/jest@npm:1.0.0":
44464435
version: 1.0.0
44474436
resolution: "@react-native-harness/jest@npm:1.0.0"
@@ -16321,7 +16310,6 @@ __metadata:
1632116310
"@react-native-community/cli": 18.0.0
1632216311
"@react-native-community/cli-platform-android": 18.0.0
1632316312
"@react-native-community/cli-platform-ios": 18.0.0
16324-
"@react-native-harness/coverage-ios": "file:/Users/boga/Work/Margelo/react-native-harness/packages/coverage-ios"
1632516313
"@react-native-harness/platform-android": 1.0.0
1632616314
"@react-native-harness/platform-apple": 1.0.0
1632716315
"@react-native-picker/picker": ^2.11.4

0 commit comments

Comments
 (0)