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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 21 additions & 3 deletions RNRive.podspec
Original file line number Diff line number Diff line change
Expand Up @@ -24,11 +24,17 @@ if !rive_ios_version && package['runtimeVersions'] && package['runtimeVersions']
rive_ios_version = package['runtimeVersions']['ios']
end

if !rive_ios_version
use_rive_spm = ENV['USE_RIVE_SPM'] == '1' || (defined?($UseRiveSPM) && $UseRiveSPM)

if !use_rive_spm && !rive_ios_version
raise "Internal Error: Failed to determine Rive iOS SDK version. Please ensure package.json contains 'runtimeVersions.ios'"
end

Pod::UI.puts "@rive-app/react-native: Rive iOS SDK #{rive_ios_version}"
if use_rive_spm
Pod::UI.puts "@rive-app/react-native: Using RiveRuntime via Swift Package Manager"
else
Pod::UI.puts "@rive-app/react-native: Rive iOS SDK #{rive_ios_version}"
end

Pod::Spec.new do |s|
s.name = "RNRive"
Expand All @@ -47,7 +53,19 @@ Pod::Spec.new do |s|
load 'nitrogen/generated/ios/RNRive+autolinking.rb'
add_nitrogen_files(s)

s.dependency "RiveRuntime", rive_ios_version
if use_rive_spm
spm_dependency(s,
url: 'https://github.com/rive-app/rive-ios.git',
requirement: {kind: 'upToNextMajorVersion', minimumVersion: '6.15.0'},
products: ['RiveRuntime']
)
else
s.dependency "RiveRuntime", rive_ios_version
end

install_modules_dependencies(s)

if use_rive_spm
s.xcconfig = { 'OTHER_SWIFT_FLAGS' => '$(inherited) -DRIVE_EXPERIMENTAL_API' }
end
end
17 changes: 17 additions & 0 deletions android/src/main/java/com/margelo/nitro/rive/HybridRiveFile.kt
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package com.margelo.nitro.rive
import androidx.annotation.Keep
import app.rive.runtime.kotlin.core.File
import com.facebook.proguard.annotations.DoNotStrip
import com.margelo.nitro.core.Promise
import java.lang.ref.WeakReference
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.Dispatchers
Expand Down Expand Up @@ -96,6 +97,22 @@ class HybridRiveFile : HybridRiveFileSpec() {
}
}

override fun getEnums(): Promise<Array<RiveEnumDefinition>> {
return Promise.async {
val file = riveFile ?: return@async emptyArray()
try {
file.enums.map { enum ->
Copy link
Contributor

Choose a reason for hiding this comment

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

🚫 [ktlint] standard:chain-method-continuation reported by reviewdog 🐶
Expected newline before '.'

RiveEnumDefinition(
name = enum.name,
values = enum.values.toTypedArray()
)
}.toTypedArray()
} catch (e: NoSuchMethodError) {
throw UnsupportedOperationException("getEnums requires rive-android SDK with enums support")
}
}
}

override fun dispose() {
scope.cancel()
weakViews.clear()
Expand Down
20 changes: 19 additions & 1 deletion example/__tests__/viewmodel-properties.harness.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
import { describe, it, expect } from 'react-native-harness';
import type { ViewModelInstance } from '@rive-app/react-native';
import type {
ViewModelInstance,
RiveEnumDefinition,
} from '@rive-app/react-native';
import { RiveFileFactory } from '@rive-app/react-native';

const DATABINDING = require('../assets/rive/databinding.riv');
Expand Down Expand Up @@ -225,3 +228,18 @@ describe('Property Listeners', () => {
expect(() => cleanup2()).not.toThrow();
});
});

describe('RiveFile Enums', () => {
const expectedEnums: RiveEnumDefinition[] = [
{ name: 'Pets', values: ['chipmunk', 'rat', 'frog', 'owl', 'cat', 'dog'] },
];

it('getEnums returns enum definitions from file', async () => {
const file = await RiveFileFactory.fromSource(DATABINDING, undefined);
const enums = await file.getEnums();

expect(enums.length).toBe(expectedEnums.length);
expect(enums[0]?.name).toBe(expectedEnums[0]?.name);
expect(enums[0]?.values).toEqual(expectedEnums[0]?.values);
});
});
2 changes: 2 additions & 0 deletions example/ios/Podfile
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
ENV['RCT_NEW_ARCH_ENABLED'] = '1'

$UseRiveSPM = ENV['USE_RIVE_SPM'] == '1'

# Resolve react_native_pods.rb with node to allow for hoisting
require Pod::Executable.execute_command('node', ['-p',
'require.resolve(
Expand Down

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

49 changes: 49 additions & 0 deletions ios/HybridRiveFile.swift
Original file line number Diff line number Diff line change
@@ -1,14 +1,27 @@
import NitroModules
import RiveRuntime
#if RIVE_EXPERIMENTAL_API
@_spi(RiveExperimental) import RiveRuntime
#endif

typealias ReferencedAssetCache = [String: RiveFileAsset]

/// Source for creating experimental File instances
enum ExperimentalFileSource {
case data(Data)
case resource(String)
}

class HybridRiveFile: HybridRiveFileSpec, RiveViewSource {
var riveFile: RiveFile?
var referencedAssetCache: ReferencedAssetCache?
var assetLoader: ReferencedAssetLoader?
var cachedFactory: RiveFactory?
private var weakViews: [Weak<RiveReactNativeView>] = []

/// Source for experimental API - stored to create experimental File on demand
var experimentalSource: ExperimentalFileSource?

public func setRiveFile(_ riveFile: RiveFile) {
self.riveFile = riveFile
}
Expand Down Expand Up @@ -118,6 +131,42 @@ class HybridRiveFile: HybridRiveFileSpec, RiveViewSource {
}
}

func getEnums() throws -> Promise<[RiveEnumDefinition]> {
return Promise.async { [weak self] in
#if RIVE_EXPERIMENTAL_API
guard let source = self?.experimentalSource else {
throw NSError(
domain: "RiveError",
code: 1,
userInfo: [NSLocalizedDescriptionKey: "getEnums requires experimental API. Use USE_RIVE_SPM=1 with pod install."]
)
}

// Create worker and experimental file on demand
let worker = await Worker()
let experimentalSource: Source
switch source {
case .data(let data):
experimentalSource = .data(data)
case .resource(let name):
experimentalSource = .local(name, nil)
}

let file = try await File(source: experimentalSource, worker: worker)
let viewModelEnums = try await file.getViewModelEnums()
return viewModelEnums.map { vmEnum in
RiveEnumDefinition(name: vmEnum.name, values: vmEnum.values)
}
#else
throw NSError(
domain: "RiveError",
code: 1,
userInfo: [NSLocalizedDescriptionKey: "getEnums requires RiveRuntime 6.15.0+ with experimental API. Use USE_RIVE_SPM=1 with pod install."]
)
#endif
}
}

func dispose() {
weakViews.removeAll()
referencedAssetCache = nil
Expand Down
18 changes: 13 additions & 5 deletions ios/HybridRiveFileFactory.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,19 +16,22 @@
/// - fileWithCustomAssetLoader: Closure to load the file with a custom asset loader.
/// - file: Closure to load the file without a custom asset loader.
/// - referencedAssets: Optional referenced assets.
/// - experimentalSource: Closure to extract the experimental source from the prepared result.
/// - Returns: A promise resolving to a `HybridRiveFileSpec`.
/// - Throws: Runtime errors if any step fails.
func genericFrom<CheckResult, Prepared>(

Check warning on line 22 in ios/HybridRiveFileFactory.swift

View workflow job for this annotation

GitHub Actions / lint-swift

Function should have 5 parameters or less: it currently has 6 (function_parameter_count)
check: @escaping () throws -> CheckResult,
prepare: @escaping (CheckResult) async throws -> Prepared,
fileWithCustomAssetLoader: @escaping (Prepared, @escaping LoadAsset) throws -> RiveFile,
file: @escaping (Prepared) throws -> RiveFile,
referencedAssets: ReferencedAssetsType?
referencedAssets: ReferencedAssetsType?,
experimentalSource: @escaping (Prepared) -> ExperimentalFileSource?
) throws -> Promise<(any HybridRiveFileSpec)> {
return Promise.async {
do {
let checked = try check()
let prepared = try await prepare(checked)
let expSource = experimentalSource(prepared)

let result = try await withCheckedThrowingContinuation { continuation in
DispatchQueue.global(qos: .userInitiated).async {
Expand Down Expand Up @@ -73,6 +76,7 @@
hybridRiveFile.cachedFactory = factory
}
hybridRiveFile.assetLoader = result.loader
hybridRiveFile.experimentalSource = expSource
return hybridRiveFile
} catch let error as NSError {
throw RuntimeError.error(
Expand All @@ -98,7 +102,8 @@
try RiveFile(data: data, loadCdn: loadCdn, customAssetLoader: loader)
},
file: { (data) in try RiveFile(data: data, loadCdn: loadCdn) },
referencedAssets: referencedAssets
referencedAssets: referencedAssets,
experimentalSource: { data in .data(data) }
)
}

Expand All @@ -119,7 +124,8 @@
try RiveFile(data: data, loadCdn: loadCdn, customAssetLoader: loader)
},
file: { (data) in try RiveFile(data: data, loadCdn: loadCdn) },
referencedAssets: referencedAssets
referencedAssets: referencedAssets,
experimentalSource: { data in .data(data) }
)
}

Expand All @@ -137,7 +143,8 @@
try RiveFile(resource: resource, loadCdn: loadCdn, customAssetLoader: loader)
},
file: { (resource) in try RiveFile(resource: resource, loadCdn: loadCdn) },
referencedAssets: referencedAssets
referencedAssets: referencedAssets,
experimentalSource: { resource in .resource(resource) }
)
}

Expand All @@ -157,7 +164,8 @@
try RiveFile(data: data, loadCdn: loadCdn, customAssetLoader: loader)
},
file: { (data) in try RiveFile(data: data, loadCdn: loadCdn) },
referencedAssets: referencedAssets
referencedAssets: referencedAssets,
experimentalSource: { data in .data(data) }
)
}
}
31 changes: 31 additions & 0 deletions nitrogen/generated/android/c++/JHybridRiveFileSpec.cpp

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

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

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

Loading
Loading