Skip to content

Commit 6d4a18a

Browse files
committed
feat: add async APIs for RiveFile/ViewModel and fix experimental instanceName
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.
1 parent b5a4677 commit 6d4a18a

29 files changed

Lines changed: 539 additions & 84 deletions

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

Lines changed: 39 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -59,44 +59,62 @@ class HybridRiveFile(
5959
return Promise.async { viewModelByIndexImpl(index) }
6060
}
6161

62-
override fun viewModelByName(name: String): HybridViewModelSpec? {
62+
private suspend fun viewModelByNameImpl(name: String): HybridViewModelSpec? {
6363
val file = riveFile ?: return null
64+
val names = file.getViewModelNames()
65+
if (!names.contains(name)) return null
66+
return HybridViewModel(file, riveWorker, name, this)
67+
}
68+
69+
// Deprecated: Use viewModelByNameAsync instead
70+
override fun viewModelByName(name: String): HybridViewModelSpec? {
6471
return try {
65-
val names = runBlocking { file.getViewModelNames() }
66-
if (!names.contains(name)) return null
67-
HybridViewModel(file, riveWorker, name, this)
72+
runBlocking { viewModelByNameImpl(name) }
6873
} catch (e: Exception) {
6974
Log.e(TAG, "viewModelByName('$name') failed", e)
7075
null
7176
}
7277
}
7378

74-
override fun defaultArtboardViewModel(artboardBy: ArtboardBy?): HybridViewModelSpec? {
79+
override fun viewModelByNameAsync(name: String): Promise<HybridViewModelSpec?> {
80+
return Promise.async { viewModelByNameImpl(name) }
81+
}
82+
83+
private suspend fun defaultArtboardViewModelImpl(artboardBy: ArtboardBy?): HybridViewModelSpec? {
7584
val file = riveFile ?: return null
76-
return try {
77-
val artboardName = when (artboardBy?.type) {
78-
ArtboardByTypes.INDEX -> {
79-
val artboardNames = runBlocking { file.getArtboardNames() }
80-
artboardNames.getOrNull(artboardBy.index!!.toInt())
81-
}
82-
ArtboardByTypes.NAME -> artboardBy.name
83-
null -> null
85+
val artboardName = when (artboardBy?.type) {
86+
ArtboardByTypes.INDEX -> {
87+
val artboardNames = file.getArtboardNames()
88+
artboardNames.getOrNull(artboardBy.index!!.toInt())
8489
}
90+
ArtboardByTypes.NAME -> artboardBy.name
91+
null -> null
92+
}
8593

86-
val artboard = if (artboardName != null) {
87-
Artboard.fromFile(file, artboardName)
88-
} else {
89-
Artboard.fromFile(file)
90-
}
91-
val vmSource = ViewModelSource.DefaultForArtboard(artboard)
92-
val resolvedName = runBlocking { resolveDefaultVMName(file, vmSource) }
93-
HybridViewModel(file, riveWorker, resolvedName, this, vmSource)
94+
val artboard = if (artboardName != null) {
95+
Artboard.fromFile(file, artboardName)
96+
} else {
97+
Artboard.fromFile(file)
98+
}
99+
val vmSource = ViewModelSource.DefaultForArtboard(artboard)
100+
val resolvedName = resolveDefaultVMName(file, vmSource)
101+
return HybridViewModel(file, riveWorker, resolvedName, this, vmSource)
102+
}
103+
104+
// Deprecated: Use defaultArtboardViewModelAsync instead
105+
override fun defaultArtboardViewModel(artboardBy: ArtboardBy?): HybridViewModelSpec? {
106+
return try {
107+
runBlocking { defaultArtboardViewModelImpl(artboardBy) }
94108
} catch (e: Exception) {
95109
Log.e(TAG, "defaultArtboardViewModel failed", e)
96110
null
97111
}
98112
}
99113

114+
override fun defaultArtboardViewModelAsync(artboardBy: ArtboardBy?): Promise<HybridViewModelSpec?> {
115+
return Promise.async { defaultArtboardViewModelImpl(artboardBy) }
116+
}
117+
100118
override val artboardCount: Double
101119
get() {
102120
val file = riveFile ?: return 0.0

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

Lines changed: 32 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -56,19 +56,29 @@ class HybridViewModel(
5656
}
5757
}
5858

59+
private suspend fun createInstanceByNameImpl(name: String): HybridViewModelInstanceSpec? {
60+
val instanceNames = riveFile.getViewModelInstanceNames(viewModelName)
61+
if (!instanceNames.contains(name)) return null
62+
val source = vmSource.namedInstance(name)
63+
val vmi = ViewModelInstance.fromFile(riveFile, source)
64+
return HybridViewModelInstance(vmi, riveWorker, parentFile, viewModelName, name)
65+
}
66+
67+
// Deprecated: Use createInstanceByNameAsync instead
5968
override fun createInstanceByName(name: String): HybridViewModelInstanceSpec? {
6069
return try {
61-
val instanceNames = runBlocking { riveFile.getViewModelInstanceNames(viewModelName) }
62-
if (!instanceNames.contains(name)) return null
63-
val source = vmSource.namedInstance(name)
64-
val vmi = ViewModelInstance.fromFile(riveFile, source)
65-
HybridViewModelInstance(vmi, riveWorker, parentFile, viewModelName, name)
70+
runBlocking { createInstanceByNameImpl(name) }
6671
} catch (e: Exception) {
6772
Log.e(TAG, "createInstanceByName('$name') failed", e)
6873
null
6974
}
7075
}
7176

77+
override fun createInstanceByNameAsync(name: String): Promise<HybridViewModelInstanceSpec?> {
78+
return Promise.async { createInstanceByNameImpl(name) }
79+
}
80+
81+
// Deprecated: Use createDefaultInstanceAsync instead
7282
override fun createDefaultInstance(): HybridViewModelInstanceSpec? {
7383
return try {
7484
val source = vmSource.defaultInstance()
@@ -80,6 +90,15 @@ class HybridViewModel(
8090
}
8191
}
8292

93+
override fun createDefaultInstanceAsync(): Promise<HybridViewModelInstanceSpec?> {
94+
return Promise.async {
95+
val source = vmSource.defaultInstance()
96+
val vmi = ViewModelInstance.fromFile(riveFile, source)
97+
HybridViewModelInstance(vmi, riveWorker, parentFile, viewModelName)
98+
}
99+
}
100+
101+
// Deprecated: Use createInstanceAsync instead
83102
override fun createInstance(): HybridViewModelInstanceSpec? {
84103
return try {
85104
val source = vmSource.blankInstance()
@@ -90,4 +109,12 @@ class HybridViewModel(
90109
null
91110
}
92111
}
112+
113+
override fun createInstanceAsync(): Promise<HybridViewModelInstanceSpec?> {
114+
return Promise.async {
115+
val source = vmSource.blankInstance()
116+
val vmi = ViewModelInstance.fromFile(riveFile, source)
117+
HybridViewModelInstance(vmi, riveWorker, parentFile, viewModelName)
118+
}
119+
}
93120
}

android/src/experimental/java/com/margelo/nitro/rive/HybridViewModelInstance.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,8 @@ class HybridViewModelInstance(
3939
return propertyNames.contains(path)
4040
}
4141

42+
// TODO: Workaround — rive-android experimental SDK doesn't expose ViewModelInstance.name.
43+
// Only works when caller knows the name (createInstanceByName). Falls back to "" otherwise.
4244
override val instanceName: String
4345
get() = _instanceName ?: ""
4446

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

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ class HybridRiveFile : HybridRiveFileSpec() {
4646
}
4747
}
4848

49+
// Deprecated: Use viewModelByNameAsync instead
4950
override fun viewModelByName(name: String): HybridViewModelSpec? {
5051
return try {
5152
val vm = riveFile?.getViewModelByName(name) ?: return null
@@ -55,6 +56,14 @@ class HybridRiveFile : HybridRiveFileSpec() {
5556
}
5657
}
5758

59+
override fun viewModelByNameAsync(name: String): Promise<HybridViewModelSpec?> {
60+
return Promise.async {
61+
val vm = riveFile?.getViewModelByName(name) ?: return@async null
62+
HybridViewModel(vm)
63+
}
64+
}
65+
66+
// Deprecated: Use defaultArtboardViewModelAsync instead
5867
override fun defaultArtboardViewModel(artboardBy: ArtboardBy?): HybridViewModelSpec? {
5968
try {
6069
val artboard = when (artboardBy?.type) {
@@ -70,6 +79,10 @@ class HybridRiveFile : HybridRiveFileSpec() {
7079
}
7180
}
7281

82+
override fun defaultArtboardViewModelAsync(artboardBy: ArtboardBy?): Promise<HybridViewModelSpec?> {
83+
return Promise.async { defaultArtboardViewModel(artboardBy) }
84+
}
85+
7386
override val artboardCount: Double
7487
get() = riveFile?.artboardNames?.size?.toDouble() ?: 0.0
7588

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

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ class HybridViewModel(private val viewModel: ViewModel) : HybridViewModelSpec()
3838
}
3939
}
4040

41+
// Deprecated: Use createInstanceByNameAsync instead
4142
override fun createInstanceByName(name: String): HybridViewModelInstanceSpec? {
4243
try {
4344
val vmi = viewModel.createInstanceFromName(name)
@@ -47,6 +48,18 @@ class HybridViewModel(private val viewModel: ViewModel) : HybridViewModelSpec()
4748
}
4849
}
4950

51+
override fun createInstanceByNameAsync(name: String): Promise<HybridViewModelInstanceSpec?> {
52+
return Promise.async {
53+
try {
54+
val vmi = viewModel.createInstanceFromName(name)
55+
HybridViewModelInstance(vmi)
56+
} catch (e: ViewModelException) {
57+
null
58+
}
59+
}
60+
}
61+
62+
// Deprecated: Use createDefaultInstanceAsync instead
5063
override fun createDefaultInstance(): HybridViewModelInstanceSpec? {
5164
try {
5265
val vmi = viewModel.createDefaultInstance()
@@ -56,6 +69,18 @@ class HybridViewModel(private val viewModel: ViewModel) : HybridViewModelSpec()
5669
}
5770
}
5871

72+
override fun createDefaultInstanceAsync(): Promise<HybridViewModelInstanceSpec?> {
73+
return Promise.async {
74+
try {
75+
val vmi = viewModel.createDefaultInstance()
76+
HybridViewModelInstance(vmi)
77+
} catch (e: ViewModelException) {
78+
null
79+
}
80+
}
81+
}
82+
83+
// Deprecated: Use createInstanceAsync instead
5984
override fun createInstance(): HybridViewModelInstanceSpec? {
6085
try {
6186
val vmi = viewModel.createBlankInstance()
@@ -64,4 +89,15 @@ class HybridViewModel(private val viewModel: ViewModel) : HybridViewModelSpec()
6489
return null
6590
}
6691
}
92+
93+
override fun createInstanceAsync(): Promise<HybridViewModelInstanceSpec?> {
94+
return Promise.async {
95+
try {
96+
val vmi = viewModel.createBlankInstance()
97+
HybridViewModelInstance(vmi)
98+
} catch (e: ViewModelException) {
99+
null
100+
}
101+
}
102+
}
67103
}

example/__tests__/viewmodel-instance-lookup.harness.tsx

Lines changed: 5 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ import {
77
cleanup,
88
} from 'react-native-harness';
99
import { useEffect } from 'react';
10-
import { Platform, Text, View } from 'react-native';
10+
import { Text, View } from 'react-native';
1111
import {
1212
RiveFileFactory,
1313
ArtboardByName,
@@ -16,12 +16,6 @@ import {
1616
} from '@rive-app/react-native';
1717
import type { ViewModelInstance } from '@rive-app/react-native';
1818

19-
function isExperimentalIOS() {
20-
return (
21-
Platform.OS === 'ios' && RiveFileFactory.getBackend() === 'experimental'
22-
);
23-
}
24-
2519
const MULTI_AB = require('../assets/rive/arbtboards-models-instances.riv');
2620

2721
function expectDefined<T>(value: T): asserts value is NonNullable<T> {
@@ -260,9 +254,7 @@ describe('useViewModelInstance by viewModelName + instanceName verifies _id', ()
260254
);
261255
await waitFor(() => expect(ctx.instance).not.toBeNull(), { timeout: 5000 });
262256
expect(ctx.id).toBe('vm1.vmi1.id');
263-
if (!isExperimentalIOS()) {
264-
expect(ctx.instanceName).toBe('vmi1');
265-
}
257+
expect(ctx.instanceName).toBe('vmi1');
266258
cleanup();
267259
});
268260

@@ -279,9 +271,7 @@ describe('useViewModelInstance by viewModelName + instanceName verifies _id', ()
279271
);
280272
await waitFor(() => expect(ctx.instance).not.toBeNull(), { timeout: 5000 });
281273
expect(ctx.id).toBe('vm1.vmi2.id');
282-
if (!isExperimentalIOS()) {
283-
expect(ctx.instanceName).toBe('vmi2');
284-
}
274+
expect(ctx.instanceName).toBe('vmi2');
285275
cleanup();
286276
});
287277

@@ -298,9 +288,7 @@ describe('useViewModelInstance by viewModelName + instanceName verifies _id', ()
298288
);
299289
await waitFor(() => expect(ctx.instance).not.toBeNull(), { timeout: 5000 });
300290
expect(ctx.id).toBe('vm2.vmi2.id');
301-
if (!isExperimentalIOS()) {
302-
expect(ctx.instanceName).toBe('vmi2');
303-
}
291+
expect(ctx.instanceName).toBe('vmi2');
304292
cleanup();
305293
});
306294

@@ -317,9 +305,7 @@ describe('useViewModelInstance by viewModelName + instanceName verifies _id', ()
317305
);
318306
await waitFor(() => expect(ctx.instance).not.toBeNull(), { timeout: 5000 });
319307
expect(ctx.id).toBe('vm3.vmi1.id');
320-
if (!isExperimentalIOS()) {
321-
expect(ctx.instanceName).toBe('vmi1');
322-
}
308+
expect(ctx.instanceName).toBe('vmi1');
323309
cleanup();
324310
});
325311

ios/legacy/HybridRiveFile.swift

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -49,11 +49,20 @@ class HybridRiveFile: HybridRiveFileSpec, RiveViewSource {
4949
}
5050
}
5151

52+
// Deprecated: Use viewModelByNameAsync instead
5253
func viewModelByName(name: String) throws -> (any HybridViewModelSpec)? {
5354
guard let vm = riveFile?.viewModelNamed(name) else { return nil }
5455
return HybridViewModel(viewModel: vm)
5556
}
56-
57+
58+
func viewModelByNameAsync(name: String) throws -> Promise<(any HybridViewModelSpec)?> {
59+
return Promise.async {
60+
guard let vm = self.riveFile?.viewModelNamed(name) else { return nil }
61+
return HybridViewModel(viewModel: vm)
62+
}
63+
}
64+
65+
// Deprecated: Use defaultArtboardViewModelAsync instead
5766
func defaultArtboardViewModel(artboardBy: ArtboardBy?) throws -> (any HybridViewModelSpec)? {
5867
let artboard: RiveArtboard?
5968

@@ -76,7 +85,13 @@ class HybridRiveFile: HybridRiveFileSpec, RiveViewSource {
7685
let vm = riveFile?.defaultViewModel(for: artboard) else { return nil }
7786
return HybridViewModel(viewModel: vm)
7887
}
79-
88+
89+
func defaultArtboardViewModelAsync(artboardBy: ArtboardBy?) throws -> Promise<(any HybridViewModelSpec)?> {
90+
return Promise.async {
91+
try self.defaultArtboardViewModel(artboardBy: artboardBy)
92+
}
93+
}
94+
8095
var artboardCount: Double {
8196
Double(riveFile?.artboardNames().count ?? 0)
8297
}

0 commit comments

Comments
 (0)