From b2f469e4cce3718c461e2ef30612ecdb3990f5b8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Mon, 9 Mar 2026 15:38:23 +0100 Subject: [PATCH 1/5] feat(ios): consume RiveRuntime via SPM instead of CocoaPods pod MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Replace `s.dependency "RiveRuntime"` with `spm_dependency` pointing at rive-app/rive-ios on GitHub. The version is still sourced from the existing resolution chain (env var → global var → Podfile.properties.json → package.json `runtimeVersions.ios`), so no version pinning changes are needed. Co-Authored-By: Claude Sonnet 4.6 --- RNRive.podspec | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/RNRive.podspec b/RNRive.podspec index 99664968..9b011cca 100644 --- a/RNRive.podspec +++ b/RNRive.podspec @@ -47,7 +47,11 @@ Pod::Spec.new do |s| load 'nitrogen/generated/ios/RNRive+autolinking.rb' add_nitrogen_files(s) - s.dependency "RiveRuntime", rive_ios_version + spm_dependency(s, + url: 'https://github.com/rive-app/rive-ios.git', + requirement: { kind: 'upToNextMajorVersion', minimumVersion: rive_ios_version }, + products: ['RiveRuntime'] + ) install_modules_dependencies(s) end From 5d3329dca6b7d00c001da5850cbade7dc3b699b9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Mon, 9 Mar 2026 15:43:46 +0100 Subject: [PATCH 2/5] fix(ios): pin RiveRuntime SPM dependency to exact version Using exactVersion instead of upToNextMajorVersion ensures the SPM package resolves to the same version declared in package.json. Users can still override via the existing env var / global / Podfile.properties.json mechanism. Co-Authored-By: Claude Sonnet 4.6 --- RNRive.podspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/RNRive.podspec b/RNRive.podspec index 9b011cca..e3be4dc1 100644 --- a/RNRive.podspec +++ b/RNRive.podspec @@ -49,7 +49,7 @@ Pod::Spec.new do |s| spm_dependency(s, url: 'https://github.com/rive-app/rive-ios.git', - requirement: { kind: 'upToNextMajorVersion', minimumVersion: rive_ios_version }, + requirement: { kind: 'exactVersion', version: rive_ios_version }, products: ['RiveRuntime'] ) From c3befd020e49e17c5e1163483fdfbf9349d0364a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Tue, 10 Mar 2026 16:37:44 +0100 Subject: [PATCH 3/5] fix(ios): embed RiveRuntime.framework from SPM in post_install hook CocoaPods does not automatically embed dynamic frameworks resolved via spm_dependency. Add a post_install step to both example Podfiles that appends the RiveRuntime embed call to every target's frameworks.sh script. Targets are discovered dynamically so no target name is hardcoded. Consumers of this library will need the same post_install hook in their own Podfiles. Co-Authored-By: Claude Sonnet 4.6 --- example/ios/Podfile | 21 +++++++++++ expo-example/ios/Podfile | 81 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+) create mode 100644 expo-example/ios/Podfile diff --git a/example/ios/Podfile b/example/ios/Podfile index c04206ab..3592cb0b 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -33,5 +33,26 @@ target 'RiveExample' do :mac_catalyst_enabled => false, # :ccache_enabled => true ) + + # SPM-resolved dynamic frameworks aren't embedded by CocoaPods automatically. + # Append RiveRuntime to each target's embed frameworks script phase. + installer.aggregate_targets.each do |target| + embed_script = File.join( + installer.sandbox.root, + 'Target Support Files', + target.name, + "#{target.name}-frameworks.sh" + ) + next unless File.exist?(embed_script) + content = File.read(embed_script) + next if content.include?('RiveRuntime') + content.sub!( + /if \[ "\$\{COCOAPODS_PARALLEL_CODE_SIGN\}" == "true" \]; then\s+wait\s+fi/, + "install_framework \"${PODS_XCFRAMEWORKS_BUILD_DIR}/RiveRuntime/RiveRuntime.framework\"\n" \ + "if [ \"${COCOAPODS_PARALLEL_CODE_SIGN}\" == \"true\" ]; then\n wait\nfi" + ) + File.write(embed_script, content) + Pod::UI.puts "[RNRive] Added RiveRuntime.framework to embed script for #{target.name}" + end end end diff --git a/expo-example/ios/Podfile b/expo-example/ios/Podfile new file mode 100644 index 00000000..eed5f7cc --- /dev/null +++ b/expo-example/ios/Podfile @@ -0,0 +1,81 @@ +require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking") +require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods") + +require 'json' +podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} + +def ccache_enabled?(podfile_properties) + # Environment variable takes precedence + return ENV['USE_CCACHE'] == '1' if ENV['USE_CCACHE'] + + # Fall back to Podfile properties + podfile_properties['apple.ccacheEnabled'] == 'true' +end + +ENV['RCT_NEW_ARCH_ENABLED'] ||= '0' if podfile_properties['newArchEnabled'] == 'false' +ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] ||= podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] +ENV['RCT_USE_RN_DEP'] ||= '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true' && podfile_properties['newArchEnabled'] != 'false' +ENV['RCT_USE_PREBUILT_RNCORE'] ||= '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true' && podfile_properties['newArchEnabled'] != 'false' +platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' + +prepare_react_native_project! + +target 'expoexample' do + use_expo_modules! + + if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' + config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; + else + config_command = [ + 'npx', + 'expo-modules-autolinking', + 'react-native-config', + '--json', + '--platform', + 'ios' + ] + end + + config = use_native_modules!(config_command) + + use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] + use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] + + use_react_native!( + :path => config[:reactNativePath], + :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', + # An absolute path to your application root. + :app_path => "#{Pod::Config.instance.installation_root}/..", + :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', + ) + + post_install do |installer| + react_native_post_install( + installer, + config[:reactNativePath], + :mac_catalyst_enabled => false, + :ccache_enabled => ccache_enabled?(podfile_properties), + ) + + # SPM-resolved dynamic frameworks aren't embedded by CocoaPods automatically. + # Append RiveRuntime to each target's embed frameworks script phase. + installer.aggregate_targets.each do |target| + embed_script = File.join( + installer.sandbox.root, + 'Target Support Files', + target.name, + "#{target.name}-frameworks.sh" + ) + next unless File.exist?(embed_script) + content = File.read(embed_script) + next if content.include?('RiveRuntime') + content.sub!( + /if \[ "\$\{COCOAPODS_PARALLEL_CODE_SIGN\}" == "true" \]; then\s+wait\s+fi/, + "install_framework \"${PODS_XCFRAMEWORKS_BUILD_DIR}/RiveRuntime/RiveRuntime.framework\"\n" \ + "if [ \"${COCOAPODS_PARALLEL_CODE_SIGN}\" == \"true\" ]; then\n wait\nfi" + ) + File.write(embed_script, content) + Pod::UI.puts "[RNRive] Added RiveRuntime.framework to embed script for #{target.name}" + end + end +end From 146a1c5c1aabeae79e7d5a09557c69a7fe7c08b1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Tue, 10 Mar 2026 16:40:05 +0100 Subject: [PATCH 4/5] refactor(ios): move SPM embed fix into podspec via Pod::Installer.prepend Instead of requiring every consumer to add a post_install hook to their Podfile, hook into run_podfile_post_install_hooks directly from the podspec using the same Pod::Installer.prepend pattern as the Xcode 26 modulemap fix. Removes the duplicate workaround from both example Podfiles. Co-Authored-By: Claude Sonnet 4.6 --- RNRive.podspec | 31 +++++++++++++++++++++++++++++++ example/ios/Podfile | 21 --------------------- expo-example/ios/Podfile | 21 --------------------- 3 files changed, 31 insertions(+), 42 deletions(-) diff --git a/RNRive.podspec b/RNRive.podspec index e3be4dc1..5226bda4 100644 --- a/RNRive.podspec +++ b/RNRive.podspec @@ -30,6 +30,37 @@ end Pod::UI.puts "@rive-app/react-native: Rive iOS SDK #{rive_ios_version}" +# SPM-resolved dynamic frameworks aren't embedded by CocoaPods automatically. +# Hook into post_install to append RiveRuntime to every target's embed script +# so consumers don't need to add anything to their own Podfiles. +if defined?(Pod::Installer) + module RiveSPMEmbedFix + def run_podfile_post_install_hooks + super + aggregate_targets.each do |target| + embed_script = File.join( + sandbox.root, + 'Target Support Files', + target.name, + "#{target.name}-frameworks.sh" + ) + next unless File.exist?(embed_script) + content = File.read(embed_script) + next if content.include?('RiveRuntime') + content.sub!( + /if \[ "\$\{COCOAPODS_PARALLEL_CODE_SIGN\}" == "true" \]; then\s+wait\s+fi/, + "install_framework \"${PODS_XCFRAMEWORKS_BUILD_DIR}/RiveRuntime/RiveRuntime.framework\"\n" \ + "if [ \"${COCOAPODS_PARALLEL_CODE_SIGN}\" == \"true\" ]; then\n wait\nfi" + ) + File.write(embed_script, content) + Pod::UI.puts "[RNRive] Added RiveRuntime.framework to embed script for #{target.name}" + end + end + end + + Pod::Installer.prepend(RiveSPMEmbedFix) +end + Pod::Spec.new do |s| s.name = "RNRive" s.version = package["version"] diff --git a/example/ios/Podfile b/example/ios/Podfile index 3592cb0b..c04206ab 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -33,26 +33,5 @@ target 'RiveExample' do :mac_catalyst_enabled => false, # :ccache_enabled => true ) - - # SPM-resolved dynamic frameworks aren't embedded by CocoaPods automatically. - # Append RiveRuntime to each target's embed frameworks script phase. - installer.aggregate_targets.each do |target| - embed_script = File.join( - installer.sandbox.root, - 'Target Support Files', - target.name, - "#{target.name}-frameworks.sh" - ) - next unless File.exist?(embed_script) - content = File.read(embed_script) - next if content.include?('RiveRuntime') - content.sub!( - /if \[ "\$\{COCOAPODS_PARALLEL_CODE_SIGN\}" == "true" \]; then\s+wait\s+fi/, - "install_framework \"${PODS_XCFRAMEWORKS_BUILD_DIR}/RiveRuntime/RiveRuntime.framework\"\n" \ - "if [ \"${COCOAPODS_PARALLEL_CODE_SIGN}\" == \"true\" ]; then\n wait\nfi" - ) - File.write(embed_script, content) - Pod::UI.puts "[RNRive] Added RiveRuntime.framework to embed script for #{target.name}" - end end end diff --git a/expo-example/ios/Podfile b/expo-example/ios/Podfile index eed5f7cc..3a160e54 100644 --- a/expo-example/ios/Podfile +++ b/expo-example/ios/Podfile @@ -56,26 +56,5 @@ target 'expoexample' do :mac_catalyst_enabled => false, :ccache_enabled => ccache_enabled?(podfile_properties), ) - - # SPM-resolved dynamic frameworks aren't embedded by CocoaPods automatically. - # Append RiveRuntime to each target's embed frameworks script phase. - installer.aggregate_targets.each do |target| - embed_script = File.join( - installer.sandbox.root, - 'Target Support Files', - target.name, - "#{target.name}-frameworks.sh" - ) - next unless File.exist?(embed_script) - content = File.read(embed_script) - next if content.include?('RiveRuntime') - content.sub!( - /if \[ "\$\{COCOAPODS_PARALLEL_CODE_SIGN\}" == "true" \]; then\s+wait\s+fi/, - "install_framework \"${PODS_XCFRAMEWORKS_BUILD_DIR}/RiveRuntime/RiveRuntime.framework\"\n" \ - "if [ \"${COCOAPODS_PARALLEL_CODE_SIGN}\" == \"true\" ]; then\n wait\nfi" - ) - File.write(embed_script, content) - Pod::UI.puts "[RNRive] Added RiveRuntime.framework to embed script for #{target.name}" - end end end From 6be0e567e3a2cc76312e6bd1f95f087568a12a4b Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Tue, 10 Mar 2026 18:24:38 +0100 Subject: [PATCH 5/5] untrack expo-example/ios/Podfile (gitignored) --- expo-example/ios/Podfile | 60 ---------------------------------------- 1 file changed, 60 deletions(-) delete mode 100644 expo-example/ios/Podfile diff --git a/expo-example/ios/Podfile b/expo-example/ios/Podfile deleted file mode 100644 index 3a160e54..00000000 --- a/expo-example/ios/Podfile +++ /dev/null @@ -1,60 +0,0 @@ -require File.join(File.dirname(`node --print "require.resolve('expo/package.json')"`), "scripts/autolinking") -require File.join(File.dirname(`node --print "require.resolve('react-native/package.json')"`), "scripts/react_native_pods") - -require 'json' -podfile_properties = JSON.parse(File.read(File.join(__dir__, 'Podfile.properties.json'))) rescue {} - -def ccache_enabled?(podfile_properties) - # Environment variable takes precedence - return ENV['USE_CCACHE'] == '1' if ENV['USE_CCACHE'] - - # Fall back to Podfile properties - podfile_properties['apple.ccacheEnabled'] == 'true' -end - -ENV['RCT_NEW_ARCH_ENABLED'] ||= '0' if podfile_properties['newArchEnabled'] == 'false' -ENV['EX_DEV_CLIENT_NETWORK_INSPECTOR'] ||= podfile_properties['EX_DEV_CLIENT_NETWORK_INSPECTOR'] -ENV['RCT_USE_RN_DEP'] ||= '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true' && podfile_properties['newArchEnabled'] != 'false' -ENV['RCT_USE_PREBUILT_RNCORE'] ||= '1' if podfile_properties['ios.buildReactNativeFromSource'] != 'true' && podfile_properties['newArchEnabled'] != 'false' -platform :ios, podfile_properties['ios.deploymentTarget'] || '15.1' - -prepare_react_native_project! - -target 'expoexample' do - use_expo_modules! - - if ENV['EXPO_USE_COMMUNITY_AUTOLINKING'] == '1' - config_command = ['node', '-e', "process.argv=['', '', 'config'];require('@react-native-community/cli').run()"]; - else - config_command = [ - 'npx', - 'expo-modules-autolinking', - 'react-native-config', - '--json', - '--platform', - 'ios' - ] - end - - config = use_native_modules!(config_command) - - use_frameworks! :linkage => podfile_properties['ios.useFrameworks'].to_sym if podfile_properties['ios.useFrameworks'] - use_frameworks! :linkage => ENV['USE_FRAMEWORKS'].to_sym if ENV['USE_FRAMEWORKS'] - - use_react_native!( - :path => config[:reactNativePath], - :hermes_enabled => podfile_properties['expo.jsEngine'] == nil || podfile_properties['expo.jsEngine'] == 'hermes', - # An absolute path to your application root. - :app_path => "#{Pod::Config.instance.installation_root}/..", - :privacy_file_aggregation_enabled => podfile_properties['apple.privacyManifestAggregationEnabled'] != 'false', - ) - - post_install do |installer| - react_native_post_install( - installer, - config[:reactNativePath], - :mac_catalyst_enabled => false, - :ccache_enabled => ccache_enabled?(podfile_properties), - ) - end -end