rive-react-native icon indicating copy to clipboard operation
rive-react-native copied to clipboard

Working with Expo [docs]

Open thejustinwalsh opened this issue 3 years ago β€’ 27 comments

Description

Documentation for working with Expo is lacking a workflow for loading animations.

Details

When attempting to load an animation in expo I hit a wall relatively quickly regarding animation loading. Sharing my notes with anyone else who is using Rive in Expo.

Setup Metro Bundler

Add resolution for .riv files so that we may require references to the assets.

metro.config.js

const { getDefaultConfig } = require('expo/metro-config');

const defaultConfig = getDefaultConfig(__dirname);

defaultConfig.resolver.assetExts = [
  ...defaultConfig.resolver.assetExts,
  'riv'
];

module.exports = defaultConfig;

Use Expo Asset to load animations

Using the expo-asset module we can retrieve a full file URI to the animation that is bundled into our app, and load the animations using the url prop on the Rive component.

App.tsx

import { StatusBar } from 'expo-status-bar';
import { Image, StyleSheet, Text, View } from 'react-native';
import Rive, { Alignment, Fit } from 'rive-react-native';

import { useAssets } from 'expo-asset';

export default function App() {
  const [assets, error] = useAssets([require('./assets/slime.riv')]);
  const animationUrl = assets?.[0].localUri;
  
  return (
    <View style={styles.container}>
      {animationUrl && 
        <Rive url={animationUrl} fit={Fit.Contain} alignment={Alignment.Center} style={styles.animation} autoplay />
      }
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  animation: {
    width: 400,
    height: 400,
  }
});

thejustinwalsh avatar Jan 04 '22 00:01 thejustinwalsh

This worked for me

telliott22 avatar Apr 05 '22 07:04 telliott22

getting Error "RiveReactNativeView" was not found in a UI Manager

fullsnack-DEV avatar Jun 05 '22 07:06 fullsnack-DEV

"RiveReactNativeView" was not found in a UI Manager

same here

tom4k avatar Jun 21 '22 09:06 tom4k

Have you found any solution on it ?

fullsnack-DEV avatar Jul 13 '22 05:07 fullsnack-DEV

I think managed expo doesn't support rive-react-native yet

deathemperor avatar Aug 07 '22 06:08 deathemperor

Any update on this? Any solution to make it work with Expo managed? This is the only thing that it’s blocking me from using Expo managed.

hadnet avatar Aug 07 '22 18:08 hadnet

Seems eject is needed to do pod install step for ios environment

suiyi8760 avatar Aug 13 '22 10:08 suiyi8760

I thought I didn't do anything special but just checked out my demo project and I am using Expo SDK > 42 and used EAS to build a custom Expo Application for testing the native extensions. EAS did the work of giving me an Expo Go app with the native dependencies.

thejustinwalsh avatar Sep 20 '22 23:09 thejustinwalsh

Just to clarify for everyone, you don't need to "eject". You just need a custom dev client, made with expo prebuild and expo run:ios rather than Expo Go. Or, you can build the dev client/final app on EAS. As long as you don't use Expo Go, then you can use any native library with Expo.

nandorojo avatar Sep 29 '22 16:09 nandorojo

For what it's worth, expo prebuild didn't work for me, since I got a minimum deployment error. I fixed it by adding this plugin to my app.config.ts:

import { withPodfileProperties } from "@expo/config-plugins"

export default {
  plugins: [
    withPodfileProperties,
    (config) => {
      config.modResults = {
        ...config.modResults,
        "ios.deploymentTarget": "14.0",
      };
      return config;
    },
  ],
}

Then expo run:ios worked for me.

nandorojo avatar Sep 29 '22 16:09 nandorojo

Looks like @nandorojo solution dosn't work for me. I keep getting:

βœ– Config sync failed
TypeError: [ios.podfileProperties]: withIosPodfilePropertiesBaseMod: action is not a function
TypeError: [ios.podfileProperties]: withIosPodfilePropertiesBaseMod: action is not a function

But I did found a way around it which does work.

This is how my app.config.ts looks like:

const baseConfig = {
  name: 'app',
  slug: 'app',
  version: '1.0.0',
}

export default {
  ...baseConfig,
  mods: {
    ios: {
      podfileProperties: (config: { modResults: any }) => {
        config.modResults = {
          ...config.modResults,
          'ios.deploymentTarget': '14.0'
        }
        return config
      }
    }
  }
}

Then 'expo prebuild' and then expo run:ios and everything works for me.

thehobbit85 avatar Dec 16 '22 01:12 thehobbit85

good to know, this may have changed with sdk 47

nandorojo avatar Dec 16 '22 01:12 nandorojo

I was just about to add that my solution is tested on expo version 47.0.8. So yeah, you're probably right. Also, rive-react-native version 3.0.41.

thehobbit85 avatar Dec 16 '22 03:12 thehobbit85

@nandorojo @thehobbit85 you can also set the minimum deployment version using the expo-build-properties config plugin.

{
  "plugins": [  
    [
      "expo-build-properties",
      {
        "ios": { "deploymentTarget": "14.0"  }
      }
    ]
  ]
}

dmahajan980 avatar Jan 16 '23 19:01 dmahajan980

@nandorojo @thehobbit85 you can also set the minimum deployment version using the expo-build-properties config plugin.


{

  "plugins": [  

    [

      "expo-build-properties",

      {

        "ios": { "deploymentTarget": "14.0"  }

      }

    ]

  ]

}

This is the recommended approach now.

nandorojo avatar Jan 16 '23 19:01 nandorojo

Use Expo Asset to load animations

Using the expo-asset module we can retrieve a full file URI to the animation that is bundled into our app, and load the animations using the url prop on the Rive component.

App.tsx

import { StatusBar } from 'expo-status-bar';
import { Image, StyleSheet, Text, View } from 'react-native';
import Rive, { Alignment, Fit } from 'rive-react-native';

import { useAssets } from 'expo-asset';

export default function App() {
  const [assets, error] = useAssets([require('./assets/slime.riv')]);
  const animationUrl = assets?.[0].localUri;
  
  return (
    <View style={styles.container}>
      {animationUrl && 
        <Rive url={animationUrl} fit={Fit.Contain} alignment={Alignment.Center} style={styles.animation} autoplay />
      }
      <StatusBar style="auto" />
    </View>
  );
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#fff',
    alignItems: 'center',
    justifyContent: 'center',
  },
  animation: {
    width: 400,
    height: 400,
  }
});

Unfortunately, this does not work on Android.

dmahajan980 avatar Jan 21 '23 13:01 dmahajan980

Recommend the Rive team prioritizes seamless Expo managed workflow support. If it doesn't then it makes the lib unusable for like 50%+ of React Native projects

iway1 avatar Feb 08 '23 03:02 iway1

I'm able to make it work with expo custom dev client on ios, but don't you guys have this problem where the initial frame before playing the animation is not the actual first frame of the animation ?

perroudsky avatar Feb 10 '23 16:02 perroudsky

I'm able to make it work with expo custom dev client, but don't you guys have this problem where the initial frame before playing the animation is not the actual first frame of the animation ?

Hi there @perroudsky! Can you share the original/sample repo showing how you did this on Android?

dmahajan980 avatar Feb 10 '23 17:02 dmahajan980

Hi @dmahajan980, sorry i was talking about ios. Haven't tried on android yet

perroudsky avatar Feb 10 '23 17:02 perroudsky

If it doesn't then it makes the lib unusable for like 50%+ of React Native projects

Indeed, and maybe even more than 50% on newer projects. Nowadays Expo is the default way of using React native, so not supporting expo will really hamper rive's adoption on new projects.

sergioisidoro avatar Feb 18 '23 15:02 sergioisidoro

I came up with another strategy to integrate Rive animations into a React Native/Expo project. At the cost of a little bit of extra storage space and computation time, animations can be embedded into the JS bundle as base-64 encoded data-URI's.

Here's an example config using babel-plugin-inline-import-data-uri:

// babel.config.js
module.exports = (api) => {
  // ...
  return {
    plugins: [
      ['inline-import-data-uri', { extensions: ['.riv'] }]
    ]
  }
};
// App.tsx
import { View } from 'react-native';
import Rive from 'rive-react-native';

import MyAnimation from './my-animation.riv';

export default function App() {
  return (
    <View>
      <Rive
        autoplay
        url={MyAnimation}
        style={{ width: 400, height: 400 }}
      />
    </View>
  );
}

With this method, nothing needs to change in the metro configuration since it explicitly avoids using the RN/metro asset pipeline.


Sadly, just like the expo-asset method, this method only seems to work on iOS 😭 On Android, a native runtime error is logged:

[1047] NetworkDispatcher.processRequest: Unhandled exception java.lang.RuntimeException: Bad URL data:application/octet-stream;base64,...

The rive-react-native android implementation and the rive-android library both only support network protocols and not data:, because riveUrl is always processed with Android's Volley HTTP client, which is only meant to deploy network requests.

This might be the same issue that's causing the expo-asset method to fail on Android too, since it resolves to a file: protocol URL?


Update: I was able to work around this limitation on Android by patching the rive-react-native library to manually decode the data-URI:

diff --git a/android/src/main/java/com/rivereactnative/RiveReactNativeView.kt b/android/src/main/java/com/rivereactnative/RiveReactNativeView.kt
index 12a0d808a2abe4a8a1b887b1bc58614f4c73dd05..8d37080b94e2faa44e8613ab791fad4237891510 100644
--- a/android/src/main/java/com/rivereactnative/RiveReactNativeView.kt
+++ b/android/src/main/java/com/rivereactnative/RiveReactNativeView.kt
@@ -19,6 +19,7 @@ import com.facebook.react.bridge.ReadableArray
 import com.facebook.react.modules.core.ExceptionsManagerModule
 import com.facebook.react.uimanager.ThemedReactContext
 import java.io.UnsupportedEncodingException
+import java.util.Base64
 import kotlin.IllegalStateException
 
 
@@ -299,6 +300,24 @@ class RiveReactNativeView(private val context: ThemedReactContext) : FrameLayout
 
 
   private fun setUrlRiveResource(url: String, autoplay: Boolean = this.autoplay) {
+    if (url.startsWith("data:")) {
+      val b64data = url.substringAfter(',')
+      val decoder: Base64.Decoder = Base64.getDecoder()
+      val bytes = decoder.decode(b64data)
+
+      riveAnimationView.setRiveBytes(
+        bytes,
+        fit = this.fit,
+        alignment = this.alignment,
+        autoplay = autoplay,
+        stateMachineName = this.stateMachineName,
+        animationName = this.animationName,
+        artboardName = this.artboardName
+      )
+
+      return
+    }
+
     val queue = Volley.newRequestQueue(context)
     val stringRequest = RNRiveFileRequest(url, { bytes ->
       try {

Disclaimer: This patch is kind of quick and dirty - it could probably be cleaner and more error-safe.

nderscore avatar Apr 25 '23 17:04 nderscore

@nderscore Has this Android patch been holding up? If so, Would it be worth creating a PR for it?

tslater avatar Aug 15 '23 15:08 tslater

@tslater I actually haven't been using it. The project I want to use Rive on doesn't have any animations (yet πŸ˜…)

I also don't love that my workaround relies on parsing somewhat-large data URL's in JS and passing them across the JS bridge to the native side - it's probably not very performant.

At some point, I would like to try an alternative patch to add local file URL support to the android portion of the library, which I think should unlock a path to using expo-assets instead. I just haven't prioritized investigating that path yet.

nderscore avatar Aug 15 '23 17:08 nderscore

@dmahajan980 Do you know why this doesn't work on android? Is the native code just not set up for local file URLs?

tslater avatar Aug 25 '23 05:08 tslater

Has this issue been ignored by the Rive team so far? It looks like just because of it our team will stick with Lottie for a while.

It would be awesome to see Rive support for Expo out of the box.

enfipy avatar Oct 09 '23 14:10 enfipy

Completing the circular reference:

https://help.rive.app/runtimes/overview/react-native/loading-in-rive-files

I don't recall if resourceName was exposed or documented at the time of my initial request. Anyone on Android able to confirm that this method works?

expo-filesystem might be a better tool to use to get the paths to local assets here as well. I haven't tried this yet.

Lastly maybe having a ref method or prop that expects base64 encoded data is a solution that works as suggested above. expo-filesystem also exposes methods to return the riv file as a base64 encoded string through readAsStringAsync.

thejustinwalsh avatar Nov 27 '23 12:11 thejustinwalsh

@nderscore Fantastic! ty!

sugaith avatar Dec 06 '23 00:12 sugaith

as for me now. it's just make the app crash on ios and android, i already use prebuild

bryanprimus avatar Apr 19 '24 19:04 bryanprimus

Posting this here for visibility: Docs on how to get Rive working with Expo: https://rive.app/community/doc/adding-rive-to-expo/docFSwIlblYi

The suggestion for a normal react-native app on how to add Rive assets apply to expo as well: https://rive.app/community/doc/loading-in-rive-files/doc8P5oDeLlH

There is also a plugin that automatically brings in files. See this issue for more info: https://github.com/rive-app/rive-react-native/issues/185#issuecomment-1937039990

We can potentially add that to the package. Closing this issue as the above docs exist now, and we can track adding assets in the linked issue.

HayesGordon avatar May 15 '24 19:05 HayesGordon

@HayesGordon I'm not sure that I would consider this complete.

The documented solution requires a full rebuild of your native app (or your dev client) in order to add or update Rive assets. This also applies to the linked plugin.

Without explicit support for expo assets, it's missing two things that I would consider essential, as a developer building an app with Expo:

  • The ability to hot-reload rive animations during development
  • The ability to deploy Rive animations in an EAS Update without a native app update

Without being able to use Expo Assets, this is just a vanilla react native integration. This is a blocker for me being able to adopt Rive in my app.

nderscore avatar May 15 '24 19:05 nderscore