From 2725ba33e2b7f2cc95d8c9bc95b6ab965c3f59a8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Mon, 20 May 2024 19:34:26 +0200 Subject: [PATCH 1/3] Allow library podspec to declare Swift Package Manager dependencies --- .../react-native/scripts/cocoapods/spm.rb | 73 +++++++++++++++++++ .../react-native/scripts/cocoapods/utils.rb | 8 ++ .../react-native/scripts/react_native_pods.rb | 1 + 3 files changed, 82 insertions(+) create mode 100644 packages/react-native/scripts/cocoapods/spm.rb diff --git a/packages/react-native/scripts/cocoapods/spm.rb b/packages/react-native/scripts/cocoapods/spm.rb new file mode 100644 index 000000000000..7e88151321cc --- /dev/null +++ b/packages/react-native/scripts/cocoapods/spm.rb @@ -0,0 +1,73 @@ + +$rn_spm_dependencies_by_pod = {} + +class SPM + def self.dependency(pod_spec, url:, requirement:, products:) + $rn_spm_dependencies_by_pod[pod_spec.name] ||= [] + $rn_spm_dependencies_by_pod[pod_spec.name] << { url: url, requirement: requirement, products: products} + end + + def self.apply(installer) + project = installer.pods_project + + log 'Cleaning old SPM dependencies from Pods project' + clean_spm_dependencies_from_target(project) + log 'Adding SPM dependencies to Pods project' + $rn_spm_dependencies_by_pod.each do |pod_name, dependencies| + dependencies.each do |spm_spec| + log "Adding SPM dependency on product #{spm_spec[:products]}" + add_spm_to_target( + project, + project.targets.find { |t| t.name == pod_name}, + spm_spec[:url], + spm_spec[:requirement], + spm_spec[:products] + ) + log " Adding workaround for Swift package not found issue" + target = project.targets.find { |t| t.name == pod_name} + target.build_configurations.each do |config| + target.build_settings(config.name)['SWIFT_INCLUDE_PATHS'] ||= ['$(inherited)'] + search_path = '${SYMROOT}/${CONFIGURATION}${EFFECTIVE_PLATFORM_NAME}/' + unless target.build_settings(config.name)['SWIFT_INCLUDE_PATHS'].include?(search_path) + target.build_settings(config.name)['SWIFT_INCLUDE_PATHS'].push(search_path) + end + end + end + end + end + + private + + def self.log(msg) + puts "[SPM] #{msg}" + end + + def self.clean_spm_dependencies_from_target(project) + project.root_object.package_references.delete_if { |pkg| pkg.class == Xcodeproj::Project::Object::XCRemoteSwiftPackageReference } + end + + def self.add_spm_to_target(project, target, url, requirement, products) + pkg_class = Xcodeproj::Project::Object::XCRemoteSwiftPackageReference + ref_class = Xcodeproj::Project::Object::XCSwiftPackageProductDependency + pkg = project.root_object.package_references.find { |p| p.class == pkg_class && p.repositoryURL == url } + if !pkg + pkg = project.new(pkg_class) + pkg.repositoryURL = url + pkg.requirement = requirement + log(" Requirement: #{requirement}") + project.root_object.package_references << pkg + end + products.each do |product_name| + ref = target.package_product_dependencies.find do |r| + r.class == ref_class && r.package == pkg && r.product_name == product_name + end + next if ref + + log(" Adding product dependency #{product_name} to #{target.name}") + ref = project.new(ref_class) + ref.package = pkg + ref.product_name = product_name + target.package_product_dependencies << ref + end + end +end \ No newline at end of file diff --git a/packages/react-native/scripts/cocoapods/utils.rb b/packages/react-native/scripts/cocoapods/utils.rb index b5195aadc7cb..0fd6d7f66f45 100644 --- a/packages/react-native/scripts/cocoapods/utils.rb +++ b/packages/react-native/scripts/cocoapods/utils.rb @@ -4,6 +4,7 @@ # LICENSE file in the root directory of this source tree. require_relative "./helpers.rb" +require_relative "./spm.rb" # Utilities class for React Native Cocoapods class ReactNativePodsUtils @@ -196,7 +197,14 @@ def self.apply_xcode_15_patch(installer, xcodebuild_manager: Xcodebuild) end project.save() end + end + + def self.apply_spm_dependencies(installer) + SPM.apply(installer) + end + def self.spm_dependency(s, url:, requirement:, products:) + SPM.dependency(s, url: url, requirement: requirement, products: products) end private diff --git a/packages/react-native/scripts/react_native_pods.rb b/packages/react-native/scripts/react_native_pods.rb index 77aca67f240e..61a48ff58589 100644 --- a/packages/react-native/scripts/react_native_pods.rb +++ b/packages/react-native/scripts/react_native_pods.rb @@ -294,6 +294,7 @@ def react_native_post_install( ReactNativePodsUtils.updateOSDeploymentTarget(installer) ReactNativePodsUtils.set_dynamic_frameworks_flags(installer) ReactNativePodsUtils.add_ndebug_flag_to_pods_in_release(installer) + ReactNativePodsUtils.apply_spm_dependencies(installer) if privacy_file_aggregation_enabled PrivacyManifestUtils.add_aggregated_privacy_manifest(installer) From 175f927106aabe1146aa75ed7d3648e78677cf59 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Sat, 25 May 2024 15:41:58 +0200 Subject: [PATCH 2/3] Allow library podspec to declare Swift Package Manager dependencies - code update based on review --- .../react-native/scripts/cocoapods/spm.rb | 36 ++++++++++--------- .../react-native/scripts/cocoapods/utils.rb | 8 ----- .../react-native/scripts/react_native_pods.rb | 15 +++++++- 3 files changed, 34 insertions(+), 25 deletions(-) diff --git a/packages/react-native/scripts/cocoapods/spm.rb b/packages/react-native/scripts/cocoapods/spm.rb index 7e88151321cc..3cc2f0d167e9 100644 --- a/packages/react-native/scripts/cocoapods/spm.rb +++ b/packages/react-native/scripts/cocoapods/spm.rb @@ -1,19 +1,20 @@ +class SPMManager + def initialize() + @dependencies_by_pod = {} + end -$rn_spm_dependencies_by_pod = {} - -class SPM - def self.dependency(pod_spec, url:, requirement:, products:) - $rn_spm_dependencies_by_pod[pod_spec.name] ||= [] - $rn_spm_dependencies_by_pod[pod_spec.name] << { url: url, requirement: requirement, products: products} + def dependency(pod_spec, url:, requirement:, products:) + @dependencies_by_pod[pod_spec.name] ||= [] + @dependencies_by_pod[pod_spec.name] << { url: url, requirement: requirement, products: products} end - def self.apply(installer) + def apply_on_post_install(installer) project = installer.pods_project log 'Cleaning old SPM dependencies from Pods project' - clean_spm_dependencies_from_target(project) + clean_spm_dependencies_from_target(project, @dependencies_by_pod) log 'Adding SPM dependencies to Pods project' - $rn_spm_dependencies_by_pod.each do |pod_name, dependencies| + @dependencies_by_pod.each do |pod_name, dependencies| dependencies.each do |spm_spec| log "Adding SPM dependency on product #{spm_spec[:products]}" add_spm_to_target( @@ -38,23 +39,24 @@ def self.apply(installer) private - def self.log(msg) - puts "[SPM] #{msg}" + def log(msg) + ::Pod::UI.puts "[SPM] #{msg}" end - def self.clean_spm_dependencies_from_target(project) - project.root_object.package_references.delete_if { |pkg| pkg.class == Xcodeproj::Project::Object::XCRemoteSwiftPackageReference } + def clean_spm_dependencies_from_target(project, new_targets) + project.root_object.package_references.delete_if { |pkg| (pkg.class == Xcodeproj::Project::Object::XCRemoteSwiftPackageReference) } end - def self.add_spm_to_target(project, target, url, requirement, products) + def add_spm_to_target(project, target, url, requirement, products) pkg_class = Xcodeproj::Project::Object::XCRemoteSwiftPackageReference ref_class = Xcodeproj::Project::Object::XCSwiftPackageProductDependency pkg = project.root_object.package_references.find { |p| p.class == pkg_class && p.repositoryURL == url } if !pkg + log(" UUID: #{project.generate_uuid}") pkg = project.new(pkg_class) pkg.repositoryURL = url pkg.requirement = requirement - log(" Requirement: #{requirement}") + log(" Adding package to workspace: #{pkg.inspect}") project.root_object.package_references << pkg end products.each do |product_name| @@ -70,4 +72,6 @@ def self.add_spm_to_target(project, target, url, requirement, products) target.package_product_dependencies << ref end end -end \ No newline at end of file +end + +SPM = SPMManager.new diff --git a/packages/react-native/scripts/cocoapods/utils.rb b/packages/react-native/scripts/cocoapods/utils.rb index 0fd6d7f66f45..b5195aadc7cb 100644 --- a/packages/react-native/scripts/cocoapods/utils.rb +++ b/packages/react-native/scripts/cocoapods/utils.rb @@ -4,7 +4,6 @@ # LICENSE file in the root directory of this source tree. require_relative "./helpers.rb" -require_relative "./spm.rb" # Utilities class for React Native Cocoapods class ReactNativePodsUtils @@ -197,14 +196,7 @@ def self.apply_xcode_15_patch(installer, xcodebuild_manager: Xcodebuild) end project.save() end - end - - def self.apply_spm_dependencies(installer) - SPM.apply(installer) - end - def self.spm_dependency(s, url:, requirement:, products:) - SPM.dependency(s, url: url, requirement: requirement, products: products) end private diff --git a/packages/react-native/scripts/react_native_pods.rb b/packages/react-native/scripts/react_native_pods.rb index 61a48ff58589..37472234c234 100644 --- a/packages/react-native/scripts/react_native_pods.rb +++ b/packages/react-native/scripts/react_native_pods.rb @@ -17,6 +17,7 @@ require_relative './cocoapods/runtime.rb' require_relative './cocoapods/helpers.rb' require_relative './cocoapods/privacy_manifest_utils.rb' +require_relative './cocoapods/spm.rb' # Importing to expose use_native_modules! require_relative './cocoapods/autolinking.rb' @@ -241,6 +242,18 @@ def install_modules_dependencies(spec, new_arch_enabled: NewArchitectureHelper.n NewArchitectureHelper.install_modules_dependencies(spec, new_arch_enabled, folly_config[:version]) end + +# This function can be used by library developer to declare an SwiftPackageManager dependency. +# +# Parameters: +# - spec: The spec the Swift Package Manager dependency has to be added to +# - url: The URL of the Swift Package Manager dependency +# - requirement: The version requirement of the Swift Package Manager dependency (eg. ` {kind: 'upToNextMajorVersion', minimumVersion: '5.9.1'},`) +# - products: The product/target of the Swift Package Manager dependency (eg. AlamofireDynamic) +def spm_dependency(spec, url:, requirement:, products:) + SPM.dependency(spec, url: url, requirement: requirement, products: products) +end + # It returns the default flags. # deprecated. def get_default_flags() @@ -294,7 +307,7 @@ def react_native_post_install( ReactNativePodsUtils.updateOSDeploymentTarget(installer) ReactNativePodsUtils.set_dynamic_frameworks_flags(installer) ReactNativePodsUtils.add_ndebug_flag_to_pods_in_release(installer) - ReactNativePodsUtils.apply_spm_dependencies(installer) + SPM.apply_on_post_install(installer) if privacy_file_aggregation_enabled PrivacyManifestUtils.add_aggregated_privacy_manifest(installer) From c384cd9eb038a22e3ea4d2bf32bba0904794bc5f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Mikl=C3=B3s=20Fazekas?= Date: Thu, 20 Jun 2024 10:49:36 +0200 Subject: [PATCH 3/3] Allow library podspec to declare Swift Package Manager dependencies - show warning after install --- packages/react-native/scripts/cocoapods/spm.rb | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/packages/react-native/scripts/cocoapods/spm.rb b/packages/react-native/scripts/cocoapods/spm.rb index 3cc2f0d167e9..8281550deeb0 100644 --- a/packages/react-native/scripts/cocoapods/spm.rb +++ b/packages/react-native/scripts/cocoapods/spm.rb @@ -35,6 +35,15 @@ def apply_on_post_install(installer) end end end + + unless @dependencies_by_pod.empty? + log_warning "If you're using Xcode 15 or earlier you might need to close and reopen the Xcode workspace" + unless ENV["USE_FRAMEWORKS"] == "dynamic" + @dependencies_by_pod.each do |pod_name, dependencies| + log_warning "Pod #{pod_name} is using swift package(s) #{dependencies.map{|i| i[:products]}.flatten.uniq.join(", ")} with static linking, this might cause linker errors. Consider using USE_FRAMEOWRKS=dynamic, see https://github.com/facebook/react-native/pull/44627#issuecomment-2123119711 for more information" + end + end + end end private @@ -43,6 +52,10 @@ def log(msg) ::Pod::UI.puts "[SPM] #{msg}" end + def log_warning(msg) + ::Pod::UI.puts "\n\n[SPM] WARNING!!! #{msg}\n\n" + end + def clean_spm_dependencies_from_target(project, new_targets) project.root_object.package_references.delete_if { |pkg| (pkg.class == Xcodeproj::Project::Object::XCRemoteSwiftPackageReference) } end @@ -52,7 +65,6 @@ def add_spm_to_target(project, target, url, requirement, products) ref_class = Xcodeproj::Project::Object::XCSwiftPackageProductDependency pkg = project.root_object.package_references.find { |p| p.class == pkg_class && p.repositoryURL == url } if !pkg - log(" UUID: #{project.generate_uuid}") pkg = project.new(pkg_class) pkg.repositoryURL = url pkg.requirement = requirement