├── .editorconfig ├── .gitattributes ├── .github ├── ISSUE_TEMPLATE │ └── i-have-a-problem-with-rive-react-native.md └── workflows │ ├── add_to_project.yml │ ├── publish-prerelease.yml │ ├── publish-release.yml │ └── typecheck-lint.yml ├── .gitignore ├── .watchmanconfig ├── .yarn ├── plugins │ └── @yarnpkg │ │ ├── plugin-interactive-tools.cjs │ │ └── plugin-workspace-tools.cjs └── releases │ └── yarn-4.7.0.cjs ├── .yarnrc ├── .yarnrc.yml ├── Backup.swift ├── CONTRIBUTING.md ├── LICENSE ├── README.md ├── android ├── .project ├── build.gradle ├── gradle.properties ├── gradle │ └── wrapper │ │ ├── gradle-wrapper.jar │ │ └── gradle-wrapper.properties ├── gradlew ├── gradlew.bat └── src │ └── main │ ├── AndroidManifest.xml │ └── java │ └── com │ └── rivereactnative │ ├── RNAlignment.kt │ ├── RNDirection.kt │ ├── RNFit.kt │ ├── RNLoopMode.kt │ ├── RNPropertyType.kt │ ├── RNRiveError.kt │ ├── RNRiveRendererType.kt │ ├── RiveReactNativeModule.kt │ ├── RiveReactNativePackage.kt │ ├── RiveReactNativeRendererModule.kt │ ├── RiveReactNativeView.kt │ └── RiveReactNativeViewManager.kt ├── babel.config.js ├── docs ├── rive-react-native-reference.md ├── types.md └── usage-guide.md ├── example ├── .gitignore ├── README.md ├── android │ ├── .gitignore │ ├── app │ │ ├── build.gradle │ │ ├── debug.keystore │ │ ├── proguard-rules.pro │ │ └── src │ │ │ ├── debug │ │ │ └── AndroidManifest.xml │ │ │ └── main │ │ │ ├── AndroidManifest.xml │ │ │ ├── java │ │ │ └── com │ │ │ │ └── anonymous │ │ │ │ └── example │ │ │ │ ├── MainActivity.kt │ │ │ │ └── MainApplication.kt │ │ │ └── res │ │ │ ├── Inter-594377.ttf │ │ │ ├── drawable-hdpi │ │ │ └── splashscreen_logo.png │ │ │ ├── drawable-mdpi │ │ │ └── splashscreen_logo.png │ │ │ ├── drawable-xhdpi │ │ │ └── splashscreen_logo.png │ │ │ ├── drawable-xxhdpi │ │ │ └── splashscreen_logo.png │ │ │ ├── drawable-xxxhdpi │ │ │ └── splashscreen_logo.png │ │ │ ├── drawable │ │ │ ├── ic_launcher_background.xml │ │ │ └── rn_edit_text_material.xml │ │ │ ├── mipmap-anydpi-v26 │ │ │ ├── ic_launcher.xml │ │ │ └── ic_launcher_round.xml │ │ │ ├── mipmap-hdpi │ │ │ ├── ic_launcher.webp │ │ │ ├── ic_launcher_foreground.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-mdpi │ │ │ ├── ic_launcher.webp │ │ │ ├── ic_launcher_foreground.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xhdpi │ │ │ ├── ic_launcher.webp │ │ │ ├── ic_launcher_foreground.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xxhdpi │ │ │ ├── ic_launcher.webp │ │ │ ├── ic_launcher_foreground.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── mipmap-xxxhdpi │ │ │ ├── ic_launcher.webp │ │ │ ├── ic_launcher_foreground.webp │ │ │ └── ic_launcher_round.webp │ │ │ ├── raw │ │ │ ├── artboard_animations.riv │ │ │ ├── avatars.riv │ │ │ ├── bird.riv │ │ │ ├── constrained.riv │ │ │ ├── flying_car.riv │ │ │ ├── hello_world_nested.riv │ │ │ ├── hello_world_text.riv │ │ │ ├── hero_editor.riv │ │ │ ├── layout_test.riv │ │ │ ├── nested_inputs.riv │ │ │ ├── nested_menu.riv │ │ │ ├── out_of_band.riv │ │ │ ├── rainy.riv │ │ │ ├── rating.riv │ │ │ ├── rewards.riv │ │ │ ├── skills.riv │ │ │ ├── skills_listener.riv │ │ │ ├── truck_v7.riv │ │ │ ├── two_bone_ik.riv │ │ │ ├── ui_swipe_left_to_delete.riv │ │ │ └── v6_file.riv │ │ │ ├── referenced-image-2929282.png │ │ │ ├── referenced_audio-2929340.wav │ │ │ ├── values-night │ │ │ └── colors.xml │ │ │ └── values │ │ │ ├── colors.xml │ │ │ ├── strings.xml │ │ │ └── styles.xml │ ├── build.gradle │ ├── gradle.properties │ ├── gradle │ │ └── wrapper │ │ │ ├── gradle-wrapper.jar │ │ │ └── gradle-wrapper.properties │ ├── gradlew │ ├── gradlew.bat │ └── settings.gradle ├── app.json ├── app │ ├── (examples) │ │ ├── DataBinding.tsx │ │ ├── DynamicText.tsx │ │ ├── ErrorHandledManually.tsx │ │ ├── ErrorNotHandled.tsx │ │ ├── Events.tsx │ │ ├── Http.tsx │ │ ├── Layout.tsx │ │ ├── MeshExample.tsx │ │ ├── MultipleArtboards.tsx │ │ ├── NestedDynamicText.tsx │ │ ├── NestedInputs.tsx │ │ ├── OutOfBandAssets.tsx │ │ ├── ResponsiveLayout.tsx │ │ ├── Simple.tsx │ │ ├── SimpleControls.tsx │ │ ├── SourceProp.tsx │ │ └── StateMachine.tsx │ ├── +not-found.tsx │ ├── _layout.tsx │ └── index.tsx ├── assets │ ├── audio │ │ └── referenced_audio-2929340.wav │ ├── fonts │ │ ├── Inter-594377.ttf │ │ └── SpaceMono-Regular.ttf │ ├── images │ │ ├── adaptive-icon.png │ │ ├── favicon.png │ │ ├── icon.png │ │ ├── partial-react-logo.png │ │ ├── react-logo.png │ │ ├── react-logo@2x.png │ │ ├── react-logo@3x.png │ │ ├── referenced-image-2929282.png │ │ └── splash-icon.png │ └── rive │ │ └── avatars.riv ├── components │ ├── Themed.tsx │ ├── ThemedText.tsx │ └── ThemedView.tsx ├── constants │ ├── Colors.ts │ └── typesPredicates.ts ├── hooks │ ├── useColorScheme.ts │ ├── useColorScheme.web.ts │ └── useThemeColor.ts ├── ios │ ├── .gitignore │ ├── .xcode.env │ ├── Assets │ │ ├── FollowPathCar.riv │ │ ├── Inter-594377.ttf │ │ ├── artboard_animations.riv │ │ ├── avatars.riv │ │ ├── bird.riv │ │ ├── constrained.riv │ │ ├── hello_world_nested.riv │ │ ├── hello_world_text.riv │ │ ├── hero_editor.riv │ │ ├── layout_test.riv │ │ ├── nested_inputs.riv │ │ ├── nested_menu.riv │ │ ├── out_of_band.riv │ │ ├── rainy.riv │ │ ├── rating.riv │ │ ├── referenced-image-2929282.png │ │ ├── referenced_audio-2929340.wav │ │ ├── skills_listener.riv │ │ ├── switch.riv │ │ ├── truck_v7.riv │ │ ├── two_bone_ik.riv │ │ ├── ui_swipe_left_to_delete.riv │ │ └── v6_file.riv │ ├── Podfile │ ├── Podfile.lock │ ├── Podfile.properties.json │ ├── example.xcodeproj │ │ ├── project.pbxproj │ │ └── xcshareddata │ │ │ └── xcschemes │ │ │ └── example.xcscheme │ ├── example.xcworkspace │ │ ├── contents.xcworkspacedata │ │ └── xcshareddata │ │ │ └── IDEWorkspaceChecks.plist │ ├── example │ │ ├── AppDelegate.h │ │ ├── AppDelegate.mm │ │ ├── Images.xcassets │ │ │ ├── AppIcon.appiconset │ │ │ │ ├── App-Icon-1024x1024@1x.png │ │ │ │ └── Contents.json │ │ │ ├── Contents.json │ │ │ ├── SplashScreenBackground.colorset │ │ │ │ └── Contents.json │ │ │ └── SplashScreenLogo.imageset │ │ │ │ ├── Contents.json │ │ │ │ ├── image.png │ │ │ │ ├── image@2x.png │ │ │ │ └── image@3x.png │ │ ├── Info.plist │ │ ├── PrivacyInfo.xcprivacy │ │ ├── SplashScreen.storyboard │ │ ├── Supporting │ │ │ └── Expo.plist │ │ ├── example-Bridging-Header.h │ │ ├── example.entitlements │ │ ├── main.m │ │ └── noop-file.swift │ └── rewards.riv ├── metro.config.js ├── package.json ├── scripts │ └── reset-project.js └── tsconfig.json ├── ios ├── RCTLog.swift ├── RCTSwiftLog.h ├── RCTSwiftLog.m ├── RNAlignment.swift ├── RNDirection.swift ├── RNFit.swift ├── RNLoopMode.swift ├── RNPropertyType.swift ├── RNRiveError.swift ├── RNRiveRendererType.swift ├── RiveReactNative-Bridging-Header.h ├── RiveReactNative.xcodeproj │ └── project.pbxproj ├── RiveReactNativeEventModule.h ├── RiveReactNativeEventModule.m ├── RiveReactNativeModule.swift ├── RiveReactNativeModuleBridge.m ├── RiveReactNativeRendererModule.swift ├── RiveReactNativeRendererModuleBridge.m ├── RiveReactNativeView.swift ├── RiveReactNativeViewManager.m ├── RiveReactNativeViewManager.swift └── utility.swift ├── package.json ├── rive-react-native.podspec ├── scripts ├── add_build_number.sh └── bootstrap.js ├── src ├── Rive.tsx ├── __tests__ │ └── index.test.tsx ├── helpers.ts ├── index.tsx ├── types.ts └── utils.ts ├── tsconfig.build.json ├── tsconfig.json ├── version.txt └── yarn.lock /.editorconfig: -------------------------------------------------------------------------------- 1 | # EditorConfig helps developers define and maintain consistent 2 | # coding styles between different editors and IDEs 3 | # editorconfig.org 4 | 5 | root = true 6 | 7 | [*] 8 | 9 | indent_style = space 10 | indent_size = 2 11 | 12 | end_of_line = lf 13 | charset = utf-8 14 | trim_trailing_whitespace = true 15 | insert_final_newline = true 16 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | *.pbxproj -text 2 | # specific for windows script files 3 | *.bat text eol=crlf -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/i-have-a-problem-with-rive-react-native.md: -------------------------------------------------------------------------------- 1 | --- 2 | name: I have a problem with rive-react-native 3 | about: Tell us what's wrong 4 | title: '' 5 | labels: '' 6 | assignees: '' 7 | 8 | --- 9 | 10 | <-- Thanks for using Rive! 11 | 12 | If something isn't working like you expected, please: 13 | - Make sure you've updated rive-react-native to the latest version: 14 | https://www.npmjs.com/package/rive-react-native 15 | - Check out our help center 16 | https://help.rive.app/ 17 | - You can file a new feature request at 18 | https://feedback.rive.app/ 19 | 20 | If you think you found a bug or if you can't find what you're looking for 21 | in our docs, fill out the template below. 22 | 23 | --> 24 | 25 | ### Description 26 | <-- A clear and concise description of what the problem is. --> 27 | 28 | ### Provide a Repro 29 | <-- 30 | Please provide the most **minimal repro** you can: 31 | - If the problem is small enough, paste a code snippet 32 | - Otherwise, you can attach a ZIP archive or paste a link to a GitHub repository with the bundle that reproduces the issue 33 | 34 | --> 35 | 36 | #### Source `.riv`/`.rev` file 37 | <-- 38 | Attaching runtime-exported `.riv` file that reproduces the issue is immensely helpful for our team to identify the problem as fast as possible and fix it. 39 | Please also include the source (`.rev`) by [downloading a backup](https://help.rive.app/editor/exporting#for-backup) from the Editor. 40 | 41 | N.B. Because GitHub only supports certain file types use a ZIP archive to upload `.riv`/`.rev` files to this issue. 42 | 43 | You can also privately share the `.riv`/`.rev` file with us at hello@rive.app. 44 | --> 45 | 46 | ### Expected behavior 47 | <-- A clear and concise description of what you expected to happen. --> 48 | 49 | #### Screenshots 50 | <-- If applicable, add screenshots to help explain your problem. --> 51 | 52 | ![Screenshot](url) 53 | ### Device & Versions (please complete the following information) 54 | - Device: [e.g. Android Emulator, iOS Simulator] 55 | - OS: [e.g. Android SDK API Level 30, iOS 14.5] 56 | - NPM Version: [Copy-paste the output of `npm --version` e.g.: `7.10.2`] 57 | 58 | #### Additional context 59 | <-- Add any other context about the problem here. --> 60 | -------------------------------------------------------------------------------- /.github/workflows/add_to_project.yml: -------------------------------------------------------------------------------- 1 | name: Adds all new issues to project board 2 | 3 | on: 4 | issues: 5 | types: 6 | - opened 7 | 8 | jobs: 9 | add-to-project: 10 | name: Add issue to project 11 | runs-on: ubuntu-latest 12 | steps: 13 | - uses: actions/add-to-project@v0.5.0 14 | with: 15 | project-url: https://github.com/orgs/rive-app/projects/12/views/1 16 | github-token: ${{ secrets.ADD_TO_PROJECT_ACTION }} 17 | 18 | - uses: actions/github-script@v6 19 | with: 20 | script: | 21 | github.rest.issues.addLabels({ 22 | issue_number: context.issue.number, 23 | owner: context.repo.owner, 24 | repo: context.repo.repo, 25 | labels: ["triage"] 26 | }) 27 | -------------------------------------------------------------------------------- /.github/workflows/publish-prerelease.yml: -------------------------------------------------------------------------------- 1 | name: Publish Rive React Native pre-release 2 | on: 3 | workflow_dispatch: 4 | jobs: 5 | publish: 6 | name: Install and publish 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v4 11 | with: 12 | fetch-depth: 0 13 | 14 | - name: Git config 15 | run: | 16 | git config --local user.email 'hello@rive.app' 17 | git config --local user.name ${{ github.actor }} 18 | 19 | - name: Setup Node.js 20 | uses: actions/setup-node@v4 21 | with: 22 | node-version: 18.x 23 | 24 | - name: Enable Corepack and Use Correct Yarn Version 25 | run: | 26 | corepack enable 27 | corepack prepare yarn@4.7.0 --activate 28 | 29 | - name: Restore dependencies 30 | id: yarn-cache 31 | uses: actions/cache/restore@v4 32 | with: 33 | path: | 34 | **/node_modules 35 | .yarn/install-state.gz 36 | key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}-${{ hashFiles('**/package.json', '!node_modules/**') }} 37 | restore-keys: | 38 | ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} 39 | ${{ runner.os }}-yarn- 40 | 41 | - name: Debug Yarn State (Optional) 42 | run: yarn install --check-cache 43 | 44 | - name: Install dependencies 45 | if: steps.yarn-cache.outputs.cache-hit != 'true' 46 | run: yarn install --immutable 47 | shell: bash 48 | 49 | - name: Cache dependencies 50 | if: steps.yarn-cache.outputs.cache-hit != 'true' 51 | uses: actions/cache/save@v4 52 | with: 53 | path: | 54 | **/node_modules 55 | .yarn/install-state.gz 56 | key: ${{ steps.yarn-cache.outputs.cache-primary-key }} 57 | 58 | - name: Lint 59 | run: yarn lint 60 | 61 | - name: Type Check 62 | run: yarn typescript 63 | 64 | - name: Build lib 65 | run: yarn prepare 66 | 67 | - name: Authenticate with registry 68 | run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_AUTOMATION_TOKEN }} 69 | 70 | - name: Publish 71 | run: | 72 | chmod +x ./scripts/add_build_number.sh 73 | yarn release --increment=$(./scripts/add_build_number.sh) --preRelease=beta --ci 74 | env: 75 | GITHUB_TOKEN: ${{ secrets.REPO_PAT }} 76 | -------------------------------------------------------------------------------- /.github/workflows/publish-release.yml: -------------------------------------------------------------------------------- 1 | name: Publish Rive React Native release 2 | on: 3 | workflow_dispatch: 4 | jobs: 5 | publish: 6 | name: Install and publish 7 | runs-on: ubuntu-latest 8 | steps: 9 | - name: Checkout 10 | uses: actions/checkout@v4 11 | with: 12 | fetch-depth: 0 13 | 14 | - name: Git config 15 | run: | 16 | git config --local user.email 'hello@rive.app' 17 | git config --local user.name ${{ github.actor }} 18 | 19 | - name: Setup Node.js 20 | uses: actions/setup-node@v4 21 | with: 22 | node-version: 18.x 23 | 24 | - name: Enable Corepack and Use Correct Yarn Version 25 | run: | 26 | corepack enable 27 | corepack prepare yarn@4.7.0 --activate 28 | 29 | - name: Restore dependencies 30 | id: yarn-cache 31 | uses: actions/cache/restore@v4 32 | with: 33 | path: | 34 | **/node_modules 35 | .yarn/install-state.gz 36 | key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}-${{ hashFiles('**/package.json', '!node_modules/**') }} 37 | restore-keys: | 38 | ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} 39 | ${{ runner.os }}-yarn- 40 | 41 | - name: Debug Yarn State (Optional) 42 | run: yarn install --check-cache 43 | 44 | - name: Install dependencies 45 | if: steps.yarn-cache.outputs.cache-hit != 'true' 46 | run: yarn install --immutable 47 | shell: bash 48 | 49 | - name: Cache dependencies 50 | if: steps.yarn-cache.outputs.cache-hit != 'true' 51 | uses: actions/cache/save@v4 52 | with: 53 | path: | 54 | **/node_modules 55 | .yarn/install-state.gz 56 | key: ${{ steps.yarn-cache.outputs.cache-primary-key }} 57 | 58 | - name: Lint 59 | run: yarn lint 60 | 61 | - name: Type Check 62 | run: yarn typescript 63 | 64 | - name: Build lib 65 | run: yarn prepare 66 | 67 | - name: Authenticate with registry 68 | run: npm config set //registry.npmjs.org/:_authToken ${{ secrets.NPM_AUTOMATION_TOKEN }} 69 | 70 | - name: Publish 71 | run: | 72 | chmod +x ./scripts/add_build_number.sh 73 | yarn release --increment=$(cat version.txt) 74 | env: 75 | GITHUB_TOKEN: ${{ secrets.REPO_PAT }} 76 | -------------------------------------------------------------------------------- /.github/workflows/typecheck-lint.yml: -------------------------------------------------------------------------------- 1 | name: Typecheck and Lint 2 | 3 | on: [push] 4 | 5 | jobs: 6 | typecheck-and-lint: 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v4 10 | 11 | - name: Setup Node.js 12 | uses: actions/setup-node@v4 13 | with: 14 | node-version: 18.x 15 | 16 | - name: Enable Corepack and Use Correct Yarn Version 17 | run: | 18 | corepack enable 19 | corepack prepare yarn@4.7.0 --activate 20 | 21 | - name: Restore dependencies 22 | id: yarn-cache 23 | uses: actions/cache/restore@v4 24 | with: 25 | path: | 26 | **/node_modules 27 | .yarn/install-state.gz 28 | key: ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }}-${{ hashFiles('**/package.json', '!node_modules/**') }} 29 | restore-keys: | 30 | ${{ runner.os }}-yarn-${{ hashFiles('yarn.lock') }} 31 | ${{ runner.os }}-yarn- 32 | 33 | - name: Debug Yarn State (Optional) 34 | run: yarn install --check-cache 35 | 36 | - name: Install dependencies 37 | if: steps.yarn-cache.outputs.cache-hit != 'true' 38 | run: yarn install --immutable 39 | shell: bash 40 | 41 | - name: Cache dependencies 42 | if: steps.yarn-cache.outputs.cache-hit != 'true' 43 | uses: actions/cache/save@v4 44 | with: 45 | path: | 46 | **/node_modules 47 | .yarn/install-state.gz 48 | key: ${{ steps.yarn-cache.outputs.cache-primary-key }} 49 | 50 | - name: Run Eslint Checks 51 | run: yarn lint 52 | 53 | - name: Run Typescript Checks 54 | run: yarn typescript 55 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # XDE 6 | .expo/ 7 | 8 | # VSCode 9 | .vscode/ 10 | jsconfig.json 11 | 12 | # Xcode 13 | # 14 | build/ 15 | *.pbxuser 16 | !default.pbxuser 17 | *.mode1v3 18 | !default.mode1v3 19 | *.mode2v3 20 | !default.mode2v3 21 | *.perspectivev3 22 | !default.perspectivev3 23 | xcuserdata 24 | *.xccheckout 25 | *.moved-aside 26 | DerivedData 27 | *.hmap 28 | *.ipa 29 | *.xcuserstate 30 | project.xcworkspace 31 | **/.xcode.env.local 32 | 33 | # Android/IJ 34 | # 35 | .classpath 36 | .cxx 37 | .gradle 38 | .idea 39 | .project 40 | .settings 41 | local.properties 42 | android.iml 43 | 44 | # Cocoapods 45 | # 46 | example/ios/Pods 47 | 48 | # Ruby 49 | example/vendor/ 50 | 51 | # node.js 52 | # 53 | node_modules/ 54 | npm-debug.log 55 | yarn-debug.log 56 | yarn-error.log 57 | 58 | # Ignore files generated by npm pack 59 | /*.tgz 60 | 61 | # BUCK 62 | buck-out/ 63 | \.buckd/ 64 | android/app/libs 65 | android/keystores/debug.keystore 66 | 67 | # Yarn 68 | .yarn/* 69 | !.yarn/patches 70 | !.yarn/plugins 71 | !.yarn/releases 72 | !.yarn/sdks 73 | !.yarn/versions 74 | 75 | # Expo 76 | .expo/ 77 | 78 | # Turborepo 79 | .turbo/ 80 | 81 | # generated by bob 82 | lib/ 83 | 84 | # React Native Codegen 85 | ios/generated 86 | android/generated 87 | 88 | # React Native Nitro Modules 89 | nitrogen/ 90 | # OSX 91 | # 92 | .DS_Store 93 | 94 | # XDE 95 | .expo/ 96 | 97 | # VSCode 98 | .vscode/ 99 | jsconfig.json 100 | 101 | # Xcode 102 | # 103 | build/ 104 | *.pbxuser 105 | !default.pbxuser 106 | *.mode1v3 107 | !default.mode1v3 108 | *.mode2v3 109 | !default.mode2v3 110 | *.perspectivev3 111 | !default.perspectivev3 112 | xcuserdata 113 | *.xccheckout 114 | *.moved-aside 115 | DerivedData 116 | *.hmap 117 | *.ipa 118 | *.xcuserstate 119 | project.xcworkspace 120 | **/.xcode.env.local 121 | 122 | # Android/IJ 123 | # 124 | .classpath 125 | .cxx 126 | .gradle 127 | .idea 128 | .project 129 | .settings 130 | local.properties 131 | android.iml 132 | 133 | # Cocoapods 134 | # 135 | example/ios/Pods 136 | 137 | # Ruby 138 | example/vendor/ 139 | 140 | # node.js 141 | # 142 | node_modules/ 143 | npm-debug.log 144 | yarn-debug.log 145 | yarn-error.log 146 | 147 | # BUCK 148 | buck-out/ 149 | \.buckd/ 150 | android/app/libs 151 | android/keystores/debug.keystore 152 | 153 | # Yarn 154 | .yarn/* 155 | !.yarn/patches 156 | !.yarn/plugins 157 | !.yarn/releases 158 | !.yarn/sdks 159 | !.yarn/versions 160 | 161 | # Expo 162 | .expo/ 163 | 164 | # Turborepo 165 | .turbo/ 166 | 167 | # generated by bob 168 | lib/ 169 | 170 | # React Native Codegen 171 | ios/generated 172 | android/generated 173 | 174 | # React Native Nitro Modules 175 | nitrogen/ 176 | -------------------------------------------------------------------------------- /.watchmanconfig: -------------------------------------------------------------------------------- 1 | {} -------------------------------------------------------------------------------- /.yarnrc: -------------------------------------------------------------------------------- 1 | # Override Yarn command so we can automatically setup the repo on running `yarn` 2 | 3 | yarn-path "scripts/bootstrap.js" 4 | -------------------------------------------------------------------------------- /.yarnrc.yml: -------------------------------------------------------------------------------- 1 | nodeLinker: node-modules 2 | 3 | yarnPath: .yarn/releases/yarn-4.7.0.cjs 4 | -------------------------------------------------------------------------------- /Backup.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Backup.swift 3 | // rive-react-native 4 | // 5 | // Created by Peter G Hayes on 02/03/2023. 6 | // 7 | 8 | import Foundation 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 TMaszko 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ![Build Status](https://github.com/rive-app/rive-react-native/actions/workflows/typecheck-lint.yml/badge.svg) 2 | ![Discord badge](https://img.shields.io/discord/532365473602600965) 3 | ![Twitter handle](https://img.shields.io/twitter/follow/rive_app.svg?style=social&label=Follow) 4 | 5 | # Rive React Native 6 | 7 | ![Rive hero image](https://rive-app.notion.site/image/https%3A%2F%2Fs3-us-west-2.amazonaws.com%2Fsecure.notion-static.com%2Fff44ed5f-1eea-4154-81ef-84547e61c3fd%2Frive_notion.png?table=block&id=f198cab2-c0bc-4ce8-970c-42220379bcf3&spaceId=9c949665-9ad9-445f-b9c4-5ee204f8b60c&width=2000&userId=&cache=v2) 8 | 9 | A React Native runtime library for [Rive](https://rive.app). 10 | 11 | This library is a wrapper around the iOS/Android runtime, providing a component and ref pattern for React Native applications. 12 | 13 | ## Table of contents 14 | 15 | - :star: [Rive Overview](#rive-overview) 16 | - 🚀 [Getting Started & API docs](#getting-started) 17 | - :mag: [Supported Devices](#supported-devices) 18 | - :books: [Examples](#examples) 19 | - 🏃 [Migration Guides](#migration-guides) 20 | - 👨‍💻 [Contributing](#contributing) 21 | - :question: [Issues](#issues) 22 | 23 | ## Rive Overview 24 | 25 | [Rive](https://rive.app) is a real-time interactive design and animation tool that helps teams create and run interactive animations anywhere. Designers and developers use our collaborative editor to create motion graphics that respond to different states and user inputs. Our lightweight open-source runtime libraries allow them to load their animations into apps, games, and websites. 26 | 27 | :house_with_garden: [Homepage](https://rive.app/) 28 | 29 | :blue_book: [General help docs](https://help.rive.app/) 30 | 31 | 🛠 [Resources for building in Rive](https://rive.app/resources/) 32 | 33 | ## Getting Started 34 | 35 | Follow along with the link below for a quick start in getting Rive React Native integrated into your multi-platform applications. 36 | 37 | [Getting Started with Rive in React Native](https://help.rive.app/runtimes/overview/react-native) 38 | 39 | [API documentation](https://help.rive.app/runtimes/overview/react-native/props) 40 | 41 | ## Supported Devices 42 | 43 | Because this runtime library has a dependency on the [Rive Android](https://github.com/rive-app/rive-android) and [Rive iOS](https://github.com/rive-app/rive-ios) runtimes, the supported devices align with each of these dependencies minimum supported devices, as well as the minimum device requirements of the React Native framework. 44 | 45 | - iOS: **14.0+** 46 | - Android: 47 | - Minimum SDK version: **21** 48 | - Target SDK version: **33** 49 | 50 | ## Examples 51 | 52 | Check out the `example/` folder to run an example application using the Rive React Native runtime. It runs on the local build of this library, and showcases a number of ways to use the Rive component and `useRef` hook pattern: 53 | 54 | - Setting a Rive file via a URL, or asset in the Android/iOS projects 55 | - Displaying single or multiple animations / artboards on one component 56 | - Setting up and maniuplating a state machine via inputs 57 | - ...and more! 58 | 59 | Steps: 60 | 1. Run `yarn bootstrap` 61 | 2. cd `example` 62 | 3. `yarn expo run:android` or `yarn expo run:ios` 63 | 64 | iOS: 65 | - 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 66 | 67 | ## Migration Guides 68 | 69 | Using an older version of the runtime and need to learn how to upgrade to the latest version? Check out the migration guides below in our help center that help guide you through version bumps; breaking changes and all! 70 | 71 | [Migration guides](https://help.rive.app/runtimes/overview/react-native/migrating-from-v3-to-v4) 72 | 73 | ## Contributing 74 | 75 | We love contributions! Check out our [contributing docs](./CONTRIBUTING.md) to get more details into how to run this project, the examples, and more all locally. 76 | 77 | ## Issues 78 | 79 | Have an issue with using the runtime, or want to suggest a feature/API to help make your development life better? Log an issue in our [issues](https://github.com/rive-app/rive-react-native/issues) tab! You can also browse older issues and discussion threads there to see solutions that may have worked for common problems. 80 | -------------------------------------------------------------------------------- /android/.project: -------------------------------------------------------------------------------- 1 | 2 | 3 | rive-react-native 4 | Project example-android-rive-react-native created by Buildship. 5 | 6 | 7 | 8 | 9 | org.eclipse.jdt.core.javabuilder 10 | 11 | 12 | 13 | 14 | org.eclipse.buildship.core.gradleprojectbuilder 15 | 16 | 17 | 18 | 19 | 20 | org.eclipse.jdt.core.javanature 21 | org.eclipse.buildship.core.gradleprojectnature 22 | 23 | 24 | 25 | 1740025890274 26 | 27 | 30 28 | 29 | org.eclipse.core.resources.regexFilterMatcher 30 | node_modules|\.git|__CREATED_BY_JAVA_LANGUAGE_SERVER__ 31 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /android/gradle.properties: -------------------------------------------------------------------------------- 1 | RiveReactNative_kotlinVersion=1.8.10 2 | RiveReactNative_compileSdkVersion=34 3 | RiveReactNative_buildToolsVersion=30.0.3 4 | RiveReactNative_targetSdkVersion=34 5 | -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.jar: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/android/gradle/wrapper/gradle-wrapper.jar -------------------------------------------------------------------------------- /android/gradle/wrapper/gradle-wrapper.properties: -------------------------------------------------------------------------------- 1 | distributionBase=GRADLE_USER_HOME 2 | distributionPath=wrapper/dists 3 | distributionUrl=https\://services.gradle.org/distributions/gradle-8.0.1-bin.zip 4 | networkTimeout=10000 5 | zipStoreBase=GRADLE_USER_HOME 6 | zipStorePath=wrapper/dists 7 | -------------------------------------------------------------------------------- /android/gradlew.bat: -------------------------------------------------------------------------------- 1 | @rem 2 | @rem Copyright 2015 the original author or authors. 3 | @rem 4 | @rem Licensed under the Apache License, Version 2.0 (the "License"); 5 | @rem you may not use this file except in compliance with the License. 6 | @rem You may obtain a copy of the License at 7 | @rem 8 | @rem https://www.apache.org/licenses/LICENSE-2.0 9 | @rem 10 | @rem Unless required by applicable law or agreed to in writing, software 11 | @rem distributed under the License is distributed on an "AS IS" BASIS, 12 | @rem WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 | @rem See the License for the specific language governing permissions and 14 | @rem limitations under the License. 15 | @rem 16 | 17 | @if "%DEBUG%" == "" @echo off 18 | @rem ########################################################################## 19 | @rem 20 | @rem Gradle startup script for Windows 21 | @rem 22 | @rem ########################################################################## 23 | 24 | @rem Set local scope for the variables with windows NT shell 25 | if "%OS%"=="Windows_NT" setlocal 26 | 27 | set DIRNAME=%~dp0 28 | if "%DIRNAME%" == "" set DIRNAME=. 29 | set APP_BASE_NAME=%~n0 30 | set APP_HOME=%DIRNAME% 31 | 32 | @rem Resolve any "." and ".." in APP_HOME to make it shorter. 33 | for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi 34 | 35 | @rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script. 36 | set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m" 37 | 38 | @rem Find java.exe 39 | if defined JAVA_HOME goto findJavaFromJavaHome 40 | 41 | set JAVA_EXE=java.exe 42 | %JAVA_EXE% -version >NUL 2>&1 43 | if "%ERRORLEVEL%" == "0" goto execute 44 | 45 | echo. 46 | echo ERROR: JAVA_HOME is not set and no 'java' command could be found in your PATH. 47 | echo. 48 | echo Please set the JAVA_HOME variable in your environment to match the 49 | echo location of your Java installation. 50 | 51 | goto fail 52 | 53 | :findJavaFromJavaHome 54 | set JAVA_HOME=%JAVA_HOME:"=% 55 | set JAVA_EXE=%JAVA_HOME%/bin/java.exe 56 | 57 | if exist "%JAVA_EXE%" goto execute 58 | 59 | echo. 60 | echo ERROR: JAVA_HOME is set to an invalid directory: %JAVA_HOME% 61 | echo. 62 | echo Please set the JAVA_HOME variable in your environment to match the 63 | echo location of your Java installation. 64 | 65 | goto fail 66 | 67 | :execute 68 | @rem Setup the command line 69 | 70 | set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar 71 | 72 | 73 | @rem Execute Gradle 74 | "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* 75 | 76 | :end 77 | @rem End local scope for the variables with windows NT shell 78 | if "%ERRORLEVEL%"=="0" goto mainEnd 79 | 80 | :fail 81 | rem Set variable GRADLE_EXIT_CONSOLE if you need the _script_ return code instead of 82 | rem the _cmd.exe /c_ return code! 83 | if not "" == "%GRADLE_EXIT_CONSOLE%" exit 1 84 | exit /b 1 85 | 86 | :mainEnd 87 | if "%OS%"=="Windows_NT" endlocal 88 | 89 | :omega 90 | -------------------------------------------------------------------------------- /android/src/main/AndroidManifest.xml: -------------------------------------------------------------------------------- 1 | 5 | 6 | 11 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /android/src/main/java/com/rivereactnative/RNAlignment.kt: -------------------------------------------------------------------------------- 1 | package com.rivereactnative 2 | 3 | import app.rive.runtime.kotlin.core.Alignment 4 | 5 | enum class RNAlignment(private val mValue: String) { 6 | TopLeft("topLeft"), 7 | TopCenter("topCenter"), 8 | TopRight("topRight"), 9 | CenterLeft("centerLeft"), 10 | Center("center"), 11 | CenterRight("centerRight"), 12 | BottomLeft("bottomLeft"), 13 | BottomCenter("bottomCenter"), 14 | BottomRight("bottomRight"); 15 | 16 | override fun toString(): String { 17 | return mValue 18 | } 19 | 20 | companion object { 21 | 22 | fun mapToRNAlignment(alignment: String): RNAlignment { 23 | return valueOf(values().first() { it.toString() == alignment }.name) 24 | } 25 | 26 | fun mapToRiveAlignment(v: RNAlignment): Alignment { 27 | return when (v) { 28 | TopLeft -> Alignment.TOP_LEFT 29 | TopCenter -> Alignment.TOP_CENTER 30 | TopRight -> Alignment.TOP_RIGHT 31 | CenterLeft -> Alignment.CENTER_LEFT 32 | Center -> Alignment.CENTER 33 | CenterRight -> Alignment.CENTER_RIGHT 34 | BottomLeft -> Alignment.BOTTOM_LEFT 35 | BottomCenter -> Alignment.BOTTOM_CENTER 36 | BottomRight -> Alignment.BOTTOM_RIGHT 37 | else -> throw IllegalStateException("Unsupported Alignment type") 38 | } 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /android/src/main/java/com/rivereactnative/RNDirection.kt: -------------------------------------------------------------------------------- 1 | package com.rivereactnative 2 | 3 | import app.rive.runtime.kotlin.core.Direction 4 | 5 | enum class RNDirection(private val mValue: String) { 6 | Backwards("backwards"), 7 | Auto("auto"), 8 | Forwards("forwards"); 9 | 10 | override fun toString(): String { 11 | return mValue 12 | } 13 | 14 | companion object { 15 | fun mapToRNDirection(direction: String): RNDirection { 16 | return valueOf(values().first() { it.toString() == direction }.name) 17 | } 18 | 19 | fun mapToRiveDirection(rnDirection: RNDirection): Direction { 20 | return when (rnDirection) { 21 | Backwards -> Direction.BACKWARDS 22 | Auto -> Direction.AUTO 23 | Forwards -> Direction.FORWARDS 24 | } 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /android/src/main/java/com/rivereactnative/RNFit.kt: -------------------------------------------------------------------------------- 1 | package com.rivereactnative 2 | 3 | import app.rive.runtime.kotlin.core.Fit 4 | 5 | enum class RNFit(private val mValue: String) { 6 | Cover("cover"), 7 | Contain("contain"), 8 | Fill("fill"), 9 | FitWidth("fitWidth"), 10 | FitHeight("fitHeight"), 11 | None("none"), 12 | ScaleDown("scaleDown"), 13 | Layout("layout"); 14 | 15 | override fun toString(): String { 16 | return mValue 17 | } 18 | 19 | companion object { 20 | 21 | fun mapToRNFit(fit: String): RNFit { 22 | return valueOf(values().first() { it.toString() == fit }.name) 23 | } 24 | 25 | fun mapToRiveFit(rnFit: RNFit): Fit { 26 | return when (rnFit) { 27 | Cover -> Fit.COVER 28 | Contain -> Fit.CONTAIN 29 | Fill -> Fit.FILL 30 | FitWidth -> Fit.FIT_WIDTH 31 | FitHeight -> Fit.FIT_HEIGHT 32 | None -> Fit.NONE 33 | ScaleDown -> Fit.SCALE_DOWN 34 | Layout -> Fit.LAYOUT 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /android/src/main/java/com/rivereactnative/RNLoopMode.kt: -------------------------------------------------------------------------------- 1 | package com.rivereactnative 2 | 3 | import app.rive.runtime.kotlin.core.Loop as RiveLoop 4 | 5 | enum class RNLoopMode(private val mValue: String) { 6 | OneShot("oneShot"), 7 | Loop("loop"), 8 | PingPong("pingPong"), 9 | Auto("auto"); 10 | 11 | override fun toString(): String { 12 | return mValue 13 | } 14 | 15 | companion object { 16 | fun mapToRNLoopMode(loopMode: String): RNLoopMode { 17 | return valueOf(values().first() { it.toString() == loopMode }.name) 18 | } 19 | 20 | fun mapToRNLoopMode(riveLoopMode: RiveLoop): RNLoopMode { 21 | return when (riveLoopMode) { 22 | RiveLoop.ONESHOT -> OneShot 23 | RiveLoop.LOOP -> Loop 24 | RiveLoop.PINGPONG -> PingPong 25 | RiveLoop.AUTO -> Auto 26 | } 27 | } 28 | 29 | fun mapToRiveLoop(rnLoopMode: RNLoopMode): RiveLoop { 30 | return when (rnLoopMode) { 31 | OneShot -> RiveLoop.ONESHOT 32 | Loop -> RiveLoop.LOOP 33 | PingPong -> RiveLoop.PINGPONG 34 | Auto -> RiveLoop.AUTO 35 | } 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /android/src/main/java/com/rivereactnative/RNPropertyType.kt: -------------------------------------------------------------------------------- 1 | package com.rivereactnative 2 | 3 | enum class RNPropertyType(private val mValue: kotlin.String) { 4 | Number("number"), 5 | String("string"), 6 | Boolean("boolean"), 7 | Color("color"), 8 | Trigger("trigger"), 9 | Enum("enum"); 10 | 11 | override fun toString(): kotlin.String { 12 | return mValue 13 | } 14 | 15 | companion object { 16 | 17 | fun mapToRNPropertyType(propertyType: kotlin.String): RNPropertyType { 18 | return valueOf(entries.first() { it.toString() == propertyType }.name) 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /android/src/main/java/com/rivereactnative/RNRiveError.kt: -------------------------------------------------------------------------------- 1 | package com.rivereactnative 2 | 3 | import app.rive.runtime.kotlin.core.errors.* 4 | 5 | enum class RNRiveError(private val mValue: String) { 6 | FileNotFound("FileNotFound"), 7 | UnsupportedRuntimeVersion("UnsupportedRuntimeVersion"), 8 | IncorrectRiveFileUrl("IncorrectRiveFileUrl"), 9 | IncorrectAnimationName("IncorrectAnimationName"), 10 | MalformedFile("MalformedFile"), 11 | IncorrectArtboardName("IncorrectArtboardName"), 12 | IncorrectStateMachineName("IncorrectStateMachineName"), 13 | IncorrectStateMachineInput("IncorrectStateMachineInput"), 14 | TextRunNotFoundError("TextRunNotFoundError"), 15 | DataBindingError("DataBindingError"); 16 | 17 | var message: String = "Default message" 18 | 19 | override fun toString(): String { 20 | return mValue 21 | } 22 | 23 | companion object { 24 | fun mapToRNRiveError(ex: RiveException): RNRiveError? { 25 | return when (ex) { 26 | is ArtboardException -> { 27 | val err = IncorrectArtboardName 28 | err.message = ex.message!! 29 | return err 30 | } 31 | is UnsupportedRuntimeVersionException -> { 32 | val err = UnsupportedRuntimeVersion 33 | err.message = ex.message!! 34 | return err 35 | } 36 | is MalformedFileException -> { 37 | val err = MalformedFile 38 | err.message = ex.message!! 39 | return err 40 | } 41 | is AnimationException -> { 42 | val err = IncorrectAnimationName 43 | err.message = ex.message!! 44 | return err 45 | } 46 | is StateMachineException -> { 47 | val err = IncorrectStateMachineName 48 | err.message = ex.message!! 49 | return err 50 | } 51 | is StateMachineInputException -> { 52 | val err = IncorrectStateMachineInput 53 | err.message = ex.message!! 54 | return err 55 | } 56 | is TextValueRunException -> { 57 | val err = TextRunNotFoundError 58 | err.message = ex.message!! 59 | return err 60 | } 61 | is ViewModelException -> { 62 | val err = DataBindingError 63 | err.message = ex.message!! 64 | return err; 65 | } 66 | else -> null 67 | } 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /android/src/main/java/com/rivereactnative/RNRiveRendererType.kt: -------------------------------------------------------------------------------- 1 | package com.rivereactnative 2 | 3 | import app.rive.runtime.kotlin.core.RendererType 4 | 5 | enum class RNRiveRendererType(private val rendererTypeName: String) { 6 | Rive("rive"), 7 | Canvas("canvas"); 8 | 9 | override fun toString(): String { 10 | return rendererTypeName 11 | } 12 | 13 | companion object { 14 | fun mapToRNRiveRendererType(rendererType: String): RNRiveRendererType { 15 | return values().first { it.rendererTypeName == rendererType } 16 | } 17 | 18 | fun mapToRiveRendererType(rnRendererType: RNRiveRendererType): RendererType { 19 | return when (rnRendererType) { 20 | Rive -> RendererType.Rive 21 | Canvas -> RendererType.Canvas 22 | } 23 | } 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /android/src/main/java/com/rivereactnative/RiveReactNativeModule.kt: -------------------------------------------------------------------------------- 1 | package com.rivereactnative 2 | 3 | import com.facebook.react.bridge.* 4 | import com.facebook.react.uimanager.UIManagerHelper 5 | import com.facebook.react.uimanager.UIManagerModule 6 | 7 | class RiveReactNativeModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { 8 | override fun getName() = "RiveReactNativeModule" 9 | 10 | private fun handleState(node: Int, promise: Promise, stateGetter: (RiveReactNativeView) -> T) { 11 | val uiManager = UIManagerHelper.getUIManager(reactApplicationContext, node) 12 | val view = uiManager?.resolveView(node) as? RiveReactNativeView 13 | if (view != null) { 14 | val value = stateGetter(view) 15 | promise.resolve(value) 16 | } else { 17 | promise.reject("VIEW_NOT_FOUND", "Could not find RiveReactNativeView") 18 | } 19 | } 20 | 21 | @ReactMethod 22 | fun getBooleanState(node: Int, inputName: String, promise: Promise) { 23 | handleState(node, promise) { view -> view.getBooleanState(inputName) } 24 | } 25 | 26 | @ReactMethod 27 | fun getNumberState(node: Int, inputName: String, promise: Promise) { 28 | handleState(node, promise) { view -> view.getNumberState(inputName) } 29 | } 30 | 31 | @ReactMethod 32 | fun getBooleanStateAtPath(node: Int, inputName: String, path: String, promise: Promise) { 33 | handleState(node, promise) { view -> view.getBooleanStateAtPath(inputName, path) } 34 | } 35 | 36 | @ReactMethod 37 | fun getNumberStateAtPath(node: Int, inputName: String, path: String, promise: Promise) { 38 | handleState(node, promise) { view -> view.getNumberStateAtPath(inputName, path) } 39 | } 40 | 41 | @ReactMethod 42 | fun addListener(type: String?) { 43 | // Keep: Required for RN built in Event Emitter Calls. 44 | } 45 | 46 | @ReactMethod 47 | fun removeListeners(type: Int?) { 48 | // Keep: Required for RN built in Event Emitter Calls. 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /android/src/main/java/com/rivereactnative/RiveReactNativePackage.kt: -------------------------------------------------------------------------------- 1 | package com.rivereactnative 2 | 3 | import com.facebook.react.ReactPackage 4 | import com.facebook.react.bridge.NativeModule 5 | import com.facebook.react.bridge.ReactApplicationContext 6 | import com.facebook.react.uimanager.ViewManager 7 | 8 | 9 | class RiveReactNativePackage : ReactPackage { 10 | override fun createNativeModules( 11 | reactContext: ReactApplicationContext 12 | ): MutableList = listOf(RiveReactNativeRendererModule(reactContext), RiveReactNativeModule(reactContext)).toMutableList() 13 | 14 | override fun createViewManagers(reactContext: ReactApplicationContext): List> { 15 | return listOf(RiveReactNativeViewManager()) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /android/src/main/java/com/rivereactnative/RiveReactNativeRendererModule.kt: -------------------------------------------------------------------------------- 1 | package com.rivereactnative 2 | 3 | import com.facebook.react.bridge.ReactApplicationContext 4 | import com.facebook.react.bridge.ReactContextBaseJavaModule 5 | import com.facebook.react.bridge.ReactMethod 6 | import app.rive.runtime.kotlin.core.Rive 7 | 8 | class RiveReactNativeRendererModule(reactContext: ReactApplicationContext) : ReactContextBaseJavaModule(reactContext) { 9 | override fun getName() = "RiveReactNativeRendererModule" 10 | 11 | @ReactMethod fun defaultRenderer(iosRenderer: String, androidRenderer: String) { 12 | val rnRendererType = RNRiveRendererType.mapToRNRiveRendererType(androidRenderer); 13 | val rendererType = RNRiveRendererType.mapToRiveRendererType(rnRendererType); 14 | Rive.init(reactApplicationContext, defaultRenderer = rendererType) 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /babel.config.js: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | presets: ['module:metro-react-native-babel-preset'], 3 | }; 4 | -------------------------------------------------------------------------------- /docs/types.md: -------------------------------------------------------------------------------- 1 | # Types and enums 2 | 3 | ## Fit 4 | 5 | ```ts 6 | export enum Fit { 7 | Cover = 'cover', 8 | Contain = 'contain', 9 | Fill = 'fill', 10 | FitWidth = 'fitWidth', 11 | FitHeight = 'fitHeight', 12 | None = 'none', 13 | ScaleDown = 'scaleDown', 14 | Layout = 'layout', 15 | } 16 | ``` 17 | 18 | ## Alignment 19 | 20 | ```ts 21 | export enum Alignment { 22 | TopLeft = 'topLeft', 23 | TopCenter = 'topCenter', 24 | TopRight = 'topRight', 25 | CenterLeft = 'centerLeft', 26 | Center = 'center', 27 | CenterRight = 'centerRight', 28 | BottomLeft = 'bottomLeft', 29 | BottomCenter = 'bottomCenter', 30 | BottomRight = 'bottomRight', 31 | } 32 | ``` 33 | 34 | ## LoopMode 35 | 36 | ```ts 37 | export enum LoopMode { 38 | OneShot = 'oneShot', 39 | Loop = 'loop', 40 | PingPong = 'pingPong', 41 | Auto = 'auto', 42 | } 43 | ``` 44 | 45 | ## Direction 46 | 47 | ```ts 48 | export enum Direction { 49 | Backwards = 'backwards', 50 | Auto = 'auto', 51 | Forwards = 'forwards', 52 | } 53 | ``` 54 | 55 | ## RNRiveError 56 | 57 | ```ts 58 | export type RNRiveError = { 59 | message: string; 60 | type: RNRiveErrorType; 61 | }; 62 | ``` 63 | 64 | ## RnRiveErrorType 65 | 66 | ```ts 67 | export enum RNRiveErrorType { 68 | FileNotFound = 'FileNotFound', 69 | UnsupportedRuntimeVersion = 'UnsupportedRuntimeVersion', 70 | IncorrectRiveFileUrl = 'IncorrectRiveFileUrl', 71 | IncorrectAnimationName = 'IncorrectAnimationName', 72 | MalformedFile = 'MalformedFile', 73 | IncorrectArtboardName = 'IncorrectArtboardName', 74 | IncorrectStateMachineName = 'IncorrectStateMachineName', 75 | IncorrectStateMachineInput = 'IncorrectStateMachineInput', 76 | } 77 | ``` 78 | -------------------------------------------------------------------------------- /docs/usage-guide.md: -------------------------------------------------------------------------------- 1 | # Rive component usage guide 2 | 3 | Rive component allows setup and interaction with the native implementation of the Rive runtime on iOS and Android. 4 | 5 | ## 1. Installing dependencies 6 | 7 | To install the SDK run the following command in your terminal: 8 | 9 | ```sh 10 | yarn add rive-react-native 11 | ``` 12 | 13 | For iOS you will have to run `pod install` inside `ios` directory in order to install needed native dependencies. Android won't require any additional steps. 14 | 15 | ## 2. Rendering `Rive` component in your app 16 | 17 | To render the animation in your app just use [`Rive`](./rive-react-native-reference.md) component wherever you need it. 18 | 19 | ```jsx 20 | import Rive from 'rive-react-native'; 21 | 22 | export default function App() { 23 | return ( 24 | 25 | 26 | 27 | ); 28 | } 29 | ``` 30 | 31 | ## 3. Setting `resourceName` and other useful props 32 | 33 | To load and play the animation use[`resourceName`](./rive-react-native-reference.md#resourceName-optional) prop. 34 | In order to play the animation directly after loading [`autoplay`](./rive-react-native-reference.md#autoplay-optional) prop can be used. 35 | 36 | ```jsx 37 | 38 | ``` 39 | 40 | You can also set the url as an animation source or control its layout/placement using component props. The whole list of available props can be found [here](rive-react-native-reference.md#props). 41 | 42 | ## 4. Triggering play/pause manually 43 | 44 | In addition to configuring the rive animation view declaratively there is also a way to trigger some actions imperatively using component's ref. 45 | 46 | On of those actions are `play` and `pause` which can be used to manually pause and start the animation. 47 | 48 | ```tsx 49 | import Rive, { RiveRef } from 'rive-react-native' 50 | 51 | export default function App() { 52 | const riveRef = React.useRef(null); 53 | 54 | const handlePlayPress = () => { 55 | riveRef?.current?.play(); 56 | }; 57 | 58 | return ( 59 | 60 | 64 | 65 | 28 | 35 | 52 | 62 | 63 | 64 | ); 65 | } 66 | 67 | const styles = StyleSheet.create({ 68 | safeAreaViewContainer: { 69 | flex: 1, 70 | }, 71 | container: { 72 | flexGrow: 1, 73 | width: '100%', 74 | height: '50%', 75 | }, 76 | animation: { 77 | width: '100%', 78 | height: '50%', 79 | }, 80 | }); 81 | -------------------------------------------------------------------------------- /example/app/(examples)/OutOfBandAssets.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { SafeAreaView, ScrollView, StyleSheet, View } from 'react-native'; 3 | import Rive, { Fit, RNRiveError } from 'rive-react-native'; 4 | import { Picker } from '@react-native-picker/picker'; 5 | 6 | export default function StateMachine() { 7 | const [uri, setUri] = React.useState('https://picsum.photos/id/372/500/500'); 8 | 9 | return ( 10 | 11 | { 57 | console.log(riveError); 58 | }} 59 | /> 60 | {/* 61 | Load in an external asset from a URL, or bundled asset on the native 62 | platform, or as a source loaded directly from JavaScript. 63 | */} 64 | 65 | 66 | 67 | setUri(value)} 70 | mode={'dropdown'} 71 | style={styles.picker} 72 | > 73 | {[ 74 | 'https://picsum.photos/id/372/500/500', 75 | 'https://picsum.photos/id/373/500/500', 76 | ].map((key) => ( 77 | 78 | ))} 79 | 80 | 81 | 82 | 83 | 84 | ); 85 | } 86 | 87 | const styles = StyleSheet.create({ 88 | safeAreaViewContainer: { 89 | flex: 1, 90 | }, 91 | container: { 92 | flexGrow: 1, 93 | alignItems: 'center', 94 | justifyContent: 'flex-start', 95 | }, 96 | animation: { 97 | width: '100%', 98 | height: 400, 99 | }, 100 | picker: { 101 | flex: 1, 102 | width: '100%', 103 | }, 104 | pickerWrapper: { 105 | borderWidth: 1, 106 | borderColor: 'black', 107 | borderRadius: 5, 108 | alignItems: 'center', 109 | margin: 16, 110 | }, 111 | pickersWrapper: { 112 | flex: 1, 113 | padding: 16, 114 | alignSelf: 'stretch', 115 | }, 116 | }); 117 | -------------------------------------------------------------------------------- /example/app/(examples)/ResponsiveLayout.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { SafeAreaView, StyleSheet, View, Button, Text } from 'react-native'; 3 | import Rive, { Fit } from 'rive-react-native'; 4 | 5 | const resourceName = 'layout_test'; 6 | 7 | export default function ResponsiveLayout() { 8 | const [scaleFactor, setScaleFactor] = React.useState(4.0); 9 | 10 | const increaseScale = () => setScaleFactor((prev) => prev + 0.5); 11 | const decreaseScale = () => 12 | setScaleFactor((prev) => Math.max(0.5, prev - 0.5)); 13 | 14 | return ( 15 | 16 | 25 | 26 | Layout Scale Factor 27 | 28 | 77 | 78 | Animation: 79 | 80 | 87 | 95 | 103 | 104 | 105 | 106 | 107 | 108 | ); 109 | } 110 | 111 | const styles = StyleSheet.create({ 112 | controls: { 113 | paddingHorizontal: 20, 114 | flex: 1, 115 | alignSelf: 'stretch', 116 | }, 117 | safeAreaViewContainer: { 118 | flex: 1, 119 | }, 120 | container: { 121 | flexGrow: 1, 122 | alignItems: 'center', 123 | justifyContent: 'center', 124 | paddingBottom: 32, 125 | }, 126 | box: { 127 | width: '100%', 128 | height: 400, 129 | marginVertical: 20, 130 | }, 131 | row: { 132 | flexDirection: 'row', 133 | }, 134 | controlButtonGap: { 135 | marginLeft: 8, 136 | }, 137 | controlsRow: { 138 | flexDirection: 'row', 139 | justifyContent: 'space-between', 140 | alignItems: 'center', 141 | }, 142 | resetButton: { 143 | alignSelf: 'flex-start', 144 | marginBottom: 20, 145 | }, 146 | }); 147 | -------------------------------------------------------------------------------- /example/app/(examples)/SourceProp.tsx: -------------------------------------------------------------------------------- 1 | import { SafeAreaView, StyleSheet, ScrollView, Text } from 'react-native'; 2 | import Rive, { Alignment, Fit } from 'rive-react-native'; 3 | 4 | export default function SourceProp() { 5 | return ( 6 | 7 | 8 | Require 9 | 17 | HTTP URI 18 | 28 | Resource name URI 29 | 39 | 40 | 41 | ); 42 | } 43 | 44 | const styles = StyleSheet.create({ 45 | safeAreaViewContainer: { 46 | flex: 1, 47 | }, 48 | container: { 49 | flexGrow: 1, 50 | alignItems: 'center', 51 | justifyContent: 'center', 52 | }, 53 | animation: { 54 | width: '100%', 55 | height: 300, 56 | }, 57 | }); 58 | -------------------------------------------------------------------------------- /example/app/(examples)/StateMachine.tsx: -------------------------------------------------------------------------------- 1 | import * as React from 'react'; 2 | import { useState } from 'react'; 3 | import { SafeAreaView, ScrollView, StyleSheet, Text, View } from 'react-native'; 4 | import Rive, { Fit, RiveRef } from 'rive-react-native'; 5 | import { RadioButton } from 'react-native-paper'; 6 | 7 | export default function StateMachine() { 8 | const riveRef = React.useRef(null); 9 | const [selectedLevel, setSelectedLevel] = useState('0'); 10 | 11 | const setLevel = (n: number) => { 12 | setSelectedLevel(n.toString()); 13 | riveRef.current?.setInputState('skill-controller', 'level', n); 14 | }; 15 | 16 | return ( 17 | 18 | 19 | { 26 | console.log( 27 | 'onStateChanged: ', 28 | 'stateMachineName: ', 29 | stateMachineName, 30 | 'stateName: ', 31 | stateName 32 | ); 33 | }} 34 | resourceName={'skills_listener'} 35 | /> 36 | setLevel(parseInt(newValue, 10))} 38 | value={selectedLevel} 39 | > 40 | 41 | 42 | {'Beginner'} 43 | 44 | 45 | 46 | {'Intermediate'} 47 | 48 | 49 | 50 | {'Expert'} 51 | 52 | 53 | 54 | 55 | 56 | 57 | ); 58 | } 59 | 60 | const styles = StyleSheet.create({ 61 | safeAreaViewContainer: { 62 | flex: 1, 63 | }, 64 | container: { 65 | flexGrow: 1, 66 | alignItems: 'center', 67 | justifyContent: 'center', 68 | marginBottom: 150, 69 | }, 70 | wrapper: { 71 | flex: 1, 72 | flexDirection: 'row', 73 | flexWrap: 'wrap', 74 | justifyContent: 'center', 75 | marginBottom: 20, 76 | }, 77 | box: { 78 | width: '100%', 79 | height: 500, 80 | marginVertical: 20, 81 | }, 82 | picker: { 83 | width: '100%', 84 | height: 50, 85 | }, 86 | radioButtonsWrapper: { 87 | flexDirection: 'row', 88 | }, 89 | radioButtonWrapper: { 90 | flexDirection: 'row', 91 | alignItems: 'center', 92 | marginRight: 16, 93 | }, 94 | }); 95 | -------------------------------------------------------------------------------- /example/app/+not-found.tsx: -------------------------------------------------------------------------------- 1 | import { Link, Stack } from 'expo-router'; 2 | import { StyleSheet } from 'react-native'; 3 | 4 | import { ThemedText } from '@/components/ThemedText'; 5 | import { ThemedView } from '@/components/ThemedView'; 6 | 7 | export default function NotFoundScreen() { 8 | return ( 9 | <> 10 | 11 | 12 | This screen doesn't exist. 13 | 14 | Go to home screen! 15 | 16 | 17 | 18 | ); 19 | } 20 | 21 | const styles = StyleSheet.create({ 22 | container: { 23 | flex: 1, 24 | alignItems: 'center', 25 | justifyContent: 'center', 26 | padding: 20, 27 | }, 28 | link: { 29 | marginTop: 15, 30 | paddingVertical: 15, 31 | }, 32 | }); 33 | -------------------------------------------------------------------------------- /example/app/_layout.tsx: -------------------------------------------------------------------------------- 1 | import FontAwesome from '@expo/vector-icons/FontAwesome'; 2 | import { 3 | DarkTheme, 4 | DefaultTheme, 5 | ThemeProvider, 6 | } from '@react-navigation/native'; 7 | import { useFonts } from 'expo-font'; 8 | import { Stack } from 'expo-router'; 9 | import * as SplashScreen from 'expo-splash-screen'; 10 | import { useEffect } from 'react'; 11 | import 'react-native-reanimated'; 12 | import { GestureHandlerRootView } from 'react-native-gesture-handler'; 13 | import { StyleSheet } from 'react-native'; 14 | 15 | import { useColorScheme } from '@/hooks/useColorScheme'; 16 | 17 | export { 18 | // Catch any errors thrown by the Layout component. 19 | ErrorBoundary, 20 | } from 'expo-router'; 21 | 22 | export const unstable_settings = { 23 | // Ensure that reloading on other pages keeps a back button present. 24 | initialRouteName: '/', 25 | }; 26 | 27 | // Prevent the splash screen from auto-hiding before asset loading is complete. 28 | SplashScreen.preventAutoHideAsync(); 29 | 30 | export default function RootLayout() { 31 | const [loaded, error] = useFonts({ 32 | SpaceMono: require('../assets/fonts/SpaceMono-Regular.ttf'), 33 | ...FontAwesome.font, 34 | }); 35 | 36 | // Expo Router uses Error Boundaries to catch errors in the navigation tree. 37 | useEffect(() => { 38 | if (error) throw error; 39 | }, [error]); 40 | 41 | useEffect(() => { 42 | if (loaded) { 43 | SplashScreen.hideAsync(); 44 | } 45 | }, [loaded]); 46 | 47 | if (!loaded) { 48 | return null; 49 | } 50 | 51 | return ; 52 | } 53 | 54 | function RootLayoutNav() { 55 | const colorScheme = useColorScheme(); 56 | 57 | return ( 58 | 59 | 60 | 61 | 62 | 63 | 67 | 71 | 75 | 79 | 83 | 84 | 88 | 92 | 96 | 100 | 104 | 108 | 112 | 116 | 120 | 124 | 128 | 129 | 130 | 131 | ); 132 | } 133 | 134 | const styles = StyleSheet.create({ 135 | container: { 136 | flex: 1, 137 | }, 138 | }); 139 | -------------------------------------------------------------------------------- /example/app/index.tsx: -------------------------------------------------------------------------------- 1 | import { ScrollView, StyleSheet } from 'react-native'; 2 | import { Link } from 'expo-router'; 3 | 4 | import { ReactNode } from 'react'; 5 | 6 | export function StyledLink({ 7 | href, 8 | children, 9 | }: { 10 | // to do 11 | href: any; 12 | children: ReactNode; 13 | }) { 14 | return ( 15 | 16 | {children} 17 | 18 | ); 19 | } 20 | 21 | export default function Home() { 22 | return ( 23 | 24 | Simple 25 | Data Binding 26 | HTTP 27 | Mesh Example 28 | Layout 29 | 30 | Responsive Layout 31 | 32 | Simple Controls 33 | 34 | Multiple Artboards 35 | 36 | State Machine 37 | Events 38 | Dynamic Text 39 | 40 | Out Of Band Assets 41 | 42 | 43 | Nested Dynamic Text 44 | 45 | Nested Inputs 46 | 47 | Error Not Handled 48 | 49 | 50 | Error Handled Manually 51 | 52 | SourceProp 53 | 54 | ); 55 | } 56 | 57 | const styles = StyleSheet.create({ 58 | button: { 59 | backgroundColor: '#6221ea', 60 | padding: 10, 61 | borderRadius: 5, 62 | marginBottom: 10, 63 | minWidth: 300, 64 | color: 'white', 65 | textAlign: 'center', 66 | fontSize: 16, 67 | textTransform: 'uppercase', 68 | }, 69 | container: { 70 | flexGrow: 1, 71 | alignItems: 'center', 72 | justifyContent: 'center', 73 | padding: 16, 74 | }, 75 | }); 76 | -------------------------------------------------------------------------------- /example/assets/audio/referenced_audio-2929340.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/assets/audio/referenced_audio-2929340.wav -------------------------------------------------------------------------------- /example/assets/fonts/Inter-594377.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/assets/fonts/Inter-594377.ttf -------------------------------------------------------------------------------- /example/assets/fonts/SpaceMono-Regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/assets/fonts/SpaceMono-Regular.ttf -------------------------------------------------------------------------------- /example/assets/images/adaptive-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/assets/images/adaptive-icon.png -------------------------------------------------------------------------------- /example/assets/images/favicon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/assets/images/favicon.png -------------------------------------------------------------------------------- /example/assets/images/icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/assets/images/icon.png -------------------------------------------------------------------------------- /example/assets/images/partial-react-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/assets/images/partial-react-logo.png -------------------------------------------------------------------------------- /example/assets/images/react-logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/assets/images/react-logo.png -------------------------------------------------------------------------------- /example/assets/images/react-logo@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/assets/images/react-logo@2x.png -------------------------------------------------------------------------------- /example/assets/images/react-logo@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/assets/images/react-logo@3x.png -------------------------------------------------------------------------------- /example/assets/images/referenced-image-2929282.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/assets/images/referenced-image-2929282.png -------------------------------------------------------------------------------- /example/assets/images/splash-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/assets/images/splash-icon.png -------------------------------------------------------------------------------- /example/assets/rive/avatars.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/assets/rive/avatars.riv -------------------------------------------------------------------------------- /example/components/Themed.tsx: -------------------------------------------------------------------------------- 1 | /** 2 | * Learn more about Light and Dark modes: 3 | * https://docs.expo.io/guides/color-schemes/ 4 | */ 5 | import { Text as DefaultText, View as DefaultView } from 'react-native'; 6 | 7 | import { Colors } from '@/constants/Colors'; 8 | import { useColorScheme } from '@/hooks/useColorScheme'; 9 | 10 | type ThemeProps = { 11 | lightColor?: string; 12 | darkColor?: string; 13 | }; 14 | 15 | export type TextProps = ThemeProps & DefaultText['props']; 16 | export type ViewProps = ThemeProps & DefaultView['props']; 17 | 18 | export function useThemeColor( 19 | props: { light?: string; dark?: string }, 20 | colorName: keyof typeof Colors.light & keyof typeof Colors.dark 21 | ) { 22 | const theme = useColorScheme() ?? 'light'; 23 | const colorFromProps = props[theme]; 24 | 25 | if (colorFromProps) { 26 | return colorFromProps; 27 | } else { 28 | return Colors[theme][colorName]; 29 | } 30 | } 31 | 32 | export function Text(props: TextProps) { 33 | const { style, lightColor, darkColor, ...otherProps } = props; 34 | const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text'); 35 | 36 | return ; 37 | } 38 | 39 | export function View(props: ViewProps) { 40 | const { style, lightColor, darkColor, ...otherProps } = props; 41 | const backgroundColor = useThemeColor( 42 | { light: lightColor, dark: darkColor }, 43 | 'background' 44 | ); 45 | 46 | return ; 47 | } 48 | -------------------------------------------------------------------------------- /example/components/ThemedText.tsx: -------------------------------------------------------------------------------- 1 | import { Text, TextProps, StyleSheet } from 'react-native'; 2 | 3 | import { useThemeColor } from '@/hooks/useThemeColor'; 4 | 5 | export type ThemedTextProps = TextProps & { 6 | lightColor?: string; 7 | darkColor?: string; 8 | type?: 'default' | 'title' | 'defaultSemiBold' | 'subtitle' | 'link'; 9 | }; 10 | 11 | export function ThemedText({ 12 | style, 13 | lightColor, 14 | darkColor, 15 | type = 'default', 16 | ...rest 17 | }: ThemedTextProps) { 18 | const color = useThemeColor({ light: lightColor, dark: darkColor }, 'text'); 19 | 20 | return ( 21 | 33 | ); 34 | } 35 | 36 | const styles = StyleSheet.create({ 37 | default: { 38 | fontSize: 16, 39 | lineHeight: 24, 40 | }, 41 | defaultSemiBold: { 42 | fontSize: 16, 43 | lineHeight: 24, 44 | fontWeight: '600', 45 | }, 46 | title: { 47 | fontSize: 32, 48 | fontWeight: 'bold', 49 | lineHeight: 32, 50 | }, 51 | subtitle: { 52 | fontSize: 20, 53 | fontWeight: 'bold', 54 | }, 55 | link: { 56 | lineHeight: 30, 57 | fontSize: 16, 58 | color: '#0a7ea4', 59 | }, 60 | }); 61 | -------------------------------------------------------------------------------- /example/components/ThemedView.tsx: -------------------------------------------------------------------------------- 1 | import { View, ViewProps } from 'react-native'; 2 | 3 | import { useThemeColor } from '@/hooks/useThemeColor'; 4 | 5 | export type ThemedViewProps = ViewProps & { 6 | lightColor?: string; 7 | darkColor?: string; 8 | }; 9 | 10 | export function ThemedView({ 11 | style, 12 | lightColor, 13 | darkColor, 14 | ...otherProps 15 | }: ThemedViewProps) { 16 | const backgroundColor = useThemeColor( 17 | { light: lightColor, dark: darkColor }, 18 | 'background' 19 | ); 20 | 21 | return ; 22 | } 23 | -------------------------------------------------------------------------------- /example/constants/Colors.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Below are the colors that are used in the app. The colors are defined in the light and dark mode. 3 | * There are many other ways to style your app. For example, [Nativewind](https://www.nativewind.dev/), [Tamagui](https://tamagui.dev/), [unistyles](https://reactnativeunistyles.vercel.app), etc. 4 | */ 5 | 6 | const tintColorLight = '#0a7ea4'; 7 | const tintColorDark = '#fff'; 8 | 9 | export const Colors = { 10 | light: { 11 | text: '#11181C', 12 | background: '#fff', 13 | tint: tintColorLight, 14 | icon: '#687076', 15 | tabIconDefault: '#687076', 16 | tabIconSelected: tintColorLight, 17 | }, 18 | dark: { 19 | text: '#ECEDEE', 20 | background: '#151718', 21 | tint: tintColorDark, 22 | icon: '#9BA1A6', 23 | tabIconDefault: '#9BA1A6', 24 | tabIconSelected: tintColorDark, 25 | }, 26 | }; 27 | -------------------------------------------------------------------------------- /example/constants/typesPredicates.ts: -------------------------------------------------------------------------------- 1 | export function isEnum( 2 | enumType: EnumType, 3 | enumValue: string 4 | ): enumValue is EnumType[keyof EnumType] { 5 | return Object.values(enumType).includes(enumValue); 6 | } 7 | 8 | export function isEnumKey( 9 | enumType: EnumType, 10 | enumKey: string | number | symbol 11 | ): enumKey is keyof EnumType { 12 | return Object.keys(enumType).includes(enumKey.toString()); 13 | } 14 | -------------------------------------------------------------------------------- /example/hooks/useColorScheme.ts: -------------------------------------------------------------------------------- 1 | export { useColorScheme } from 'react-native'; 2 | -------------------------------------------------------------------------------- /example/hooks/useColorScheme.web.ts: -------------------------------------------------------------------------------- 1 | import { useEffect, useState } from 'react'; 2 | import { useColorScheme as useRNColorScheme } from 'react-native'; 3 | 4 | /** 5 | * To support static rendering, this value needs to be re-calculated on the client side for web 6 | */ 7 | export function useColorScheme() { 8 | const [hasHydrated, setHasHydrated] = useState(false); 9 | 10 | useEffect(() => { 11 | setHasHydrated(true); 12 | }, []); 13 | 14 | const colorScheme = useRNColorScheme(); 15 | 16 | if (hasHydrated) { 17 | return colorScheme; 18 | } 19 | 20 | return 'light'; 21 | } 22 | -------------------------------------------------------------------------------- /example/hooks/useThemeColor.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Learn more about light and dark modes: 3 | * https://docs.expo.dev/guides/color-schemes/ 4 | */ 5 | 6 | import { Colors } from '@/constants/Colors'; 7 | import { useColorScheme } from '@/hooks/useColorScheme'; 8 | 9 | export function useThemeColor( 10 | props: { light?: string; dark?: string }, 11 | colorName: keyof typeof Colors.light & keyof typeof Colors.dark 12 | ) { 13 | const theme = useColorScheme() ?? 'light'; 14 | const colorFromProps = props[theme]; 15 | 16 | if (colorFromProps) { 17 | return colorFromProps; 18 | } else { 19 | return Colors[theme][colorName]; 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /example/ios/.gitignore: -------------------------------------------------------------------------------- 1 | # OSX 2 | # 3 | .DS_Store 4 | 5 | # Xcode 6 | # 7 | build/ 8 | *.pbxuser 9 | !default.pbxuser 10 | *.mode1v3 11 | !default.mode1v3 12 | *.mode2v3 13 | !default.mode2v3 14 | *.perspectivev3 15 | !default.perspectivev3 16 | xcuserdata 17 | *.xccheckout 18 | *.moved-aside 19 | DerivedData 20 | *.hmap 21 | *.ipa 22 | *.xcuserstate 23 | project.xcworkspace 24 | .xcode.env.local 25 | 26 | # Bundle artifacts 27 | *.jsbundle 28 | 29 | # CocoaPods 30 | /Pods/ 31 | -------------------------------------------------------------------------------- /example/ios/.xcode.env: -------------------------------------------------------------------------------- 1 | # This `.xcode.env` file is versioned and is used to source the environment 2 | # used when running script phases inside Xcode. 3 | # To customize your local environment, you can create an `.xcode.env.local` 4 | # file that is not versioned. 5 | 6 | # NODE_BINARY variable contains the PATH to the node executable. 7 | # 8 | # Customize the NODE_BINARY variable here. 9 | # For example, to use nvm with brew, add the following line 10 | # . "$(brew --prefix nvm)/nvm.sh" --no-use 11 | export NODE_BINARY=$(command -v node) 12 | -------------------------------------------------------------------------------- /example/ios/Assets/FollowPathCar.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/Assets/FollowPathCar.riv -------------------------------------------------------------------------------- /example/ios/Assets/Inter-594377.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/Assets/Inter-594377.ttf -------------------------------------------------------------------------------- /example/ios/Assets/artboard_animations.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/Assets/artboard_animations.riv -------------------------------------------------------------------------------- /example/ios/Assets/avatars.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/Assets/avatars.riv -------------------------------------------------------------------------------- /example/ios/Assets/bird.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/Assets/bird.riv -------------------------------------------------------------------------------- /example/ios/Assets/constrained.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/Assets/constrained.riv -------------------------------------------------------------------------------- /example/ios/Assets/hello_world_nested.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/Assets/hello_world_nested.riv -------------------------------------------------------------------------------- /example/ios/Assets/hello_world_text.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/Assets/hello_world_text.riv -------------------------------------------------------------------------------- /example/ios/Assets/hero_editor.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/Assets/hero_editor.riv -------------------------------------------------------------------------------- /example/ios/Assets/layout_test.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/Assets/layout_test.riv -------------------------------------------------------------------------------- /example/ios/Assets/nested_inputs.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/Assets/nested_inputs.riv -------------------------------------------------------------------------------- /example/ios/Assets/nested_menu.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/Assets/nested_menu.riv -------------------------------------------------------------------------------- /example/ios/Assets/out_of_band.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/Assets/out_of_band.riv -------------------------------------------------------------------------------- /example/ios/Assets/rainy.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/Assets/rainy.riv -------------------------------------------------------------------------------- /example/ios/Assets/rating.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/Assets/rating.riv -------------------------------------------------------------------------------- /example/ios/Assets/referenced-image-2929282.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/Assets/referenced-image-2929282.png -------------------------------------------------------------------------------- /example/ios/Assets/referenced_audio-2929340.wav: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/Assets/referenced_audio-2929340.wav -------------------------------------------------------------------------------- /example/ios/Assets/skills_listener.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/Assets/skills_listener.riv -------------------------------------------------------------------------------- /example/ios/Assets/switch.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/Assets/switch.riv -------------------------------------------------------------------------------- /example/ios/Assets/truck_v7.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/Assets/truck_v7.riv -------------------------------------------------------------------------------- /example/ios/Assets/two_bone_ik.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/Assets/two_bone_ik.riv -------------------------------------------------------------------------------- /example/ios/Assets/ui_swipe_left_to_delete.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/Assets/ui_swipe_left_to_delete.riv -------------------------------------------------------------------------------- /example/ios/Assets/v6_file.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/Assets/v6_file.riv -------------------------------------------------------------------------------- /example/ios/Podfile: -------------------------------------------------------------------------------- 1 | require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking") 2 | require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods") 3 | 4 | require 'json' 5 | podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} 6 | 7 | ENV['RCT_NEW_ARCH_ENABLED'] = podfile_properties['newArchEnabled'] == 'true' ? '1' : '0' 8 | ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] = podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] 9 | 10 | platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' 11 | install! 'cocoapods', 12 | :deterministic_uuids => false 13 | 14 | prepare_react_native_project! 15 | 16 | target 'example' do 17 | use_expo_modules! 18 | 19 | if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' 20 | config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; 21 | else 22 | config_command = [ 23 | 'node', 24 | '--no-warnings', 25 | '--eval', 26 | 'require(require.resolve(\'expo-modules-autolinking\', { paths: [require.resolve(\'expo/package.json\')] }))(process.argv.slice(1))', 27 | 'react-native-config', 28 | '--json', 29 | '--platform', 30 | 'ios' 31 | ] 32 | end 33 | 34 | config = use_native_modules!(config_command) 35 | 36 | use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] 37 | use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] 38 | 39 | use_react_native!( 40 | :path => config[:reactNativePath], 41 | :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', 42 | # An absolute path to your application root. 43 | :app_path => "#{Pod::Config.instance.installation_root}/..", 44 | :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', 45 | ) 46 | 47 | post_install do |installer| 48 | react_native_post_install( 49 | installer, 50 | config[:reactNativePath], 51 | :mac_catalyst_enabled => false, 52 | :ccache_enabled => podfile_properties['apple.ccacheEnabled'] == 'true', 53 | ) 54 | 55 | # This is necessary for Xcode 14, because it signs resource bundles by default 56 | # when building for devices. 57 | installer.target_installation_results.pod_target_installation_results 58 | .each do |pod_name, target_installation_result| 59 | target_installation_result.resource_bundle_targets.each do |resource_bundle_target| 60 | resource_bundle_target.build_configurations.each do |config| 61 | config.build_settings['CODE_SIGNING_ALLOWED'] = 'NO' 62 | end 63 | end 64 | end 65 | end 66 | end 67 | -------------------------------------------------------------------------------- /example/ios/Podfile.properties.json: -------------------------------------------------------------------------------- 1 | { 2 | "expo.jsEngine": "hermes", 3 | "EX_DEV_CLIENT_NETWORK_INSPECTOR": "true", 4 | "newArchEnabled": "true" 5 | } 6 | -------------------------------------------------------------------------------- /example/ios/example.xcodeproj/xcshareddata/xcschemes/example.xcscheme: -------------------------------------------------------------------------------- 1 | 2 | 5 | 8 | 9 | 15 | 21 | 22 | 23 | 24 | 25 | 30 | 31 | 33 | 39 | 40 | 41 | 42 | 43 | 53 | 55 | 61 | 62 | 63 | 64 | 70 | 72 | 78 | 79 | 80 | 81 | 83 | 84 | 87 | 88 | 89 | -------------------------------------------------------------------------------- /example/ios/example.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /example/ios/example.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /example/ios/example/AppDelegate.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | 5 | @interface AppDelegate : EXAppDelegateWrapper 6 | 7 | @end 8 | -------------------------------------------------------------------------------- /example/ios/example/AppDelegate.mm: -------------------------------------------------------------------------------- 1 | #import "AppDelegate.h" 2 | 3 | #import 4 | #import 5 | 6 | @implementation AppDelegate 7 | 8 | - (BOOL)application:(UIApplication *)application didFinishLaunchingWithOptions:(NSDictionary *)launchOptions 9 | { 10 | self.moduleName = @"main"; 11 | 12 | // You can add your custom initial props in the dictionary below. 13 | // They will be passed down to the ViewController used by React Native. 14 | self.initialProps = @{}; 15 | 16 | return [super application:application didFinishLaunchingWithOptions:launchOptions]; 17 | } 18 | 19 | - (NSURL *)sourceURLForBridge:(RCTBridge *)bridge 20 | { 21 | return [self bundleURL]; 22 | } 23 | 24 | - (NSURL *)bundleURL 25 | { 26 | #if DEBUG 27 | return [[RCTBundleURLProvider sharedSettings] jsBundleURLForBundleRoot:@".expo/.virtual-metro-entry"]; 28 | #else 29 | return [[NSBundle mainBundle] URLForResource:@"main" withExtension:@"jsbundle"]; 30 | #endif 31 | } 32 | 33 | // Linking API 34 | - (BOOL)application:(UIApplication *)application openURL:(NSURL *)url options:(NSDictionary *)options { 35 | return [super application:application openURL:url options:options] || [RCTLinkingManager application:application openURL:url options:options]; 36 | } 37 | 38 | // Universal Links 39 | - (BOOL)application:(UIApplication *)application continueUserActivity:(nonnull NSUserActivity *)userActivity restorationHandler:(nonnull void (^)(NSArray> * _Nullable))restorationHandler { 40 | BOOL result = [RCTLinkingManager application:application continueUserActivity:userActivity restorationHandler:restorationHandler]; 41 | return [super application:application continueUserActivity:userActivity restorationHandler:restorationHandler] || result; 42 | } 43 | 44 | // Explicitly define remote notification delegates to ensure compatibility with some third-party libraries 45 | - (void)application:(UIApplication *)application didRegisterForRemoteNotificationsWithDeviceToken:(NSData *)deviceToken 46 | { 47 | return [super application:application didRegisterForRemoteNotificationsWithDeviceToken:deviceToken]; 48 | } 49 | 50 | // Explicitly define remote notification delegates to ensure compatibility with some third-party libraries 51 | - (void)application:(UIApplication *)application didFailToRegisterForRemoteNotificationsWithError:(NSError *)error 52 | { 53 | return [super application:application didFailToRegisterForRemoteNotificationsWithError:error]; 54 | } 55 | 56 | // Explicitly define remote notification delegates to ensure compatibility with some third-party libraries 57 | - (void)application:(UIApplication *)application didReceiveRemoteNotification:(NSDictionary *)userInfo fetchCompletionHandler:(void (^)(UIBackgroundFetchResult))completionHandler 58 | { 59 | return [super application:application didReceiveRemoteNotification:userInfo fetchCompletionHandler:completionHandler]; 60 | } 61 | 62 | @end 63 | -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/AppIcon.appiconset/App-Icon-1024x1024@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/example/Images.xcassets/AppIcon.appiconset/App-Icon-1024x1024@1x.png -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "filename": "App-Icon-1024x1024@1x.png", 5 | "idiom": "universal", 6 | "platform": "ios", 7 | "size": "1024x1024" 8 | } 9 | ], 10 | "info": { 11 | "version": 1, 12 | "author": "expo" 13 | } 14 | } -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "version" : 1, 4 | "author" : "expo" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/SplashScreenBackground.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors": [ 3 | { 4 | "color": { 5 | "components": { 6 | "alpha": "1.000", 7 | "blue": "1.00000000000000", 8 | "green": "1.00000000000000", 9 | "red": "1.00000000000000" 10 | }, 11 | "color-space": "srgb" 12 | }, 13 | "idiom": "universal" 14 | } 15 | ], 16 | "info": { 17 | "version": 1, 18 | "author": "expo" 19 | } 20 | } -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/SplashScreenLogo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images": [ 3 | { 4 | "idiom": "universal", 5 | "filename": "image.png", 6 | "scale": "1x" 7 | }, 8 | { 9 | "idiom": "universal", 10 | "filename": "image@2x.png", 11 | "scale": "2x" 12 | }, 13 | { 14 | "idiom": "universal", 15 | "filename": "image@3x.png", 16 | "scale": "3x" 17 | } 18 | ], 19 | "info": { 20 | "version": 1, 21 | "author": "expo" 22 | } 23 | } -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/SplashScreenLogo.imageset/image.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/example/Images.xcassets/SplashScreenLogo.imageset/image.png -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/SplashScreenLogo.imageset/image@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/example/Images.xcassets/SplashScreenLogo.imageset/image@2x.png -------------------------------------------------------------------------------- /example/ios/example/Images.xcassets/SplashScreenLogo.imageset/image@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/example/Images.xcassets/SplashScreenLogo.imageset/image@3x.png -------------------------------------------------------------------------------- /example/ios/example/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CADisableMinimumFrameDurationOnPhone 6 | 7 | CFBundleDevelopmentRegion 8 | $(DEVELOPMENT_LANGUAGE) 9 | CFBundleDisplayName 10 | example 11 | CFBundleExecutable 12 | $(EXECUTABLE_NAME) 13 | CFBundleIdentifier 14 | $(PRODUCT_BUNDLE_IDENTIFIER) 15 | CFBundleInfoDictionaryVersion 16 | 6.0 17 | CFBundleName 18 | $(PRODUCT_NAME) 19 | CFBundlePackageType 20 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 21 | CFBundleShortVersionString 22 | 1.0.0 23 | CFBundleSignature 24 | ???? 25 | CFBundleURLTypes 26 | 27 | 28 | CFBundleURLSchemes 29 | 30 | myapp 31 | com.anonymous.example 32 | 33 | 34 | 35 | CFBundleVersion 36 | 1 37 | LSMinimumSystemVersion 38 | 12.0 39 | LSRequiresIPhoneOS 40 | 41 | NSAppTransportSecurity 42 | 43 | NSAllowsArbitraryLoads 44 | 45 | NSAllowsLocalNetworking 46 | 47 | 48 | NSUserActivityTypes 49 | 50 | $(PRODUCT_BUNDLE_IDENTIFIER).expo.index_route 51 | 52 | UILaunchStoryboardName 53 | SplashScreen 54 | UIRequiredDeviceCapabilities 55 | 56 | arm64 57 | 58 | UIRequiresFullScreen 59 | 60 | UIStatusBarStyle 61 | UIStatusBarStyleDefault 62 | UISupportedInterfaceOrientations 63 | 64 | UIInterfaceOrientationPortrait 65 | UIInterfaceOrientationPortraitUpsideDown 66 | 67 | UISupportedInterfaceOrientations~ipad 68 | 69 | UIInterfaceOrientationPortrait 70 | UIInterfaceOrientationPortraitUpsideDown 71 | UIInterfaceOrientationLandscapeLeft 72 | UIInterfaceOrientationLandscapeRight 73 | 74 | UIUserInterfaceStyle 75 | Automatic 76 | UIViewControllerBasedStatusBarAppearance 77 | 78 | 79 | 80 | -------------------------------------------------------------------------------- /example/ios/example/PrivacyInfo.xcprivacy: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | NSPrivacyAccessedAPITypes 6 | 7 | 8 | NSPrivacyAccessedAPIType 9 | NSPrivacyAccessedAPICategoryUserDefaults 10 | NSPrivacyAccessedAPITypeReasons 11 | 12 | CA92.1 13 | 14 | 15 | 16 | NSPrivacyAccessedAPIType 17 | NSPrivacyAccessedAPICategoryFileTimestamp 18 | NSPrivacyAccessedAPITypeReasons 19 | 20 | 0A2A.1 21 | 3B52.1 22 | C617.1 23 | 24 | 25 | 26 | NSPrivacyAccessedAPIType 27 | NSPrivacyAccessedAPICategoryDiskSpace 28 | NSPrivacyAccessedAPITypeReasons 29 | 30 | E174.1 31 | 85F4.1 32 | 33 | 34 | 35 | NSPrivacyAccessedAPIType 36 | NSPrivacyAccessedAPICategorySystemBootTime 37 | NSPrivacyAccessedAPITypeReasons 38 | 39 | 35F9.1 40 | 41 | 42 | 43 | NSPrivacyCollectedDataTypes 44 | 45 | NSPrivacyTracking 46 | 47 | 48 | 49 | -------------------------------------------------------------------------------- /example/ios/example/SplashScreen.storyboard: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /example/ios/example/Supporting/Expo.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | EXUpdatesCheckOnLaunch 6 | ALWAYS 7 | EXUpdatesEnabled 8 | 9 | EXUpdatesLaunchWaitMs 10 | 0 11 | 12 | -------------------------------------------------------------------------------- /example/ios/example/example-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | // 2 | // Use this file to import your target's public headers that you would like to expose to Swift. 3 | // 4 | -------------------------------------------------------------------------------- /example/ios/example/example.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | -------------------------------------------------------------------------------- /example/ios/example/main.m: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | #import "AppDelegate.h" 4 | 5 | int main(int argc, char * argv[]) { 6 | @autoreleasepool { 7 | return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class])); 8 | } 9 | } 10 | 11 | -------------------------------------------------------------------------------- /example/ios/example/noop-file.swift: -------------------------------------------------------------------------------- 1 | // 2 | // @generated 3 | // A blank Swift file must be created for native modules with Swift files to work correctly. 4 | // 5 | -------------------------------------------------------------------------------- /example/ios/rewards.riv: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rive-app/rive-react-native/578dd4981bec2ac6f92fed51b93f815dec0b6f4a/example/ios/rewards.riv -------------------------------------------------------------------------------- /example/metro.config.js: -------------------------------------------------------------------------------- 1 | const { getDefaultConfig } = require('expo/metro-config'); 2 | const path = require('path'); 3 | 4 | // Find the project and workspace directories 5 | const projectRoot = __dirname; 6 | // This can be replaced with `find-yarn-workspace-root` 7 | const monorepoRoot = path.resolve(projectRoot, '../'); 8 | 9 | const config = getDefaultConfig(projectRoot); 10 | 11 | // 1. Watch all files within the monorepo 12 | config.watchFolders = [monorepoRoot]; 13 | // 2. Let Metro know where to resolve packages and in what order 14 | config.resolver.nodeModulesPaths = [ 15 | path.resolve(projectRoot, 'node_modules'), 16 | path.resolve(monorepoRoot, 'node_modules'), 17 | ]; 18 | 19 | config.resolver.assetExts.push('riv'); 20 | 21 | module.exports = config; 22 | 23 | // const path = require('path'); 24 | 25 | // module.exports = { 26 | // resolver: { 27 | // extraNodeModules: { 28 | // 'rive-react-native': path.resolve(__dirname, '..'), 29 | // }, 30 | // }, 31 | // watchFolders: [path.resolve(__dirname, '..')], 32 | // }; 33 | -------------------------------------------------------------------------------- /example/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "example", 3 | "main": "expo-router/entry", 4 | "version": "1.0.0", 5 | "scripts": { 6 | "start": "expo start", 7 | "reset-project": "node ./scripts/reset-project.js", 8 | "android": "expo run:android", 9 | "ios": "expo run:ios", 10 | "web": "expo start --web", 11 | "test": "jest --watchAll", 12 | "lint": "expo lint" 13 | }, 14 | "installConfig": { 15 | "hoistingLimits": "workspaces" 16 | }, 17 | "jest": { 18 | "preset": "jest-expo" 19 | }, 20 | "dependencies": { 21 | "@expo/vector-icons": "^14.0.2", 22 | "@react-native-community/slider": "^4.5.6", 23 | "@react-native-picker/picker": "^2.11.0", 24 | "@react-navigation/bottom-tabs": "^7.2.0", 25 | "@react-navigation/native": "^7.0.14", 26 | "expo": "~52.0.35", 27 | "expo-blur": "~14.0.3", 28 | "expo-constants": "~17.0.6", 29 | "expo-font": "~13.0.3", 30 | "expo-haptics": "~14.0.1", 31 | "expo-linking": "~7.0.5", 32 | "expo-router": "~4.0.17", 33 | "expo-splash-screen": "~0.29.22", 34 | "expo-status-bar": "~2.0.1", 35 | "expo-symbols": "~0.2.2", 36 | "expo-system-ui": "~4.0.8", 37 | "expo-web-browser": "~14.0.2", 38 | "react": "18.3.1", 39 | "react-native": "0.76.7", 40 | "react-native-gesture-handler": "~2.20.2", 41 | "react-native-paper": "^5.13.1", 42 | "react-native-reanimated": "~3.16.1", 43 | "react-native-safe-area-context": "4.12.0", 44 | "react-native-screens": "~4.4.0", 45 | "react-native-webview": "13.12.5", 46 | "rive-react-native": "workspace:^" 47 | }, 48 | "devDependencies": { 49 | "@babel/core": "^7.25.2", 50 | "@types/jest": "^29.5.12", 51 | "@types/react": "~18.3.12", 52 | "jest": "^29.2.1", 53 | "jest-expo": "~52.0.4", 54 | "typescript": "^5.3.3" 55 | }, 56 | "private": true 57 | } 58 | -------------------------------------------------------------------------------- /example/scripts/reset-project.js: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env node 2 | 3 | /** 4 | * This script is used to reset the project to a blank state. 5 | * It deletes or moves the /app, /components, /hooks, /scripts, and /constants directories to /app-example based on user input and creates a new /app directory with an index.tsx and _layout.tsx file. 6 | * You can remove the `reset-project` script from package.json and safely delete this file after running it. 7 | */ 8 | 9 | const fs = require('fs'); 10 | const path = require('path'); 11 | const readline = require('readline'); 12 | 13 | const root = process.cwd(); 14 | const oldDirs = ['app', 'components', 'hooks', 'constants', 'scripts']; 15 | const exampleDir = 'app-example'; 16 | const newAppDir = 'app'; 17 | const exampleDirPath = path.join(root, exampleDir); 18 | 19 | const indexContent = `import { Text, View } from "react-native"; 20 | 21 | export default function Index() { 22 | return ( 23 | 30 | Edit app/index.tsx to edit this screen. 31 | 32 | ); 33 | } 34 | `; 35 | 36 | const layoutContent = `import { Stack } from "expo-router"; 37 | 38 | export default function RootLayout() { 39 | return ; 40 | } 41 | `; 42 | 43 | const rl = readline.createInterface({ 44 | input: process.stdin, 45 | output: process.stdout, 46 | }); 47 | 48 | const moveDirectories = async (userInput) => { 49 | try { 50 | if (userInput === 'y') { 51 | // Create the app-example directory 52 | await fs.promises.mkdir(exampleDirPath, { recursive: true }); 53 | console.log(`📁 /${exampleDir} directory created.`); 54 | } 55 | 56 | // Move old directories to new app-example directory or delete them 57 | for (const dir of oldDirs) { 58 | const oldDirPath = path.join(root, dir); 59 | if (fs.existsSync(oldDirPath)) { 60 | if (userInput === 'y') { 61 | const newDirPath = path.join(root, exampleDir, dir); 62 | await fs.promises.rename(oldDirPath, newDirPath); 63 | console.log(`➡️ /${dir} moved to /${exampleDir}/${dir}.`); 64 | } else { 65 | await fs.promises.rm(oldDirPath, { recursive: true, force: true }); 66 | console.log(`❌ /${dir} deleted.`); 67 | } 68 | } else { 69 | console.log(`➡️ /${dir} does not exist, skipping.`); 70 | } 71 | } 72 | 73 | // Create new /app directory 74 | const newAppDirPath = path.join(root, newAppDir); 75 | await fs.promises.mkdir(newAppDirPath, { recursive: true }); 76 | console.log('\n📁 New /app directory created.'); 77 | 78 | // Create index.tsx 79 | const indexPath = path.join(newAppDirPath, 'index.tsx'); 80 | await fs.promises.writeFile(indexPath, indexContent); 81 | console.log('📄 app/index.tsx created.'); 82 | 83 | // Create _layout.tsx 84 | const layoutPath = path.join(newAppDirPath, '_layout.tsx'); 85 | await fs.promises.writeFile(layoutPath, layoutContent); 86 | console.log('📄 app/_layout.tsx created.'); 87 | 88 | console.log('\n✅ Project reset complete. Next steps:'); 89 | console.log( 90 | `1. Run \`npx expo start\` to start a development server.\n2. Edit app/index.tsx to edit the main screen.${ 91 | userInput === 'y' 92 | ? `\n3. Delete the /${exampleDir} directory when you're done referencing it.` 93 | : '' 94 | }` 95 | ); 96 | } catch (error) { 97 | console.error(`❌ Error during script execution: ${error.message}`); 98 | } 99 | }; 100 | 101 | rl.question( 102 | 'Do you want to move existing files to /app-example instead of deleting them? (Y/n): ', 103 | (answer) => { 104 | const userInput = answer.trim().toLowerCase() || 'y'; 105 | if (userInput === 'y' || userInput === 'n') { 106 | moveDirectories(userInput).finally(() => rl.close()); 107 | } else { 108 | console.log("❌ Invalid input. Please enter 'Y' or 'N'."); 109 | rl.close(); 110 | } 111 | } 112 | ); 113 | -------------------------------------------------------------------------------- /example/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "expo/tsconfig.base", 3 | "compilerOptions": { 4 | "strict": true, 5 | "paths": { 6 | "@/*": ["./*"], 7 | "rive-react-native": ["../src/index"] 8 | } 9 | }, 10 | "include": [ 11 | "**/*.ts", 12 | "**/*.tsx", 13 | ".expo/types/**/*.ts", 14 | "expo-env.d.ts" 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /ios/RCTLog.swift: -------------------------------------------------------------------------------- 1 | func RCTLogError(_ message: String, _ file: String=#file, _ line: UInt=#line) { 2 | DispatchQueue.main.async { 3 | RCTSwiftLog.error(message, file: file, line: line) 4 | } 5 | 6 | } 7 | 8 | func RCTLogWarn(_ message: String, _ file: String=#file, _ line: UInt=#line) { 9 | DispatchQueue.main.async { 10 | RCTSwiftLog.warn(message, file: file, line: line) 11 | } 12 | } 13 | 14 | func RCTLogInfo(_ message: String, _ file: String=#file, _ line: UInt=#line) { 15 | DispatchQueue.main.async { 16 | RCTSwiftLog.info(message, file: file, line: line) 17 | } 18 | } 19 | 20 | func RCTLog(_ message: String, _ file: String=#file, _ line: UInt=#line) { 21 | DispatchQueue.main.async { 22 | RCTSwiftLog.log(message, file: file, line: line) 23 | } 24 | } 25 | 26 | func RCTLogTrace(_ message: String, _ file: String=#file, _ line: UInt=#line) { 27 | DispatchQueue.main.async { 28 | RCTSwiftLog.trace(message, file: file, line: line) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ios/RCTSwiftLog.h: -------------------------------------------------------------------------------- 1 | #import 2 | 3 | @interface RCTSwiftLog : NSObject 4 | 5 | + (void)error:(NSString * _Nonnull)message file:(NSString * _Nonnull)file line:(NSUInteger)line; 6 | + (void)warn:(NSString * _Nonnull)message file:(NSString * _Nonnull)file line:(NSUInteger)line; 7 | + (void)info:(NSString * _Nonnull)message file:(NSString * _Nonnull)file line:(NSUInteger)line; 8 | + (void)log:(NSString * _Nonnull)message file:(NSString * _Nonnull)file line:(NSUInteger)line; 9 | + (void)trace:(NSString * _Nonnull)message file:(NSString * _Nonnull)file line:(NSUInteger)line; 10 | 11 | @end -------------------------------------------------------------------------------- /ios/RCTSwiftLog.m: -------------------------------------------------------------------------------- 1 | // 2 | // RCTSwiftLog.m 3 | // 4 | // 5 | // 6 | 7 | #import 8 | 9 | #import "RCTSwiftLog.h" 10 | 11 | @implementation RCTSwiftLog 12 | 13 | + (void)info:(NSString *)message file:(NSString *)file line:(NSUInteger)line 14 | { 15 | _RCTLogNativeInternal(RCTLogLevelInfo, file.UTF8String, (int)line, @"%@", message); 16 | } 17 | 18 | + (void)warn:(NSString *)message file:(NSString *)file line:(NSUInteger)line 19 | { 20 | _RCTLogNativeInternal(RCTLogLevelWarning, file.UTF8String, (int)line, @"%@", message); 21 | } 22 | 23 | + (void)error:(NSString *)message file:(NSString *)file line:(NSUInteger)line 24 | { 25 | _RCTLogNativeInternal(RCTLogLevelError, file.UTF8String, (int)line, @"%@", message); 26 | } 27 | 28 | + (void)log:(NSString *)message file:(NSString *)file line:(NSUInteger)line 29 | { 30 | _RCTLogNativeInternal(RCTLogLevelInfo, file.UTF8String, (int)line, @"%@", message); 31 | } 32 | 33 | + (void)trace:(NSString *)message file:(NSString *)file line:(NSUInteger)line 34 | { 35 | _RCTLogNativeInternal(RCTLogLevelTrace, file.UTF8String, (int)line, @"%@", message); 36 | } 37 | 38 | @end -------------------------------------------------------------------------------- /ios/RNAlignment.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import RiveRuntime 3 | 4 | enum RNAlignment: String { 5 | case TopLeft = "topLeft" 6 | case TopCenter = "topCenter" 7 | case TopRight = "topRight" 8 | case CenterLeft = "centerLeft" 9 | case Center = "center" 10 | case CenterRight = "centerRight" 11 | case BottomLeft = "bottomLeft" 12 | case BottomCenter = "bottomCenter" 13 | case BottomRight = "bottomRight" 14 | 15 | static func mapToRNAlignment(value: String) -> RNAlignment { 16 | if let rnEnum = RNAlignment(rawValue: value) { 17 | return rnEnum 18 | } else { 19 | fatalError("Unsupported alignment type: \(value)") 20 | } 21 | } 22 | 23 | static func mapToRiveAlignment(rnAlignment: RNAlignment) -> RiveAlignment { 24 | switch rnAlignment { 25 | case .TopLeft: 26 | return RiveAlignment.topLeft 27 | case .TopCenter: 28 | return RiveAlignment.topCenter 29 | case .TopRight: 30 | return RiveAlignment.topRight 31 | case .CenterLeft: 32 | return RiveAlignment.centerLeft 33 | case .Center: 34 | return RiveAlignment.center 35 | case .CenterRight: 36 | return RiveAlignment.centerRight 37 | case .BottomLeft: 38 | return RiveAlignment.bottomLeft 39 | case .BottomCenter: 40 | return RiveAlignment.bottomCenter 41 | case .BottomRight: 42 | return RiveAlignment.bottomRight 43 | } 44 | } 45 | } 46 | 47 | -------------------------------------------------------------------------------- /ios/RNDirection.swift: -------------------------------------------------------------------------------- 1 | enum RNDirection: String { 2 | case Backwards = "backwards" 3 | case Auto = "auto" 4 | case Forwards = "forwards" 5 | 6 | static func mapToRNDirection(value: String) -> RNDirection { 7 | if let rnEnum = RNDirection(rawValue: value) { 8 | return rnEnum 9 | } else { 10 | fatalError("Unsupported direction type: \(value)") 11 | } 12 | } 13 | 14 | static func mapToRiveDirection(rnDirection: RNDirection) -> RiveDirection { 15 | switch rnDirection { 16 | case .Backwards: 17 | return .backwards 18 | case .Auto: 19 | return .autoDirection 20 | case .Forwards: 21 | return .forwards 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /ios/RNFit.swift: -------------------------------------------------------------------------------- 1 | import Foundation 2 | import RiveRuntime 3 | 4 | enum RNFit: String { 5 | case Contain = "contain" 6 | case Cover = "cover" 7 | case Fill = "fill" 8 | case FitWidth = "fitWidth" 9 | case FitHeight = "fitHeight" 10 | case None = "none" 11 | case ScaleDown = "scaleDown" 12 | case Layout = "layout" 13 | 14 | static func mapToRNFit(value: String) -> RNFit { 15 | if let rnEnum = RNFit(rawValue: value) { 16 | return rnEnum 17 | } else { 18 | fatalError("Unsupported fit type: \(value)") 19 | } 20 | } 21 | 22 | static func mapToRiveFit(rnFit: RNFit) -> RiveFit { 23 | switch rnFit { 24 | case .Contain: 25 | return RiveFit.contain 26 | case .Cover: 27 | return RiveFit.cover 28 | case .Fill: 29 | return RiveFit.fill 30 | case .FitWidth: 31 | return RiveFit.fitWidth 32 | case .FitHeight: 33 | return RiveFit.fitHeight 34 | case .None: 35 | return RiveFit.noFit 36 | case .ScaleDown: 37 | return RiveFit.scaleDown 38 | case .Layout: 39 | return RiveFit.layout 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /ios/RNLoopMode.swift: -------------------------------------------------------------------------------- 1 | enum RNLoopMode: String { 2 | case OneShot = "oneShot" 3 | case Loop = "loop" 4 | case PingPong = "pingPong" 5 | case Auto = "auto" 6 | 7 | static func mapToRNLoopMode(value: String) -> RNLoopMode { 8 | if let rnEnum = RNLoopMode(rawValue: value) { 9 | return rnEnum 10 | } else { 11 | fatalError("Unsupported loop mode type: \(value)") 12 | } 13 | } 14 | 15 | 16 | static func mapToRNLoopMode(value: Int) -> RNLoopMode { 17 | if let riveEnum = RiveRuntime.RiveLoop(rawValue: value) { 18 | switch (riveEnum) { 19 | case .oneShot: 20 | return .OneShot 21 | case .loop: 22 | return .Loop 23 | case .pingPong: 24 | return .PingPong 25 | case .autoLoop: 26 | return .Auto 27 | default: 28 | return .Auto 29 | } 30 | 31 | 32 | } else { 33 | fatalError("Unsupported loop mode type: \(value)") 34 | } 35 | } 36 | 37 | static func mapToRiveLoop(rnLoopMode: RNLoopMode) -> RiveLoop { 38 | switch rnLoopMode { 39 | case .OneShot: 40 | return .oneShot 41 | case .Loop: 42 | return .loop 43 | case .PingPong: 44 | return .pingPong 45 | case .Auto: 46 | return .autoLoop 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /ios/RNPropertyType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RNPropertyType.swift 3 | // rive-react-native 4 | // 5 | // Created by Peter G Hayes on 02/05/2025. 6 | // 7 | 8 | import Foundation 9 | 10 | enum RNPropertyType: String { 11 | case Number = "number" 12 | case String = "string" 13 | case Boolean = "boolean" 14 | case Color = "color" 15 | case Trigger = "trigger" 16 | case Enum = "enum" 17 | 18 | static func mapToRNPropertyType(value: String) -> RNPropertyType? { 19 | if let rnEnum = RNPropertyType(rawValue: value) { 20 | return rnEnum 21 | } else { 22 | return nil 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /ios/RNRiveError.swift: -------------------------------------------------------------------------------- 1 | struct BaseRNRiveError { 2 | let type: String; 3 | var message: String = "Default Message" 4 | } 5 | 6 | 7 | struct RNRiveError { 8 | static let FileNotFound = BaseRNRiveError(type: "FileNotFound") 9 | static let UnsupportedRuntimeVersion = BaseRNRiveError(type: "UnsupportedRuntimeVersion") 10 | static let IncorrectAnimationName = BaseRNRiveError(type: "IncorrectAnimationName") 11 | static let MalformedFile = BaseRNRiveError(type: "MalformedFile") 12 | static let IncorrectRiveFileUrl = BaseRNRiveError(type: "IncorrectRiveFileUrl") 13 | static let IncorrectArtboardName = BaseRNRiveError(type: "IncorrectArtboardName") 14 | static let IncorrectStateMachineName = BaseRNRiveError(type: "IncorrectStateMachineName") 15 | static let IncorrectStateMachineInput = BaseRNRiveError(type: "IncorrectStateMachineInput") 16 | static let TextRunNotFoundError = BaseRNRiveError(type: "TextRunNotFoundError") 17 | static let DataBindingError = BaseRNRiveError(type: "DataBindingError") 18 | 19 | 20 | static func mapToRNRiveError(riveError: NSError) -> BaseRNRiveError? { 21 | let riveErrorName = riveError.userInfo["name"] as! String 22 | var resultError: BaseRNRiveError? = nil 23 | switch riveErrorName { 24 | case "UnsupportedVersion": 25 | resultError = RNRiveError.UnsupportedRuntimeVersion 26 | break; 27 | case "Malformed": 28 | resultError = RNRiveError.MalformedFile 29 | break 30 | case "FileNotFound": 31 | resultError = RNRiveError.FileNotFound 32 | break; 33 | case "IncorrectRiveFileURL": 34 | resultError = RNRiveError.IncorrectRiveFileUrl 35 | break; 36 | case "NoAnimationFound": 37 | resultError = RNRiveError.IncorrectAnimationName 38 | break; 39 | case "NoArtboardFound": 40 | resultError = RNRiveError.IncorrectArtboardName 41 | break; 42 | case "NoStateMachineFound": 43 | resultError = RNRiveError.IncorrectStateMachineName 44 | break; 45 | case "NoStateMachineInputFound": 46 | resultError = RNRiveError.IncorrectStateMachineInput 47 | break; 48 | case "TextRunNotFoundError": 49 | resultError = RNRiveError.TextRunNotFoundError 50 | break; 51 | case "DataBindingError": 52 | resultError = RNRiveError.DataBindingError 53 | break; 54 | default: 55 | return nil 56 | } 57 | resultError?.message = riveError.localizedDescription 58 | return resultError 59 | } 60 | } 61 | 62 | 63 | func createFileNotFoundError() -> NSError { 64 | return NSError(domain: RiveErrorDomain, code: 800, userInfo: [NSLocalizedDescriptionKey: "File not found", "name": "FileNotFound"]) 65 | } 66 | 67 | func createMalformedFileError() -> NSError { 68 | return NSError(domain: RiveErrorDomain, code: RiveErrorCode.malformedFile.rawValue, userInfo: [NSLocalizedDescriptionKey: "Malformed Rive File", "name": "Malformed"]) 69 | } 70 | 71 | func createAssetFileError(_ assetName: String) -> NSError { 72 | return NSError(domain: RiveErrorDomain, code: 801, userInfo: [NSLocalizedDescriptionKey: "Could not load Rive asset: \(assetName)", "name": "FileNotFound"]) 73 | } 74 | 75 | func createIncorrectRiveURL(_ url: String) -> NSError { 76 | return NSError(domain: RiveErrorDomain, code: 900, userInfo: [NSLocalizedDescriptionKey: "Unable to download Rive file from: \(url)", "name": "IncorrectRiveFileURL"]) 77 | } 78 | -------------------------------------------------------------------------------- /ios/RNRiveRendererType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RNRiveRendererType.swift 3 | // rive-react-native 4 | // 5 | // Created by Peter G Hayes on 31/05/2024. 6 | // 7 | 8 | import Foundation 9 | import RiveRuntime 10 | 11 | enum RNRiveRendererType: String { 12 | case Rive = "rive" 13 | case CoreGraphics = "coreGraphics" 14 | 15 | static func mapToRNRiveRendererType(value: String) -> RNRiveRendererType { 16 | if let rnEnum = RNRiveRendererType(rawValue: value) { 17 | return rnEnum 18 | } else { 19 | fatalError("Unsupported renderer type: \(value)") 20 | } 21 | } 22 | 23 | static func mapToRendererType(rnRendererType: RNRiveRendererType) -> RendererType { 24 | switch rnRendererType { 25 | case .Rive: 26 | return RendererType.riveRenderer 27 | case .CoreGraphics: 28 | return RendererType.cgRenderer 29 | } 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /ios/RiveReactNative-Bridging-Header.h: -------------------------------------------------------------------------------- 1 | #import 2 | #import 3 | #import 4 | #import 5 | #import 6 | #import 7 | #import 8 | #import 9 | -------------------------------------------------------------------------------- /ios/RiveReactNativeEventModule.h: -------------------------------------------------------------------------------- 1 | // 2 | // RiveReactNativeEventModule.h 3 | // RiveReactNative 4 | // 5 | // Created by Peter G Hayes on 05/05/2025. 6 | // Copyright © 2025 Facebook. All rights reserved. 7 | // 8 | 9 | #ifndef RiveReactNativeEventModule_h 10 | #define RiveReactNativeEventModule_h 11 | 12 | #import 13 | #import 14 | 15 | @interface RiveReactNativeEventModule : RCTEventEmitter 16 | - (BOOL)isListenerActive:(NSString *)eventName; 17 | - (void)removeListenerByName:(NSString *)eventName; 18 | @end 19 | 20 | #endif /* RiveReactNativeEventModule_h */ 21 | -------------------------------------------------------------------------------- /ios/RiveReactNativeEventModule.m: -------------------------------------------------------------------------------- 1 | // 2 | // RiveReactNativeEventModule.m 3 | // RiveReactNative 4 | // 5 | // Created by Peter G Hayes on 05/05/2025. 6 | // Copyright © 2025 Facebook. All rights reserved. 7 | // 8 | 9 | #import "RiveReactNativeEventModule.h" 10 | 11 | @implementation RiveReactNativeEventModule { 12 | BOOL hasListeners; 13 | NSMutableSet *_activeListeners; 14 | } 15 | 16 | RCT_EXPORT_MODULE(); 17 | 18 | - (instancetype)init { 19 | self = [super init]; 20 | if (self) { 21 | _activeListeners = [NSMutableSet new]; 22 | } 23 | return self; 24 | } 25 | 26 | // Called by React Native to determine which listeners are supported in JS 27 | - (NSArray *)supportedEvents { 28 | return _activeListeners.allObjects; 29 | } 30 | 31 | // Called by React Native when a listener is added from JS 32 | - (void)addListener:(NSString *)eventName { 33 | [_activeListeners addObject:eventName]; 34 | [super addListener:eventName]; 35 | } 36 | 37 | // Called by React Native when removeListeners is called from JS 38 | // We intentionally do not clean up all the listeners in 39 | // _activeListeners, as this object is shared between all 40 | // created views. We only ever clean up particular listeners 41 | // tied to a view. See removeListenerByName. 42 | - (void)removeListeners:(double)count { 43 | [super removeListeners:count]; 44 | } 45 | 46 | // Used to manually clean up the listeners when a view is disposed 47 | - (void)removeListenerByName:(NSString *)eventName { 48 | [_activeListeners removeObject:eventName]; 49 | } 50 | 51 | // Used to determine if a particular listener has already been added. 52 | // Used in RiveReactNativeView to send a "loaded" event to JS when Rive 53 | // is ready, which we only want to do if the listener was actually added 54 | // from JS (as part of the useRive() hook). 55 | - (BOOL)isListenerActive:(NSString *)eventName { 56 | return [_activeListeners containsObject:eventName]; 57 | } 58 | 59 | @end 60 | -------------------------------------------------------------------------------- /ios/RiveReactNativeModule.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RiveReactNativeModule.swift 3 | // RiveReactNative 4 | // 5 | // Created by Peter G Hayes on 07/03/2025. 6 | // Copyright © 2025 Facebook. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | 11 | @objc(RiveReactNativeModule) 12 | class RiveReactNativeModule: NSObject, RCTBridgeModule { 13 | static func moduleName() -> String! { 14 | return "RiveReactNativeModule"; 15 | } 16 | 17 | @objc weak var bridge: RCTBridge? 18 | 19 | @objc(getBooleanState:inputName:resolver:rejecter:) 20 | func getBooleanState(_ node: NSNumber, inputName: String, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) -> Void { 21 | 22 | DispatchQueue.main.async { 23 | guard let bridge = self.bridge, 24 | let view = bridge.uiManager.view(forReactTag: node) as? RiveReactNativeView else { 25 | rejecter("VIEW_NOT_FOUND", "Could not find RiveReactNativeView", nil) 26 | return 27 | } 28 | let value = view.getBooleanState(inputName: inputName) 29 | resolver(value) 30 | } 31 | } 32 | 33 | @objc(getNumberState:inputName:resolver:rejecter:) 34 | func getNumberState(_ node: NSNumber, inputName: String, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) -> Void { 35 | 36 | DispatchQueue.main.async { 37 | guard let bridge = self.bridge, 38 | let view = bridge.uiManager.view(forReactTag: node) as? RiveReactNativeView else { 39 | rejecter("VIEW_NOT_FOUND", "Could not find RiveReactNativeView", nil) 40 | return 41 | } 42 | let value = view.getNumberState(inputName: inputName) 43 | resolver(value) 44 | } 45 | } 46 | 47 | @objc(getBooleanStateAtPath:inputName:path:resolver:rejecter:) 48 | func getBooleanStateAtPath(_ node: NSNumber, inputName: String, path: String, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) -> Void { 49 | 50 | DispatchQueue.main.async { 51 | guard let bridge = self.bridge, 52 | let view = bridge.uiManager.view(forReactTag: node) as? RiveReactNativeView else { 53 | rejecter("VIEW_NOT_FOUND", "Could not find RiveReactNativeView", nil) 54 | return 55 | } 56 | let value = view.getBooleanStateAtPath(inputName: inputName, path: path) 57 | resolver(value) 58 | } 59 | } 60 | 61 | @objc(getNumberStateAtPath:inputName:path:resolver:rejecter:) 62 | func getNumberStateAtPath(_ node: NSNumber, inputName: String, path: String, resolver: @escaping RCTPromiseResolveBlock, rejecter: @escaping RCTPromiseRejectBlock) -> Void { 63 | 64 | DispatchQueue.main.async { 65 | guard let bridge = self.bridge, 66 | let view = bridge.uiManager.view(forReactTag: node) as? RiveReactNativeView else { 67 | rejecter("VIEW_NOT_FOUND", "Could not find RiveReactNativeView", nil) 68 | return 69 | } 70 | let value = view.getNumberStateAtPath(inputName: inputName, path: path) 71 | resolver(value) 72 | } 73 | } 74 | 75 | // Required to register the module with React Native 76 | @objc static func requiresMainQueueSetup() -> Bool { 77 | return true 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /ios/RiveReactNativeModuleBridge.m: -------------------------------------------------------------------------------- 1 | // 2 | // RiveReactNativeModuleBridge.m 3 | // RiveReactNative 4 | // 5 | // Created by Peter G Hayes on 07/03/2025. 6 | // Copyright © 2025 Facebook. All rights reserved. 7 | // 8 | 9 | #import 10 | 11 | @interface RCT_EXTERN_MODULE(RiveReactNativeModule, NSObject) 12 | 13 | RCT_EXTERN_METHOD(getBooleanState:(nonnull NSNumber *)node inputName:(nonnull NSString *)inputName resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) 14 | RCT_EXTERN_METHOD(getNumberState:(nonnull NSNumber *)node inputName:(nonnull NSString *)inputName resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) 15 | RCT_EXTERN_METHOD(getBooleanStateAtPath:(nonnull NSNumber *)node inputName:(nonnull NSString *)inputName path:(nonnull NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) 16 | RCT_EXTERN_METHOD(getNumberStateAtPath:(nonnull NSNumber *)node inputName:(nonnull NSString *)inputName path:(nonnull NSString *)path resolver:(RCTPromiseResolveBlock)resolve rejecter:(RCTPromiseRejectBlock)reject) 17 | 18 | @end 19 | -------------------------------------------------------------------------------- /ios/RiveReactNativeRendererModule.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RiveReactNativeRendererModule.swift 3 | // rive-react-native 4 | // 5 | // Created by Peter G Hayes on 31/05/2024. 6 | // 7 | 8 | import Foundation 9 | import RiveRuntime 10 | 11 | @objc(RiveReactNativeRendererModule) 12 | class RiveReactNativeRendererModule: NSObject { 13 | 14 | @objc(defaultRenderer:androidRenderer:) 15 | func defaultRenderer(_ iosRenderer: String, androidRenderer: String) -> Void { 16 | let rnRendererType = RNRiveRendererType.mapToRNRiveRendererType(value: iosRenderer) 17 | RenderContextManager.shared().defaultRenderer = RNRiveRendererType.mapToRendererType(rnRendererType: rnRendererType) 18 | } 19 | // Required to register the module with React Native 20 | @objc static func requiresMainQueueSetup() -> Bool { 21 | return true 22 | } 23 | 24 | } 25 | -------------------------------------------------------------------------------- /ios/RiveReactNativeRendererModuleBridge.m: -------------------------------------------------------------------------------- 1 | // 2 | // RiveReactNativeRendererModuleBridge.m 3 | // rive-react-native 4 | // 5 | // Created by Peter G Hayes on 31/05/2024. 6 | // 7 | 8 | #import 9 | 10 | @interface RCT_EXTERN_MODULE(RiveReactNativeRendererModule, NSObject) 11 | 12 | RCT_EXTERN_METHOD(defaultRenderer:(NSString *)iosRenderer androidRenderer:(NSString *)androidRenderer) 13 | 14 | @end 15 | -------------------------------------------------------------------------------- /ios/RiveReactNativeViewManager.m: -------------------------------------------------------------------------------- 1 | 2 | #import 3 | 4 | @interface RCT_EXTERN_MODULE(RiveReactNativeViewManager, RCTViewManager) 5 | 6 | RCT_EXPORT_VIEW_PROPERTY(resourceName, NSString) 7 | RCT_EXPORT_VIEW_PROPERTY(url, NSString) 8 | RCT_EXPORT_VIEW_PROPERTY(fit, NSString) 9 | RCT_EXPORT_VIEW_PROPERTY(layoutScaleFactor, NSNumber) 10 | RCT_EXPORT_VIEW_PROPERTY(alignment, NSString) 11 | RCT_EXPORT_VIEW_PROPERTY(autoplay, BOOL) 12 | RCT_EXPORT_VIEW_PROPERTY(artboardName, NSString) 13 | RCT_EXPORT_VIEW_PROPERTY(referencedAssets, NSDictionary) 14 | RCT_EXPORT_VIEW_PROPERTY(dataBinding, NSDictionary) 15 | RCT_EXPORT_VIEW_PROPERTY(animationName, NSString) 16 | RCT_EXPORT_VIEW_PROPERTY(stateMachineName, NSString) 17 | RCT_EXPORT_VIEW_PROPERTY(isUserHandlingErrors, BOOL) 18 | RCT_EXPORT_VIEW_PROPERTY(onPlay, RCTDirectEventBlock) 19 | RCT_EXPORT_VIEW_PROPERTY(onPause, RCTDirectEventBlock) 20 | RCT_EXPORT_VIEW_PROPERTY(onStop, RCTDirectEventBlock) 21 | RCT_EXPORT_VIEW_PROPERTY(onLoopEnd, RCTDirectEventBlock) 22 | RCT_EXPORT_VIEW_PROPERTY(onStateChanged, RCTDirectEventBlock) 23 | RCT_EXPORT_VIEW_PROPERTY(onError, RCTDirectEventBlock) 24 | RCT_EXPORT_VIEW_PROPERTY(onRiveEventReceived, RCTDirectEventBlock) 25 | 26 | RCT_EXTERN_METHOD(play:(nonnull NSNumber *)node animationName:(nonnull NSString)animationName loop:(NSString)loopMode direction:(NSString)direction isStateMachine:(BOOL)isStateMachine) 27 | RCT_EXTERN_METHOD(pause:(nonnull NSNumber *)node) 28 | RCT_EXTERN_METHOD(stop:(nonnull NSNumber *)node) 29 | RCT_EXTERN_METHOD(reset:(nonnull NSNumber *)node) 30 | RCT_EXTERN_METHOD(fireState:(nonnull NSNumber *)node stateMachineName:(nonnull NSString)stateMachineName inputName:(nonnull NSString)inputName) 31 | RCT_EXTERN_METHOD(setBooleanState:(nonnull NSNumber *)node stateMachineName:(nonnull NSString)stateMachineName inputName:(nonnull NSString)inputName value:(BOOL)value) 32 | RCT_EXTERN_METHOD(setNumberState:(nonnull NSNumber *)node stateMachineName:(nonnull NSString)stateMachineName inputName:(nonnull NSString)inputName value:(nonnull NSNumber *)value) 33 | RCT_EXTERN_METHOD(fireStateAtPath:(nonnull NSNumber *)node inputName:(nonnull NSString)inputName path:(nonnull NSString)path) 34 | RCT_EXTERN_METHOD(setBooleanStateAtPath:(nonnull NSNumber *)node inputName:(nonnull NSString)inputName value:(BOOL)value path:(nonnull NSString)path) 35 | RCT_EXTERN_METHOD(setNumberStateAtPath:(nonnull NSNumber *)node inputName:(nonnull NSString)inputName value:(nonnull NSNumber *)value path:(nonnull NSString)path) 36 | RCT_EXTERN_METHOD(touchBegan:(nonnull NSNumber *)node x:(nonnull NSNumber*)x y:(nonnull NSNumber*)y) 37 | RCT_EXTERN_METHOD(touchMoved:(nonnull NSNumber *)node x:(nonnull NSNumber*)x y:(nonnull NSNumber*)y) 38 | RCT_EXTERN_METHOD(touchEnded:(nonnull NSNumber *)node x:(nonnull NSNumber*)x y:(nonnull NSNumber*)y) 39 | RCT_EXTERN_METHOD(touchCancelled:(nonnull NSNumber *)node x:(nonnull NSNumber*)x y:(nonnull NSNumber*)y) 40 | RCT_EXTERN_METHOD(setTextRunValue:(nonnull NSNumber *)node textRunName:(nonnull NSString)textRunName textRunValue:(nonnull NSString)textRunValue) 41 | RCT_EXTERN_METHOD(setTextRunValueAtPath:(nonnull NSNumber *)node textRunName:(nonnull NSString)textRunName textRunValue:(nonnull NSString)textRunValue path:(nonnull NSString)path) 42 | RCT_EXTERN_METHOD(setBooleanPropertyValue:(nonnull NSNumber *)node path:(nonnull NSString)path value:(BOOL)value) 43 | RCT_EXTERN_METHOD(setStringPropertyValue:(nonnull NSNumber *)node path:(nonnull NSString)path value:(nonnull NSString)value) 44 | RCT_EXTERN_METHOD(setNumberPropertyValue:(nonnull NSNumber *)node path:(nonnull NSString)path value:(nonnull NSNumber *)value) 45 | RCT_EXTERN_METHOD(setColorPropertyValue:(nonnull NSNumber *)node path:(nonnull NSString)path r:(nonnull NSNumber *)r g:(nonnull NSNumber *)g b:(nonnull NSNumber *)b a:(nonnull NSNumber *)a) 46 | RCT_EXTERN_METHOD(setEnumPropertyValue:(nonnull NSNumber *)node path:(nonnull NSString)path value:(nonnull NSString)value) 47 | RCT_EXTERN_METHOD(fireTriggerProperty:(nonnull NSNumber *)node path:(nonnull NSString)path) 48 | RCT_EXTERN_METHOD(registerPropertyListener:(nonnull NSNumber *)node path:(nonnull NSString)path propertyType:(nonnull NSString)propertyType) 49 | @end 50 | 51 | -------------------------------------------------------------------------------- /ios/utility.swift: -------------------------------------------------------------------------------- 1 | func getRiveFile(resourceName: String, resourceExt: String=".riv") throws -> RiveFile { 2 | guard let url = Bundle.main.url(forResource: resourceName, withExtension: resourceExt) else { 3 | throw createFileNotFoundError() 4 | } 5 | return try importRiveFile(from: url) 6 | } 7 | 8 | func getRiveURLResource(from urlString: String) throws -> RiveFile { 9 | guard let url = URL.init(string: urlString) else { 10 | throw createIncorrectRiveURL(urlString) 11 | 12 | } 13 | 14 | return try importRiveFile(from: url) 15 | } 16 | 17 | func importRiveFile(from url: URL) throws -> RiveFile { 18 | guard var data = try? Data(contentsOf: url) else { 19 | throw createIncorrectRiveURL(url.absoluteString) 20 | } 21 | let bytes = [UInt8](data) 22 | 23 | return try data.withUnsafeMutableBytes{(riveBytes:UnsafeMutableRawBufferPointer) throws ->RiveFile in 24 | guard let rawPointer = riveBytes.baseAddress else { 25 | throw createMalformedFileError() 26 | } 27 | let pointer = rawPointer.bindMemory(to: UInt8.self, capacity: bytes.count) 28 | 29 | let riveFile = try RiveFile(bytes:pointer, byteLength: UInt64(bytes.count), loadCdn: true) 30 | return riveFile 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "rive-react-native", 3 | "version": "9.3.4", 4 | "workspaces": [ 5 | "example" 6 | ], 7 | "description": "Rive React Native", 8 | "source": "src/index.tsx", 9 | "main": "lib/commonjs/index.js", 10 | "module": "lib/module/index.js", 11 | "types": "lib/typescript/index.d.ts", 12 | "react-native": "src/index.tsx", 13 | "files": [ 14 | "src", 15 | "lib", 16 | "android", 17 | "ios", 18 | "cpp", 19 | "rive-react-native.podspec", 20 | "!lib/typescript/example", 21 | "!android/build", 22 | "!ios/build", 23 | "!**/__tests__", 24 | "!**/__fixtures__", 25 | "!**/__mocks__" 26 | ], 27 | "scripts": { 28 | "test": "jest", 29 | "typescript": "tsc --noEmit", 30 | "lint": "eslint \"**/*.{js,ts,tsx}\"", 31 | "prepare": "bob build", 32 | "release": "release-it", 33 | "example": "yarn --cwd example", 34 | "pods": "cd example && pod-install --quiet", 35 | "bootstrap": "yarn example && yarn && yarn pods" 36 | }, 37 | "keywords": [ 38 | "react-native", 39 | "ios", 40 | "android" 41 | ], 42 | "repository": "https://github.com/rive-app/rive-react-native", 43 | "author": "TMaszko (https://github.com/rive-app/rive-react-native)", 44 | "license": "MIT", 45 | "bugs": { 46 | "url": "https://github.com/rive-app/rive-react-native/issues" 47 | }, 48 | "homepage": "https://github.com/rive-app/rive-react-native#readme", 49 | "publishConfig": { 50 | "registry": "https://registry.npmjs.org/" 51 | }, 52 | "devDependencies": { 53 | "@commitlint/config-conventional": "^19.5.0", 54 | "@react-native/eslint-config": "^0.73.1", 55 | "@release-it/conventional-changelog": "^9.0.2", 56 | "@types/jest": "^29.5.14", 57 | "@types/react": "~18.3.12", 58 | "@types/react-native": "0.72.2", 59 | "commitlint": "^17.0.2", 60 | "eslint": "^8.51.0", 61 | "eslint-config-prettier": "^9.0.0", 62 | "eslint-plugin-prettier": "^5.0.1", 63 | "husky": "^4.2.5", 64 | "jest": "^29.7.0", 65 | "metro-react-native-babel-preset": "^0.73.10", 66 | "pod-install": "^0.1.0", 67 | "prettier": "^3.0.3", 68 | "react-native-builder-bob": "^0.20.4", 69 | "release-it": "^17.10.0", 70 | "typescript": "^5.2.2" 71 | }, 72 | "peerDependencies": { 73 | "react": "*", 74 | "react-native": "*" 75 | }, 76 | "engines": { 77 | "node": ">=16" 78 | }, 79 | "resolutions": { 80 | "@types/react": "^18.2.44", 81 | "conventional-changelog-conventionalcommits": "8.0.0" 82 | }, 83 | "commitlint": { 84 | "extends": [ 85 | "@commitlint/config-conventional" 86 | ] 87 | }, 88 | "release-it": { 89 | "git": { 90 | "commitMessage": "chore: release ${version}", 91 | "tagName": "v${version}" 92 | }, 93 | "npm": { 94 | "publish": true 95 | }, 96 | "github": { 97 | "release": true 98 | }, 99 | "plugins": { 100 | "@release-it/conventional-changelog": { 101 | "preset": { 102 | "name": "conventionalcommits" 103 | } 104 | } 105 | } 106 | }, 107 | "eslintConfig": { 108 | "root": true, 109 | "extends": [ 110 | "@react-native", 111 | "prettier" 112 | ], 113 | "rules": { 114 | "react/react-in-jsx-scope": "off", 115 | "prettier/prettier": [ 116 | "error", 117 | { 118 | "quoteProps": "consistent", 119 | "singleQuote": true, 120 | "tabWidth": 2, 121 | "trailingComma": "es5", 122 | "useTabs": false 123 | } 124 | ] 125 | } 126 | }, 127 | "eslintIgnore": [ 128 | "node_modules/", 129 | "lib/" 130 | ], 131 | "prettier": { 132 | "quoteProps": "consistent", 133 | "singleQuote": true, 134 | "tabWidth": 2, 135 | "trailingComma": "es5", 136 | "useTabs": false 137 | }, 138 | "react-native-builder-bob": { 139 | "source": "src", 140 | "output": "lib", 141 | "targets": [ 142 | [ 143 | "commonjs", 144 | { 145 | "esm": true 146 | } 147 | ], 148 | [ 149 | "module", 150 | { 151 | "esm": true 152 | } 153 | ], 154 | [ 155 | "typescript", 156 | { 157 | "project": "tsconfig.build.json", 158 | "esm": true 159 | } 160 | ] 161 | ] 162 | }, 163 | "packageManager": "yarn@4.7.0" 164 | } 165 | -------------------------------------------------------------------------------- /rive-react-native.podspec: -------------------------------------------------------------------------------- 1 | require "json" 2 | 3 | package = JSON.parse(File.read(File.join(__dir__, "package.json"))) 4 | 5 | Pod::Spec.new do |s| 6 | s.name = "rive-react-native" 7 | s.version = package["version"] 8 | s.summary = package["description"] 9 | s.homepage = package["homepage"] 10 | s.license = package["license"] 11 | s.authors = package["author"] 12 | 13 | s.platforms = { :ios => "14.0" } 14 | s.source = { :git => "https://github.com/rive-app/rive-react-native.git", :tag => "#{s.version}" } 15 | 16 | s.source_files = "ios/**/*.{h,m,mm,swift}" 17 | 18 | s.swift_version = "5.0" 19 | 20 | s.dependency "React-Core" 21 | s.dependency "RiveRuntime", "6.9.4" 22 | end 23 | -------------------------------------------------------------------------------- /scripts/add_build_number.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | get_package_version() { 4 | PACKAGE_VERSION="$(awk -F'"' '/"version": ".+"/{ print $4; exit; }' package.json)" 5 | } 6 | get_package_version 7 | 8 | echo "$PACKAGE_VERSION" 9 | -------------------------------------------------------------------------------- /scripts/bootstrap.js: -------------------------------------------------------------------------------- 1 | const path = require('path'); 2 | const child_process = require('child_process'); 3 | 4 | const root = path.resolve(__dirname, '..'); 5 | const args = process.argv.slice(2); 6 | const options = { 7 | cwd: process.cwd(), 8 | env: process.env, 9 | stdio: 'inherit', 10 | encoding: 'utf-8', 11 | }; 12 | 13 | let result; 14 | 15 | if (process.cwd() !== root || args.length) { 16 | // We're not in the root of the project, or additional arguments were passed 17 | // In this case, forward the command to `yarn` 18 | result = child_process.spawnSync('yarn', args, options); 19 | } else { 20 | // If `yarn` is run without arguments, perform bootstrap 21 | result = child_process.spawnSync('yarn', ['bootstrap'], options); 22 | } 23 | 24 | process.exitCode = result.status; 25 | -------------------------------------------------------------------------------- /src/__tests__/index.test.tsx: -------------------------------------------------------------------------------- 1 | it.todo('write a test'); 2 | -------------------------------------------------------------------------------- /src/helpers.ts: -------------------------------------------------------------------------------- 1 | import { RNRiveError, RNRiveErrorType } from './types'; 2 | 3 | export type Without = { [P in Exclude]?: never }; 4 | export type XOR = T | U extends object 5 | ? (Without & U) | (Without & T) 6 | : T | U; 7 | 8 | export function isEnum( 9 | enumType: EnumType, 10 | enumValue: string 11 | ): enumValue is EnumType[keyof EnumType] { 12 | return Object.values(enumType).includes(enumValue); 13 | } 14 | 15 | export function convertErrorFromNativeToRN(errorFromNative: { 16 | type: string; 17 | message: string; 18 | }): RNRiveError | null { 19 | if (isEnum(RNRiveErrorType, errorFromNative.type)) { 20 | return { 21 | type: errorFromNative.type, 22 | message: errorFromNative.message, 23 | }; 24 | } 25 | return null; 26 | } 27 | -------------------------------------------------------------------------------- /src/index.tsx: -------------------------------------------------------------------------------- 1 | export * from './types'; 2 | import Rive, { 3 | RiveRenderer, 4 | useRive, 5 | useRiveString, 6 | useRiveNumber, 7 | useRiveBoolean, 8 | useRiveColor, 9 | useRiveEnum, 10 | useRiveTrigger, 11 | } from './Rive'; 12 | 13 | export { 14 | RiveRenderer, 15 | useRive, 16 | useRiveString, 17 | useRiveNumber, 18 | useRiveBoolean, 19 | useRiveColor, 20 | useRiveEnum, 21 | useRiveTrigger, 22 | }; 23 | 24 | export default Rive; 25 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | /* eslint-disable no-bitwise */ 2 | import { Image } from 'react-native'; 3 | import type { 4 | FileAssetSource, 5 | PropertyType, 6 | RiveAssetPropType, 7 | RiveRGBA, 8 | } from './types'; 9 | 10 | function parsePossibleSources(source: RiveAssetPropType): FileAssetSource { 11 | if (typeof source === 'number') { 12 | const resolvedAsset = Image.resolveAssetSource(source); 13 | if (resolvedAsset && resolvedAsset.uri) { 14 | return { sourceAssetId: resolvedAsset.uri }; 15 | } else { 16 | throw new Error('Invalid asset source provided.'); 17 | } 18 | } 19 | 20 | const uri = (source as any).uri; 21 | if (typeof source === 'object' && uri) { 22 | return { sourceUrl: uri }; 23 | } 24 | 25 | const asset = (source as any).fileName; 26 | const path = (source as any).path; 27 | 28 | if (typeof source === 'object' && asset) { 29 | const result: FileAssetSource = { sourceAsset: asset }; 30 | 31 | if (path) { 32 | result.path = path; 33 | } 34 | 35 | return result; 36 | } 37 | 38 | throw new Error('Invalid source provided.'); 39 | } 40 | 41 | function parseColor(color: string): RiveRGBA { 42 | const hex = color.replace(/^#/, ''); 43 | 44 | const isValidHex = /^[0-9A-Fa-f]{6}([0-9A-Fa-f]{2})?$/.test(hex); 45 | if (!isValidHex) { 46 | console.warn(`Rive invalid hex color: ${color}`); 47 | return { r: 0, g: 0, b: 0, a: 255 }; 48 | } 49 | 50 | let r = parseInt(hex.slice(0, 2), 16), 51 | g = parseInt(hex.slice(2, 4), 16), 52 | b = parseInt(hex.slice(4, 6), 16), 53 | a = 255; 54 | 55 | // Optionally parse alpha channel if present 56 | if (hex.length === 8) { 57 | a = parseInt(hex.slice(6, 8), 16); 58 | } 59 | 60 | return { r, g, b, a }; 61 | } 62 | 63 | function intToRiveRGBA(colorValue: number): RiveRGBA { 64 | const a = (colorValue >> 24) & 0xff; 65 | const r = (colorValue >> 16) & 0xff; 66 | const g = (colorValue >> 8) & 0xff; 67 | const b = colorValue & 0xff; 68 | return { r, g, b, a }; 69 | } 70 | 71 | export { parsePossibleSources, parseColor, intToRiveRGBA }; 72 | 73 | export const getPropertyTypeString = (propertyType: PropertyType): string => 74 | propertyType; 75 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "extends": "./tsconfig", 3 | "exclude": ["example", "lib"] 4 | } 5 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "allowUnreachableCode": false, 4 | "allowUnusedLabels": false, 5 | "esModuleInterop": true, 6 | "forceConsistentCasingInFileNames": true, 7 | "jsx": "react-native", 8 | "lib": ["esnext"], 9 | "module": "esnext", 10 | "moduleResolution": "node", 11 | "noFallthroughCasesInSwitch": true, 12 | "noImplicitReturns": true, 13 | "noStrictGenericChecks": false, 14 | "noUnusedLocals": true, 15 | "noUnusedParameters": true, 16 | "noUncheckedIndexedAccess": true, 17 | "resolveJsonModule": true, 18 | "skipLibCheck": true, 19 | "strict": false, 20 | "target": "esnext" 21 | }, 22 | "include": [ 23 | "src", 24 | "babel.config.js", 25 | ], 26 | "exclude": [ 27 | "node_modules", 28 | "lib", 29 | ] 30 | } -------------------------------------------------------------------------------- /version.txt: -------------------------------------------------------------------------------- 1 | 9.3.4 --------------------------------------------------------------------------------