Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Out of band assets #285

Merged
merged 19 commits into from
Feb 5, 2025
Merged
Show file tree
Hide file tree
Changes from 5 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
22 changes: 21 additions & 1 deletion CONTRIBUTING.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,26 @@ Follow dependency installation instructions from the [React Native docs here](ht
export JAVA_HOME=/Library/Java/JavaVirtualMachines/adoptopenjdk-11.jdk/Contents/Home`
```

### Testing in a new Expo project
1. In rive-react-native run npm pack which will create rive-react-native-x.x.x.tgz
2. Create a new expo project and prebuild (formerly eject) it: https://docs.expo.dev/
3. In Podfile set the minimum platform to 14: platform :ios, podfile_properties['ios.deploymentTarget'] || '14.0'
4. Add this to your package.json dependencies: `"rive-react-native": "file:../rive-react-native/rive-react-native-8.0.0.tgz"` (adjust the path if needed) and run `npm i`
5. Add a Rive component index.tsx and run npm run start:

```js
import Rive, { Alignment, Fit, RiveRef } from 'rive-react-native'
...
<Rive
url='https://cdn.rive.app/animations/vehicles.riv'
fit={Fit.Contain}
alignment={Alignment.Center}
artboardName={'Truck'}
style={{ width: 200, height: 200 }}
autoplay={true}
/>
```

### M1 considerations

If you're running on an M1, you may need to run terminal-based commands off [Rosetta](https://www.courier.com/blog/tips-and-tricks-to-setup-your-apple-m1-for-development/) as it may not support new M1 architecture.
Expand Down Expand Up @@ -121,7 +141,7 @@ pod update RiveRuntime
In the top-level `android` folder, change the dependency version in `build.gradle` to the version you're looking to upgrade to in the dependencies:

```
implementation 'app.rive:rive-android:8.2.1'
implementation 'app.rive:rive-android:x.x.x'
```

### Debugging against local rive-android
Expand Down
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,13 @@ Check out the `example/` folder to run an example application using the Rive Rea
- Displaying single or multiple animations / artboards on one component
- Setting up and maniuplating a state machine via inputs
- ...and more!

Steps:
1. Run yarn bootstrap
2. Run yarn example start

iOS:
- You may need to run `pod install` (first time) or `pod update RiveRuntime` (updates to the underlying Rive iOS runtime) in the `example/iOS` folder to get the runtime installed or updated

## Migration Guides

Expand Down
Binary file added example/ios/Assets/Inter-594377.ttf
Binary file not shown.
Binary file added example/ios/Assets/out_of_band.riv
Binary file not shown.
Binary file added example/ios/Assets/referenced-image-2929282.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/ios/Assets/referenced_audio-2929340.wav
Binary file not shown.
18 changes: 18 additions & 0 deletions example/ios/RiveReactNativeExample.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,11 @@
E5A17A90299AA0F5008CC433 /* avatars.riv in Resources */ = {isa = PBXBuildFile; fileRef = E5A17A8F299AA0F5008CC433 /* avatars.riv */; };
E5F3FC4029B2661500D6D265 /* switch.riv in Resources */ = {isa = PBXBuildFile; fileRef = E5F3FC3F29B2661500D6D265 /* switch.riv */; };
E5FC4EAA2ABB975100D98158 /* rating.riv in Resources */ = {isa = PBXBuildFile; fileRef = E5FC4EA92ABB975100D98158 /* rating.riv */; };
F80BCFEF2D30018300BDCF64 /* out_of_band.riv in Resources */ = {isa = PBXBuildFile; fileRef = F80BCFEE2D30018300BDCF64 /* out_of_band.riv */; };
F80BCFF02D30018300BDCF64 /* out_of_band.riv in Resources */ = {isa = PBXBuildFile; fileRef = F80BCFEE2D30018300BDCF64 /* out_of_band.riv */; };
F80BCFF22D30092A00BDCF64 /* Inter-594377.ttf in Resources */ = {isa = PBXBuildFile; fileRef = F80BCFF12D30092A00BDCF64 /* Inter-594377.ttf */; };
F80BCFF42D30093100BDCF64 /* referenced_audio-2929340.wav in Resources */ = {isa = PBXBuildFile; fileRef = F80BCFF32D30093100BDCF64 /* referenced_audio-2929340.wav */; };
F80BCFF62D30093B00BDCF64 /* referenced-image-2929282.png in Resources */ = {isa = PBXBuildFile; fileRef = F80BCFF52D30093B00BDCF64 /* referenced-image-2929282.png */; };
F8AA4CA42C0F3FDB00C1A5FF /* runtime_nested_inputs.riv in Resources */ = {isa = PBXBuildFile; fileRef = F8AA4CA32C0F3FDB00C1A5FF /* runtime_nested_inputs.riv */; };
F8AA4CA52C0F3FDB00C1A5FF /* runtime_nested_inputs.riv in Resources */ = {isa = PBXBuildFile; fileRef = F8AA4CA32C0F3FDB00C1A5FF /* runtime_nested_inputs.riv */; };
/* End PBXBuildFile section */
Expand Down Expand Up @@ -111,6 +116,10 @@
E5FC4EA92ABB975100D98158 /* rating.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = rating.riv; sourceTree = "<group>"; };
ED297162215061F000B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = System/Library/Frameworks/JavaScriptCore.framework; sourceTree = SDKROOT; };
ED2971642150620600B7C4FE /* JavaScriptCore.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = JavaScriptCore.framework; path = Platforms/AppleTVOS.platform/Developer/SDKs/AppleTVOS12.0.sdk/System/Library/Frameworks/JavaScriptCore.framework; sourceTree = DEVELOPER_DIR; };
F80BCFEE2D30018300BDCF64 /* out_of_band.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = out_of_band.riv; sourceTree = "<group>"; };
F80BCFF12D30092A00BDCF64 /* Inter-594377.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = "Inter-594377.ttf"; sourceTree = "<group>"; };
F80BCFF32D30093100BDCF64 /* referenced_audio-2929340.wav */ = {isa = PBXFileReference; lastKnownFileType = audio.wav; path = "referenced_audio-2929340.wav"; sourceTree = "<group>"; };
F80BCFF52D30093B00BDCF64 /* referenced-image-2929282.png */ = {isa = PBXFileReference; lastKnownFileType = image.png; path = "referenced-image-2929282.png"; sourceTree = "<group>"; };
F8AA4CA32C0F3FDB00C1A5FF /* runtime_nested_inputs.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = runtime_nested_inputs.riv; sourceTree = "<group>"; };
F8E2789D2CDCD6A200FAA8EF /* layouts_demo.riv */ = {isa = PBXFileReference; lastKnownFileType = file; path = layouts_demo.riv; sourceTree = "<group>"; };
/* End PBXFileReference section */
Expand Down Expand Up @@ -261,6 +270,10 @@
9D4FE60A26493B460098BF6A /* Assets */ = {
isa = PBXGroup;
children = (
F80BCFF52D30093B00BDCF64 /* referenced-image-2929282.png */,
F80BCFF32D30093100BDCF64 /* referenced_audio-2929340.wav */,
F80BCFF12D30092A00BDCF64 /* Inter-594377.ttf */,
F80BCFEE2D30018300BDCF64 /* out_of_band.riv */,
F8E2789D2CDCD6A200FAA8EF /* layouts_demo.riv */,
C3C07471283BE07300E8EB33 /* hero_editor.riv */,
E554409A2A79DC8100D550DE /* hello_world_text.riv */,
Expand Down Expand Up @@ -417,6 +430,7 @@
buildActionMask = 2147483647;
files = (
372D10392CEC2851008B4896 /* hello_world_nested.riv in Resources */,
F80BCFF02D30018300BDCF64 /* out_of_band.riv in Resources */,
F8AA4CA52C0F3FDB00C1A5FF /* runtime_nested_inputs.riv in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
Expand All @@ -428,16 +442,20 @@
9D879D00265642BA00D01424 /* truck_v7.riv in Resources */,
E5FC4EAA2ABB975100D98158 /* rating.riv in Resources */,
042FD22726B81BD1004556A3 /* constrained.riv in Resources */,
F80BCFF42D30093100BDCF64 /* referenced_audio-2929340.wav in Resources */,
9D879D0B26578A5E00D01424 /* artboard_animations.riv in Resources */,
F8AA4CA42C0F3FDB00C1A5FF /* runtime_nested_inputs.riv in Resources */,
E5637D7A292BD27F000CBC1E /* skills_listener.riv in Resources */,
372D10382CEC2851008B4896 /* hello_world_nested.riv in Resources */,
9EBE42F22CDD459200014668 /* layout_test.riv in Resources */,
F80BCFF62D30093B00BDCF64 /* referenced-image-2929282.png in Resources */,
F80BCFF22D30092A00BDCF64 /* Inter-594377.ttf in Resources */,
04A886F326A990050078530A /* two_bone_ik.riv in Resources */,
81AB9BB82411601600AC10FF /* LaunchScreen.storyboard in Resources */,
9DBF1CC52684937E0008391A /* v6_file.riv in Resources */,
9D4FE6122649427F0098BF6A /* bird.riv in Resources */,
E554409B2A79DC8100D550DE /* hello_world_text.riv in Resources */,
F80BCFEF2D30018300BDCF64 /* out_of_band.riv in Resources */,
9D879D0D265BF2A400D01424 /* ui_swipe_left_to_delete.riv in Resources */,
E5A17A90299AA0F5008CC433 /* avatars.riv in Resources */,
9D879D0926578A5400D01424 /* loopy.riv in Resources */,
Expand Down
2 changes: 2 additions & 0 deletions example/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import DynamicText from './DynamicText';
import NestedDynamicText from './NestedDynamicText';
import NestedInputs from './NestedInputs';
import Events from './Events';
import OutOfBandAssets from './OutOfBandAssets';

import {
RiveRenderer,
Expand Down Expand Up @@ -66,6 +67,7 @@ function App() {
<Stack.Screen name="Events" component={Events} />
<Stack.Screen name="NestedInputs" component={NestedInputs} />
<Stack.Screen name="DynamicText" component={DynamicText} />
<Stack.Screen name="OutOfBandAssets" component={OutOfBandAssets} />
<Stack.Screen
name="NestedDynamicText"
component={NestedDynamicText}
Expand Down
8 changes: 8 additions & 0 deletions example/src/Home.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,14 @@ export default function Home({ navigation }) {
Dynamic Text
</Button>

<Button
mode="contained"
onPress={() => navigation.navigate('OutOfBandAssets')}
style={styles.buttonStyle}
>
Out of Band Assets
</Button>

<Button
mode="contained"
onPress={() => navigation.navigate('NestedDynamicText')}
Expand Down
82 changes: 82 additions & 0 deletions example/src/OutOfBandAssets.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
import * as React from 'react';
import { SafeAreaView, ScrollView, StyleSheet, Text } from 'react-native';
import Rive, { Fit, RNRiveError } from 'rive-react-native';

export default function StateMachine() {
return (
<SafeAreaView style={styles.safeAreaViewContainer}>
<ScrollView contentContainerStyle={styles.container}>
<Rive
autoplay={true}
fit={Fit.Contain}
style={styles.box}
stateMachineName="State Machine 1"
// You can use the `assetsHandled` prop to load in external assets from a URI
// or bundled asset on the native platform (iOS and Android)
// or as a source loaded directly from JavaScript.
//
// Below demonstrates various ways to load in the same asset
// located in different places. Its not needed to store the same
// asset in all these locations, but this example does that for
// demonstration purposes.
//
// The key of the map is the unique asset identifier (as exported in the Editor),
// which is a combination of the asset name and its unique identifier.
assetsHandled={{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What's your thinking on the name assetsHandled? My first thought would be to call this referencedAssets to align it with the naming in the editor, but maybe that's too restricting.

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I've also been toying with thinking of a different name for this.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I like referencedAssets as a name!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

'Inter-594377': {
source: require('./assets/Inter-594377.ttf'),
// source: {
// fileName: 'Inter-594377.ttf',
// path: 'fonts', // only needed for Android assets
// },
},
'referenced-image-2929282': {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Though I'd prefer not to need the ID, I think using it by default is probably necessary, given the fact that it's possible to have multiple assets with the same name. I could see users being very confused when both fonts named Tomorrow display the same even though one of them is actually supposed to be a different weight.

I really like the idea of being able to override the default behavior with includeId. Do you have thoughts on that being a global attribute, rather than on a per-asset basis?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For the same fonts (or asset) we will use the same ID! You also have the option to rename these to anything you'd like.

Expanding this APIs ability is something we can also add in the future, if needed.

source: require('./assets/referenced-image-2929282.png'),
// source: {
// uri: 'https://picsum.photos/id/270/500/500',
// },
// source: {
// fileName: 'referenced-image-2929282.png',
// path: 'images', // only needed for Android assets
// },
},
'referenced_audio-2929340': {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

There's no scenario where the asset IDs might change, is there? For example, if the Rive file gets copied or if you reimport a PSD? That could be painful if you have a giant object with ID's included.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That's why the approach for the other runtimes are a callback mechanism where you can read the name + id and query the file. That's not possible for React Native at this stage, which is why this API is different.

The best way to circumvent this is to provide a "name" only fallback and forgo the id

source: require('./assets/referenced_audio-2929340.wav'),
// source: {
// fileName: 'referenced_audio-2929340.wav',
// path: 'audio', // only needed for Android assets
// },
},
}}
artboardName="Artboard"
resourceName={'out_of_band'}
onError={(riveError: RNRiveError) => {
console.log(riveError);
}}
/>
<Text>
Load in an external asset from a URL, or bundled asset on the native
platform, or as a source loaded directly from JavaScript.
</Text>
</ScrollView>
</SafeAreaView>
);
}

const styles = StyleSheet.create({
safeAreaViewContainer: {
flex: 1,
},
container: {
flexGrow: 1,
alignItems: 'center',
justifyContent: 'center',
marginBottom: 150,
padding: 10,
},
box: {
width: '100%',
height: 500,
marginVertical: 20,
},
});
Binary file added example/src/assets/Inter-594377.ttf
Binary file not shown.
Binary file added example/src/assets/referenced-image-2929282.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added example/src/assets/referenced_audio-2929340.wav
Binary file not shown.
4 changes: 4 additions & 0 deletions ios/RNRiveError.swift
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,10 @@ func createMalformedFileError() -> NSError {
return NSError(domain: RiveErrorDomain, code: RiveErrorCode.malformedFile.rawValue, userInfo: [NSLocalizedDescriptionKey: "Malformed Rive File", "name": "Malformed"])
}

func createAssetFileError(_ assetName: String) -> NSError {
return NSError(domain: RiveErrorDomain, code: RiveErrorCode.malformedFile.rawValue, userInfo: [NSLocalizedDescriptionKey: "Could not load Rive asset: \(assetName)", "name": "Malformed"])
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nitpicky, but I would maybe use .fileNotFound rather than malformed as the error code.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done

}

func createIncorrectRiveURL(_ url: String) -> NSError {
return NSError(domain: RiveErrorDomain, code: 900, userInfo: [NSLocalizedDescriptionKey: "Unable to download Rive file from: \(url)", "name": "IncorrectRiveFileURL"])
}
Loading
Loading