Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
26 changes: 26 additions & 0 deletions android/src/main/java/com/margelo/nitro/rive/HybridViewModel.kt
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,14 @@ class HybridViewModel(private val viewModel: ViewModel) : HybridViewModelSpec()
override val modelName: String
get() = viewModel.name

override val properties: Array<ViewModelPropertyInfo>
get() {
val mapped = viewModel.properties.map { prop ->
ViewModelPropertyInfo(name = prop.name, type = mapPropertyType(prop.type))
}
return mapped.toTypedArray()
}

override fun createInstanceByIndex(index: Double): HybridViewModelInstanceSpec? {
try {
val vmi = viewModel.createInstanceFromIndex(index.toInt())
Expand Down Expand Up @@ -50,4 +58,22 @@ class HybridViewModel(private val viewModel: ViewModel) : HybridViewModelSpec()
return null
}
}

companion object {
fun mapPropertyType(type: ViewModel.PropertyDataType): ViewModelPropertyType = when (type) {
ViewModel.PropertyDataType.NONE -> ViewModelPropertyType.NONE
ViewModel.PropertyDataType.STRING -> ViewModelPropertyType.STRING
ViewModel.PropertyDataType.NUMBER -> ViewModelPropertyType.NUMBER
ViewModel.PropertyDataType.BOOLEAN -> ViewModelPropertyType.BOOLEAN
ViewModel.PropertyDataType.COLOR -> ViewModelPropertyType.COLOR
ViewModel.PropertyDataType.LIST -> ViewModelPropertyType.LIST
ViewModel.PropertyDataType.ENUM -> ViewModelPropertyType.ENUM
ViewModel.PropertyDataType.TRIGGER -> ViewModelPropertyType.TRIGGER
ViewModel.PropertyDataType.VIEW_MODEL -> ViewModelPropertyType.VIEWMODEL
ViewModel.PropertyDataType.INTEGER -> ViewModelPropertyType.INTEGER
ViewModel.PropertyDataType.SYMBOL_LIST_INDEX -> ViewModelPropertyType.SYMBOLLISTINDEX
ViewModel.PropertyDataType.ASSET_IMAGE -> ViewModelPropertyType.ASSETIMAGE
ViewModel.PropertyDataType.ARTBOARD -> ViewModelPropertyType.ARTBOARD
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,10 @@ class HybridViewModelInstance(val viewModelInstance: ViewModelInstance) : Hybrid
override val instanceName: String
get() = viewModelInstance.name

// Note: Android SDK doesn't expose ViewModelInstance.properties publicly
override val properties: Array<ViewModelPropertyInfo>
get() = emptyArray()

// Returns null if ViewModelException is thrown for iOS parity
// (iOS SDK returns nil when property not found, Android SDK throws)
private inline fun <T> getPropertyOrNull(block: () -> T): T? {
Expand Down
128 changes: 128 additions & 0 deletions example/src/pages/PropertyInspector.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
import { View, Text, StyleSheet, ScrollView } from 'react-native';
import {
useRiveFile,
useViewModelInstance,
type ViewModelPropertyInfo,
type ViewModel,
} from '@rive-app/react-native';
import { useMemo } from 'react';
import type { Metadata } from '../helpers/metadata';

function PropertyList({ properties }: { properties: ViewModelPropertyInfo[] }) {
if (properties.length === 0) {
return <Text style={styles.emptyText}>No properties found</Text>;
}
return (
<View style={styles.propertyList}>
{properties.map((prop, index) => (
<View key={index} style={styles.propertyRow}>
<Text style={styles.propertyName}>{prop.name}</Text>
<Text style={styles.propertyType}>{prop.type}</Text>
</View>
))}
</View>
);
}

export default function PropertyInspector() {
const { riveFile } = useRiveFile(
require('../../assets/rive/quick_start.riv')
);

const viewModel: ViewModel | undefined = useMemo(
() => riveFile?.defaultArtboardViewModel(),
[riveFile]
);
const viewModelInstance = useViewModelInstance(riveFile);

return (
<ScrollView style={styles.container}>
<Text style={styles.title}>ViewModel Inspector</Text>

<View style={styles.section}>
<Text style={styles.sectionTitle}>ViewModel Info</Text>
<Text style={styles.info}>Name: {viewModel?.modelName ?? 'N/A'}</Text>
<Text style={styles.info}>
Property Count: {viewModel?.propertyCount ?? 0}
</Text>
<Text style={styles.info}>
Instance Count: {viewModel?.instanceCount ?? 0}
</Text>
</View>

<View style={styles.section}>
<Text style={styles.sectionTitle}>ViewModel Properties</Text>
<PropertyList properties={viewModel?.properties ?? []} />
</View>

<View style={styles.section}>
<Text style={styles.sectionTitle}>
Instance Properties ({viewModelInstance?.instanceName})
</Text>
<PropertyList properties={viewModelInstance?.properties ?? []} />
</View>
</ScrollView>
);
}

PropertyInspector.metadata = {
name: 'Property Inspector',
description:
'Debug tool to inspect ViewModel and ViewModelInstance properties',
} satisfies Metadata;

const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#1a1a2e',
},
title: {
fontSize: 24,
fontWeight: 'bold',
color: '#fff',
textAlign: 'center',
marginVertical: 20,
},
section: {
backgroundColor: '#16213e',
margin: 10,
padding: 15,
borderRadius: 10,
},
sectionTitle: {
fontSize: 18,
fontWeight: 'bold',
color: '#e94560',
marginBottom: 10,
},
info: {
fontSize: 14,
color: '#fff',
marginBottom: 5,
},
propertyList: {
marginTop: 5,
},
propertyRow: {
flexDirection: 'row',
justifyContent: 'space-between',
paddingVertical: 8,
borderBottomWidth: 1,
borderBottomColor: '#0f3460',
},
propertyName: {
fontSize: 14,
color: '#fff',
fontWeight: '500',
},
propertyType: {
fontSize: 14,
color: '#e94560',
fontFamily: 'monospace',
},
emptyText: {
fontSize: 14,
color: '#666',
fontStyle: 'italic',
},
});
1 change: 1 addition & 0 deletions example/src/pages/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export { default as ManyViewModels } from './ManyViewModels';
export { default as ResponsiveLayouts } from './ResponsiveLayouts';
export { default as SharedValueListenerExample } from './SharedValueListenerExample';
export { default as MenuListExample } from './MenuListExample';
export { default as PropertyInspector } from './PropertyInspector';
40 changes: 37 additions & 3 deletions ios/HybridViewModel.swift
Original file line number Diff line number Diff line change
@@ -1,18 +1,52 @@
import RiveRuntime

extension ViewModelPropertyType {
init(from type: RiveDataBindingViewModel.Instance.Property.Data.DataType) {
switch type {
case .none: self = .none
case .string: self = .string
case .number: self = .number
case .boolean: self = .boolean
case .color: self = .color
case .list: self = .list
case .enum: self = .enum
case .trigger: self = .trigger
case .viewModel: self = .viewmodel
case .integer: self = .integer
case .symbolListIndex: self = .symbollistindex
case .assetImage: self = .assetimage
case .artboard: self = .artboard
case .input: self = .input
case .any: self = .any
@unknown default: self = .none
}
}
}

class HybridViewModel: HybridViewModelSpec {
let viewModel: RiveDataBindingViewModel?

init(viewModel: RiveDataBindingViewModel) {
self.viewModel = viewModel
}

var propertyCount: Double { Double(viewModel?.propertyCount ?? 0) }

var instanceCount: Double { Double(viewModel?.instanceCount ?? 0) }

var modelName: String { viewModel?.name ?? "" }

var properties: [ViewModelPropertyInfo] {
guard let vm = viewModel else { return [] }
return vm.properties.map { prop in
ViewModelPropertyInfo(
name: prop.name,
type: ViewModelPropertyType(from: prop.type)
)
}
}


Check warning on line 49 in ios/HybridViewModel.swift

View workflow job for this annotation

GitHub Actions / lint-swift

Limit vertical whitespace to a single empty line; currently 2 (vertical_whitespace)
func createInstanceByIndex(index: Double) throws -> (any HybridViewModelInstanceSpec)? {
guard let viewModel = viewModel,
let vmi = viewModel.createInstance(fromIndex: UInt(index)) else { return nil }
Expand Down
12 changes: 11 additions & 1 deletion ios/HybridViewModelInstance.swift
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,22 @@ import RiveRuntime

class HybridViewModelInstance: HybridViewModelInstanceSpec {
let viewModelInstance: RiveDataBindingViewModel.Instance?

init(viewModelInstance: RiveDataBindingViewModel.Instance) {
self.viewModelInstance = viewModelInstance
}

var instanceName: String { viewModelInstance?.name ?? "" }

var properties: [ViewModelPropertyInfo] {
guard let instance = viewModelInstance else { return [] }
return instance.properties.map { prop in
ViewModelPropertyInfo(
name: prop.name,
type: ViewModelPropertyType(from: prop.type)
)
}
}

func numberProperty(path: String) throws -> (any HybridViewModelNumberPropertySpec)? {
guard let property = viewModelInstance?.numberProperty(fromPath: path) else { return nil }
Expand Down
23 changes: 23 additions & 0 deletions nitrogen/generated/android/c++/JHybridViewModelInstanceSpec.cpp

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

23 changes: 23 additions & 0 deletions nitrogen/generated/android/c++/JHybridViewModelSpec.cpp

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions nitrogen/generated/android/c++/JHybridViewModelSpec.hpp

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading
Loading