From 59318cef419e0d8bbf84a4fc33d35091bf04503c Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Tue, 14 Jul 2026 12:37:30 +0200 Subject: [PATCH 01/13] feat: add opt-in SPM integration for TalsecRuntime on iOS --- example/ios/Podfile | 4 ++++ freerasp-react-native.podspec | 42 ++++++++++++++++++++++++++++------- 2 files changed, 38 insertions(+), 8 deletions(-) diff --git a/example/ios/Podfile b/example/ios/Podfile index e038f35..d2f40b2 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -8,6 +8,10 @@ require Pod::Executable.execute_command('node', ['-p', platform :ios, min_ios_version_supported prepare_react_native_project! +# To test the experimental Swift Package Manager integration of freerasp-react-native, +# install with the SPM opt-in flag and dynamic frameworks: +# FREERASP_USE_SPM=1 USE_FRAMEWORKS=dynamic pod install +# Without these flags the default vendored xcframework is used. linkage = ENV['USE_FRAMEWORKS'] if linkage != nil Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green diff --git a/freerasp-react-native.podspec b/freerasp-react-native.podspec index 9d17785..935ffbc 100644 --- a/freerasp-react-native.podspec +++ b/freerasp-react-native.podspec @@ -14,14 +14,40 @@ Pod::Spec.new do |s| s.platforms = { :ios => "11.0" } s.source = { :git => "https://github.com/talsec/freerasp-react-native.git", :tag => "#{s.version}" } - s.source_files = 'ios/models/*.{h,m,mm,swift}', - 'ios/utils/*.{h,m,mm,swift}', - 'ios/dispatchers/*.{h,m,mm,swift}', - 'ios/*.{h,m,mm,swift}', - 'ios/TalsecRuntime.xcframework' - s.xcconfig = { 'OTHER_LDFLAGS' => '-framework TalsecRuntime' } - s.ios.vendored_frameworks = "ios/TalsecRuntime.xcframework" - + # Opt-in Swift Package Manager integration for the TalsecRuntime dependency. + # Enabled only when FREERASP_USE_SPM=1 AND the running React Native provides the + # spm_dependency helper (RN >= 0.75). Otherwise the vendored xcframework is used + # (default — no breaking change for existing consumers). The SPM path additionally + # requires USE_FRAMEWORKS=dynamic in the consumer Podfile and iOS 13+. + use_spm = ENV['FREERASP_USE_SPM'] == '1' && respond_to?(:spm_dependency, true) + + source_globs = [ + 'ios/models/*.{h,m,mm,swift}', + 'ios/utils/*.{h,m,mm,swift}', + 'ios/dispatchers/*.{h,m,mm,swift}', + 'ios/*.{h,m,mm,swift}', + ] + + if use_spm + # TalsecRuntime is resolved via Swift Package Manager (talsec/Free-RASP-iOS). + # spm_dependency injects an SPM reference into the Pods-generated Xcode project. + # NOTE: the vendored xcframework is intentionally NOT linked here to avoid + # duplicate symbols. + s.ios.deployment_target = '13.0' + spm_dependency(s, + url: 'https://github.com/talsec/Free-RASP-iOS', + requirement: { kind: 'upToNextMajorVersion', minimumVersion: '6.14.5' }, + products: ['TalsecRuntime'] + ) + else + # TalsecRuntime is provided by the vendored xcframework (default). + source_globs << 'ios/TalsecRuntime.xcframework' + s.xcconfig = { 'OTHER_LDFLAGS' => '-framework TalsecRuntime' } + s.ios.vendored_frameworks = 'ios/TalsecRuntime.xcframework' + end + + s.source_files = source_globs + # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0. # See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79. if respond_to?(:install_modules_dependencies, true) From f6c7a5867c4b4a12eaacc32d12f5398764163e9f Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Tue, 14 Jul 2026 12:37:30 +0200 Subject: [PATCH 02/13] ci: add non-blocking iOS SPM smoke-test job --- .github/workflows/ci.yml | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index e5cd143..8b5b7a3 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -99,3 +99,29 @@ jobs: working-directory: ./example run: | yarn build:ios + + build-ios-spm: + # Experimental: smoke-test the opt-in Swift Package Manager integration + # (FREERASP_USE_SPM=1). Non-blocking while SPM support is experimental; the + # vendored build-ios job above remains the gating check. + runs-on: macos-latest + needs: build-library + continue-on-error: true + env: + FREERASP_USE_SPM: "1" + USE_FRAMEWORKS: dynamic + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup + uses: ./.github/actions/setup + + - name: Install pods (SPM) + working-directory: ./example/ios + run: pod install + + - name: Build example for iOS (SPM) + working-directory: ./example + run: | + yarn build:ios From 8ee8c1f9e6e361fdc07d7627f502670168a12a59 Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Thu, 16 Jul 2026 12:59:51 +0200 Subject: [PATCH 03/13] feat: add TalsecRuntime SPM embed helper and placeholder wiring --- example/ios/Podfile | 12 +++++++ freerasp-react-native.podspec | 32 +++++++++++++++-- freerasp_spm.rb | 66 +++++++++++++++++++++++++++++++++++ 3 files changed, 107 insertions(+), 3 deletions(-) create mode 100644 freerasp_spm.rb diff --git a/example/ios/Podfile b/example/ios/Podfile index d2f40b2..034c04e 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -35,5 +35,17 @@ target 'FreeraspRNExample' do :mac_catalyst_enabled => false, # :ccache_enabled => true ) + + # freeRASP: on the opt-in SPM path, embed TalsecRuntime into the app target + # so it ships inside the app bundle (otherwise dyld fails at launch). + # In a real app (freerasp-react-native installed in node_modules) resolve it via: + # require Pod::Executable.execute_command('node', ['-p', + # 'require.resolve("freerasp-react-native/freerasp_spm.rb", {paths: [process.argv[1]]})', + # __dir__]).strip + # This example consumes the library from the repo root, so it requires it directly. + if ENV['FREERASP_USE_SPM'] == '1' + require_relative '../../freerasp_spm.rb' + freerasp_embed_talsec_spm!(installer) + end end end diff --git a/freerasp-react-native.podspec b/freerasp-react-native.podspec index 935ffbc..4dd10f1 100644 --- a/freerasp-react-native.podspec +++ b/freerasp-react-native.podspec @@ -3,6 +3,31 @@ require "json" package = JSON.parse(File.read(File.join(__dir__, "package.json"))) folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' +# --------------------------------------------------------------------------- +# Swift Package Manager (SPM) — PHASE 1 PRE-PREPARATION (non-breaking) +# +# TODO(SPM infra): the dedicated RN-flavour SPM manifest repo + GCP-hosted +# TalsecRuntime xcframework are NOT ready yet. The values below are PLACEHOLDERS. +# The opt-in SPM path (FREERASP_USE_SPM=1) is wired but NOT usable until the infra +# lands. The vendored xcframework remains the default and keeps working. +# +# IMPORTANT: this must point at the RN-flavour manifest repo — NOT talsec/Free-RASP-iOS +# (that is the *native* iOS flavour and is the wrong artifact for React Native). +# +# PHASE 2 FLIP (do this once the infra is ready — breaking, major version): +# 1. Set TALSEC_SPM_URL / TALSEC_SPM_MIN_VERSION to the real manifest repo + tag. +# 2. Remove the `use_spm` flag and the vendored branch below; call spm_dependency +# unconditionally; `raise` if spm_dependency is unavailable (RN < 0.75). +# 3. `git rm ios/TalsecRuntime.xcframework` and drop its ref in the .xcodeproj. +# 4. example/ios/Podfile: embed unconditionally + dynamic frameworks. +# 5. Expo plugin: enable iOS SPM by default (see plugin/src/index.ts). +# 6. package.json: major bump + react-native peerDependency ">=0.75.0". +# 7. CI: make the SPM iOS build the blocking gate; drop the vendored job. +# 8. Release: drop dSYM check/attach (dSYMs come from the GCP/upstream flow). +# --------------------------------------------------------------------------- +talsec_spm_url = 'https://github.com/talsec/TODO-freerasp-ios-spm' # TODO(SPM infra): real manifest repo +talsec_spm_min_version = '0.0.0' # TODO(SPM infra): real version once the manifest repo is tagged + Pod::Spec.new do |s| s.name = "freerasp-react-native" s.version = package["version"] @@ -29,14 +54,15 @@ Pod::Spec.new do |s| ] if use_spm - # TalsecRuntime is resolved via Swift Package Manager (talsec/Free-RASP-iOS). + # TalsecRuntime is resolved via Swift Package Manager (dedicated RN-flavour manifest repo). # spm_dependency injects an SPM reference into the Pods-generated Xcode project. # NOTE: the vendored xcframework is intentionally NOT linked here to avoid # duplicate symbols. + # TODO(SPM infra): placeholders above — this branch is not usable until the infra lands. s.ios.deployment_target = '13.0' spm_dependency(s, - url: 'https://github.com/talsec/Free-RASP-iOS', - requirement: { kind: 'upToNextMajorVersion', minimumVersion: '6.14.5' }, + url: talsec_spm_url, + requirement: { kind: 'upToNextMajorVersion', minimumVersion: talsec_spm_min_version }, products: ['TalsecRuntime'] ) else diff --git a/freerasp_spm.rb b/freerasp_spm.rb new file mode 100644 index 0000000..f824d5c --- /dev/null +++ b/freerasp_spm.rb @@ -0,0 +1,66 @@ +# freeRASP — Swift Package Manager (SPM) opt-in helper. +# +# When the opt-in SPM integration is enabled (FREERASP_USE_SPM=1), the podspec +# resolves TalsecRuntime through the `spm_dependency` helper, which attaches the +# package product to the *pod* target only. With dynamically linked frameworks +# (USE_FRAMEWORKS=dynamic) the app then crashes at launch with: +# +# Library not loaded: @rpath/TalsecRuntime.framework/TalsecRuntime +# +# because nothing embeds the Swift package's binary framework into the app bundle. +# +# Call `freerasp_embed_talsec_spm!(installer)` from your Podfile `post_install` +# (after `react_native_post_install`) to add TalsecRuntime to the application +# target(s) so Xcode embeds and signs it automatically. +# +# Keep the version requirement in sync with `freerasp-react-native.podspec`. +# +# TODO(SPM infra): the `url`/`requirement` defaults below are PLACEHOLDERS. They must +# match `freerasp-react-native.podspec` and point at the dedicated RN-flavour manifest +# repo (NOT talsec/Free-RASP-iOS, which is the native flavour). Not usable until the +# GCP-hosted xcframework + manifest repo infra is ready. + +def freerasp_embed_talsec_spm!(installer, + url: 'https://github.com/talsec/TODO-freerasp-ios-spm', + requirement: { kind: 'upToNextMajorVersion', minimumVersion: '0.0.0' }, + product: 'TalsecRuntime') + + pkg_class = Xcodeproj::Project::Object::XCRemoteSwiftPackageReference + ref_class = Xcodeproj::Project::Object::XCSwiftPackageProductDependency + + installer.aggregate_targets.each do |aggregate_target| + project = aggregate_target.user_project + next if project.nil? + + # Find or create the package reference in the application project. + pkg = project.root_object.package_references.find do |p| + p.class == pkg_class && p.repositoryURL == url + end + unless pkg + pkg = project.new(pkg_class) + pkg.repositoryURL = url + pkg.requirement = requirement + project.root_object.package_references << pkg + end + + aggregate_target.user_targets.each do |target| + next unless target.respond_to?(:product_type) && + target.product_type == 'com.apple.product-type.application' + + # Add the product dependency to the app target; Xcode embeds & signs + # Swift package framework products of an application target automatically. + existing = target.package_product_dependencies.find do |r| + r.class == ref_class && r.package == pkg && r.product_name == product + end + next if existing + + Pod::UI.puts "[freeRASP][SPM] Embedding #{product} into app target #{target.name}" + dep = project.new(ref_class) + dep.package = pkg + dep.product_name = product + target.package_product_dependencies << dep + end + + project.save + end +end From c4e153261395a2e3d9e1b4e19b73cd50c80863ac Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Thu, 16 Jul 2026 12:59:51 +0200 Subject: [PATCH 04/13] feat: scaffold opt-in Expo iOS SPM support --- plugin/build/index.d.ts | 1 + plugin/build/index.js | 90 ++++++++++++++++++++++++++++++++++ plugin/build/pluginConfig.d.ts | 23 +++++++++ plugin/src/index.ts | 80 ++++++++++++++++++++++++++++++ plugin/src/pluginConfig.ts | 24 +++++++++ 5 files changed, 218 insertions(+) diff --git a/plugin/build/index.d.ts b/plugin/build/index.d.ts index a03766e..078b0fa 100644 --- a/plugin/build/index.d.ts +++ b/plugin/build/index.d.ts @@ -1,3 +1,4 @@ +import { type ConfigPlugin } from '@expo/config-plugins'; import { type PluginConfigType } from './pluginConfig'; declare const _default: ConfigPlugin; export default _default; diff --git a/plugin/build/index.js b/plugin/build/index.js index bc07ecc..549ffd7 100644 --- a/plugin/build/index.js +++ b/plugin/build/index.js @@ -1,6 +1,31 @@ "use strict"; +var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + var desc = Object.getOwnPropertyDescriptor(m, k); + if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { + desc = { enumerable: true, get: function() { return m[k]; } }; + } + Object.defineProperty(o, k2, desc); +}) : (function(o, m, k, k2) { + if (k2 === undefined) k2 = k; + o[k2] = m[k]; +})); +var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { + Object.defineProperty(o, "default", { enumerable: true, value: v }); +}) : function(o, v) { + o["default"] = v; +}); +var __importStar = (this && this.__importStar) || function (mod) { + if (mod && mod.__esModule) return mod; + var result = {}; + if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); + __setModuleDefault(result, mod); + return result; +}; Object.defineProperty(exports, "__esModule", { value: true }); const config_plugins_1 = require("@expo/config-plugins"); +const fs = __importStar(require("fs")); +const path = __importStar(require("path")); const { createBuildGradlePropsConfigPlugin } = config_plugins_1.AndroidConfig.BuildProperties; const urlFreerasp = 'https://europe-west3-maven.pkg.dev/talsec-artifact-repository/freerasp'; const urlJitpack = 'https://www.jitpack.io'; @@ -71,10 +96,75 @@ const withAndroidR8Version = (expoConfig, props) => { return config; }); }; +// --------------------------------------------------------------------------- +// iOS — experimental Swift Package Manager delivery of TalsecRuntime (opt-in). +// +// TODO(SPM infra): NOT usable until the dedicated RN-flavour manifest repo + +// GCP-hosted xcframework are ready. Enabled only via `ios.useSpm: true`; off by +// default so existing Expo apps keep using the vendored xcframework (non-breaking). +// TODO(verify): the Podfile anchor and the full `expo prebuild` flow are unverified +// until the SPM infra lands (see freerasp-react-native.podspec "PHASE 2 FLIP"). +// --------------------------------------------------------------------------- +const FREERASP_SPM_EMBED_TAG = '# @generated freerasp-react-native (SPM embed)'; +/** + * Force dynamically linked frameworks — required by `spm_dependency`. + */ +const withFreeraspIosDynamicFrameworks = (config) => { + return (0, config_plugins_1.withPodfileProperties)(config, (config) => { + config.modResults['ios.useFrameworks'] = 'dynamic'; + return config; + }); +}; +/** + * Inject the TalsecRuntime embed step into the generated Podfile `post_install`, + * so the SPM binary framework ends up in the app bundle (otherwise dyld fails at launch). + */ +const withFreeraspIosSpmEmbed = (config) => { + return (0, config_plugins_1.withDangerousMod)(config, [ + 'ios', + (config) => { + const podfilePath = path.join(config.modRequest.platformProjectRoot, 'Podfile'); + let contents = fs.readFileSync(podfilePath, 'utf-8'); + if (!contents.includes(FREERASP_SPM_EMBED_TAG)) { + const anchor = 'post_install do |installer|'; + const anchorIndex = contents.indexOf(anchor); + if (anchorIndex === -1) { + config_plugins_1.WarningAggregator.addWarningIOS('freerasp-react-native', 'Could not find a `post_install` block in the Podfile to inject the ' + + 'TalsecRuntime SPM embed step.'); + } + else { + const snippet = [ + '', + ` ${FREERASP_SPM_EMBED_TAG}`, + " require Pod::Executable.execute_command('node', ['-p',", + ` 'require.resolve("freerasp-react-native/freerasp_spm.rb", {paths: [process.argv[1]]})',`, + ' __dir__]).strip', + ' freerasp_embed_talsec_spm!(installer)', + ].join('\n'); + const insertAt = anchorIndex + anchor.length; + contents = + contents.slice(0, insertAt) + snippet + contents.slice(insertAt); + fs.writeFileSync(podfilePath, contents); + } + } + return config; + }, + ]); +}; +const withRnTalsecIos = (config, props) => { + // Off by default (non-breaking). Enable via `ios.useSpm: true`. + if (!props?.ios?.useSpm) { + return config; + } + config = withFreeraspIosDynamicFrameworks(config); + config = withFreeraspIosSpmEmbed(config); + return config; +}; const withRnTalsecApp = (config, props) => { config = withBuildscriptDependency(config); config = withAndroidMinSdkVersion(config, props); config = withAndroidR8Version(config, props); + config = withRnTalsecIos(config, props); return config; }; let pkg = { diff --git a/plugin/build/pluginConfig.d.ts b/plugin/build/pluginConfig.d.ts index 3121ba3..9a77c38 100644 --- a/plugin/build/pluginConfig.d.ts +++ b/plugin/build/pluginConfig.d.ts @@ -7,6 +7,11 @@ export interface PluginConfigType { * @platform android */ android?: PluginConfigTypeAndroid; + /** + * Interface representing available configuration for iOS native build. + * @platform ios + */ + ios?: PluginConfigTypeIos; } /** * Interface representing available configuration for Android native build properties. @@ -19,3 +24,21 @@ export interface PluginConfigTypeAndroid { minSdkVersion?: number; R8Version?: string; } +/** + * Interface representing available configuration for iOS native build. + * @platform ios + * + * TODO(SPM infra): experimental Swift Package Manager delivery of TalsecRuntime. + * Not usable until the dedicated RN-flavour manifest repo + GCP-hosted xcframework + * are ready. Defaults to off — the vendored xcframework is used. + */ +export interface PluginConfigTypeIos { + /** + * Opt in to the experimental SPM integration of TalsecRuntime. When enabled the + * plugin sets dynamically linked frameworks and injects the TalsecRuntime embed + * step into the generated Podfile's `post_install`. + * + * @default false + */ + useSpm?: boolean; +} diff --git a/plugin/src/index.ts b/plugin/src/index.ts index c3e8bb3..bbee851 100644 --- a/plugin/src/index.ts +++ b/plugin/src/index.ts @@ -2,10 +2,14 @@ import { AndroidConfig, WarningAggregator, createRunOncePlugin, + withDangerousMod, + withPodfileProperties, withProjectBuildGradle, type ConfigPlugin, } from '@expo/config-plugins'; import { type ExpoConfig } from '@expo/config-types'; +import * as fs from 'fs'; +import * as path from 'path'; import { type PluginConfigType } from './pluginConfig'; const { createBuildGradlePropsConfigPlugin } = AndroidConfig.BuildProperties; @@ -106,10 +110,86 @@ const withAndroidR8Version: ConfigPlugin = ( }); }; +// --------------------------------------------------------------------------- +// iOS — experimental Swift Package Manager delivery of TalsecRuntime (opt-in). +// +// TODO(SPM infra): NOT usable until the dedicated RN-flavour manifest repo + +// GCP-hosted xcframework are ready. Enabled only via `ios.useSpm: true`; off by +// default so existing Expo apps keep using the vendored xcframework (non-breaking). +// TODO(verify): the Podfile anchor and the full `expo prebuild` flow are unverified +// until the SPM infra lands (see freerasp-react-native.podspec "PHASE 2 FLIP"). +// --------------------------------------------------------------------------- + +const FREERASP_SPM_EMBED_TAG = '# @generated freerasp-react-native (SPM embed)'; + +/** + * Force dynamically linked frameworks — required by `spm_dependency`. + */ +const withFreeraspIosDynamicFrameworks: ConfigPlugin = (config) => { + return withPodfileProperties(config, (config) => { + config.modResults['ios.useFrameworks'] = 'dynamic'; + return config; + }); +}; + +/** + * Inject the TalsecRuntime embed step into the generated Podfile `post_install`, + * so the SPM binary framework ends up in the app bundle (otherwise dyld fails at launch). + */ +const withFreeraspIosSpmEmbed: ConfigPlugin = (config) => { + return withDangerousMod(config, [ + 'ios', + (config) => { + const podfilePath = path.join( + config.modRequest.platformProjectRoot, + 'Podfile' + ); + let contents = fs.readFileSync(podfilePath, 'utf-8'); + + if (!contents.includes(FREERASP_SPM_EMBED_TAG)) { + const anchor = 'post_install do |installer|'; + const anchorIndex = contents.indexOf(anchor); + if (anchorIndex === -1) { + WarningAggregator.addWarningIOS( + 'freerasp-react-native', + 'Could not find a `post_install` block in the Podfile to inject the ' + + 'TalsecRuntime SPM embed step.' + ); + } else { + const snippet = [ + '', + ` ${FREERASP_SPM_EMBED_TAG}`, + " require Pod::Executable.execute_command('node', ['-p',", + ` 'require.resolve("freerasp-react-native/freerasp_spm.rb", {paths: [process.argv[1]]})',`, + ' __dir__]).strip', + ' freerasp_embed_talsec_spm!(installer)', + ].join('\n'); + const insertAt = anchorIndex + anchor.length; + contents = + contents.slice(0, insertAt) + snippet + contents.slice(insertAt); + fs.writeFileSync(podfilePath, contents); + } + } + return config; + }, + ]); +}; + +const withRnTalsecIos: ConfigPlugin = (config, props) => { + // Off by default (non-breaking). Enable via `ios.useSpm: true`. + if (!props?.ios?.useSpm) { + return config; + } + config = withFreeraspIosDynamicFrameworks(config); + config = withFreeraspIosSpmEmbed(config); + return config; +}; + const withRnTalsecApp: ConfigPlugin = (config, props) => { config = withBuildscriptDependency(config); config = withAndroidMinSdkVersion(config, props); config = withAndroidR8Version(config, props); + config = withRnTalsecIos(config, props); return config; }; diff --git a/plugin/src/pluginConfig.ts b/plugin/src/pluginConfig.ts index 075ef0f..97c0108 100644 --- a/plugin/src/pluginConfig.ts +++ b/plugin/src/pluginConfig.ts @@ -7,6 +7,11 @@ export interface PluginConfigType { * @platform android */ android?: PluginConfigTypeAndroid; + /** + * Interface representing available configuration for iOS native build. + * @platform ios + */ + ios?: PluginConfigTypeIos; } /** @@ -20,3 +25,22 @@ export interface PluginConfigTypeAndroid { minSdkVersion?: number; R8Version?: string; } + +/** + * Interface representing available configuration for iOS native build. + * @platform ios + * + * TODO(SPM infra): experimental Swift Package Manager delivery of TalsecRuntime. + * Not usable until the dedicated RN-flavour manifest repo + GCP-hosted xcframework + * are ready. Defaults to off — the vendored xcframework is used. + */ +export interface PluginConfigTypeIos { + /** + * Opt in to the experimental SPM integration of TalsecRuntime. When enabled the + * plugin sets dynamically linked frameworks and injects the TalsecRuntime embed + * step into the generated Podfile's `post_install`. + * + * @default false + */ + useSpm?: boolean; +} From 5bc2690ef0531cd5ba74480589a5e6cf9015aa82 Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Thu, 16 Jul 2026 14:04:04 +0200 Subject: [PATCH 05/13] feat: make SPM the sole iOS delivery for TalsecRuntime (stage 2 prep) --- example/ios/Podfile | 25 +++++------ freerasp-react-native.podspec | 79 +++++++++++++---------------------- package.json | 2 +- 3 files changed, 39 insertions(+), 67 deletions(-) diff --git a/example/ios/Podfile b/example/ios/Podfile index 034c04e..bb52884 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -8,15 +8,12 @@ require Pod::Executable.execute_command('node', ['-p', platform :ios, min_ios_version_supported prepare_react_native_project! -# To test the experimental Swift Package Manager integration of freerasp-react-native, -# install with the SPM opt-in flag and dynamic frameworks: -# FREERASP_USE_SPM=1 USE_FRAMEWORKS=dynamic pod install -# Without these flags the default vendored xcframework is used. -linkage = ENV['USE_FRAMEWORKS'] -if linkage != nil - Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green - use_frameworks! :linkage => linkage.to_sym -end +# freerasp-react-native delivers TalsecRuntime via Swift Package Manager, which +# requires dynamically linked frameworks. Default to dynamic; USE_FRAMEWORKS may +# override the linkage. +linkage = (ENV['USE_FRAMEWORKS'] || 'dynamic').to_sym +Pod::UI.puts "Configuring Pod with #{linkage}ally linked Frameworks".green +use_frameworks! :linkage => linkage target 'FreeraspRNExample' do config = use_native_modules! @@ -36,16 +33,14 @@ target 'FreeraspRNExample' do # :ccache_enabled => true ) - # freeRASP: on the opt-in SPM path, embed TalsecRuntime into the app target - # so it ships inside the app bundle (otherwise dyld fails at launch). + # freeRASP: embed TalsecRuntime (delivered via SPM) into the app target so it + # ships inside the app bundle (otherwise dyld fails at launch). # In a real app (freerasp-react-native installed in node_modules) resolve it via: # require Pod::Executable.execute_command('node', ['-p', # 'require.resolve("freerasp-react-native/freerasp_spm.rb", {paths: [process.argv[1]]})', # __dir__]).strip # This example consumes the library from the repo root, so it requires it directly. - if ENV['FREERASP_USE_SPM'] == '1' - require_relative '../../freerasp_spm.rb' - freerasp_embed_talsec_spm!(installer) - end + require_relative '../../freerasp_spm.rb' + freerasp_embed_talsec_spm!(installer) end end diff --git a/freerasp-react-native.podspec b/freerasp-react-native.podspec index 4dd10f1..9e44c82 100644 --- a/freerasp-react-native.podspec +++ b/freerasp-react-native.podspec @@ -4,26 +4,20 @@ package = JSON.parse(File.read(File.join(__dir__, "package.json"))) folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' # --------------------------------------------------------------------------- -# Swift Package Manager (SPM) — PHASE 1 PRE-PREPARATION (non-breaking) +# Swift Package Manager (SPM) delivery of TalsecRuntime — STAGE 2 SHAPE. # -# TODO(SPM infra): the dedicated RN-flavour SPM manifest repo + GCP-hosted -# TalsecRuntime xcframework are NOT ready yet. The values below are PLACEHOLDERS. -# The opt-in SPM path (FREERASP_USE_SPM=1) is wired but NOT usable until the infra -# lands. The vendored xcframework remains the default and keeps working. +# TalsecRuntime is delivered exclusively via SPM (a dedicated RN-flavour manifest +# repo + a GCP-hosted xcframework). CocoaPods stays for React Native itself; +# `spm_dependency` (RN >= 0.75) injects the SPM reference into the Pods project and +# `freerasp_embed_talsec_spm!` (see freerasp_spm.rb) embeds the framework into the +# app target. Requires USE_FRAMEWORKS=dynamic in the consumer Podfile and iOS 13+. # -# IMPORTANT: this must point at the RN-flavour manifest repo — NOT talsec/Free-RASP-iOS -# (that is the *native* iOS flavour and is the wrong artifact for React Native). -# -# PHASE 2 FLIP (do this once the infra is ready — breaking, major version): -# 1. Set TALSEC_SPM_URL / TALSEC_SPM_MIN_VERSION to the real manifest repo + tag. -# 2. Remove the `use_spm` flag and the vendored branch below; call spm_dependency -# unconditionally; `raise` if spm_dependency is unavailable (RN < 0.75). -# 3. `git rm ios/TalsecRuntime.xcframework` and drop its ref in the .xcodeproj. -# 4. example/ios/Podfile: embed unconditionally + dynamic frameworks. -# 5. Expo plugin: enable iOS SPM by default (see plugin/src/index.ts). -# 6. package.json: major bump + react-native peerDependency ">=0.75.0". -# 7. CI: make the SPM iOS build the blocking gate; drop the vendored job. -# 8. Release: drop dSYM check/attach (dSYMs come from the GCP/upstream flow). +# TODO(SPM infra): the manifest repo + GCP-hosted xcframework are NOT ready yet, so +# the values below are PLACEHOLDERS — the iOS build will not resolve until they exist. +# Before releasing, also: `git rm ios/TalsecRuntime.xcframework` + exclude it from npm, +# major version bump, and drop the dSYM check/attach release steps. +# IMPORTANT: point at the RN-flavour manifest repo — NOT talsec/Free-RASP-iOS +# (that is the native flavour and the wrong artifact for React Native). # --------------------------------------------------------------------------- talsec_spm_url = 'https://github.com/talsec/TODO-freerasp-ios-spm' # TODO(SPM infra): real manifest repo talsec_spm_min_version = '0.0.0' # TODO(SPM infra): real version once the manifest repo is tagged @@ -36,43 +30,26 @@ Pod::Spec.new do |s| s.license = package["license"] s.authors = package["author"] - s.platforms = { :ios => "11.0" } + s.platforms = { :ios => "13.0" } s.source = { :git => "https://github.com/talsec/freerasp-react-native.git", :tag => "#{s.version}" } - # Opt-in Swift Package Manager integration for the TalsecRuntime dependency. - # Enabled only when FREERASP_USE_SPM=1 AND the running React Native provides the - # spm_dependency helper (RN >= 0.75). Otherwise the vendored xcframework is used - # (default — no breaking change for existing consumers). The SPM path additionally - # requires USE_FRAMEWORKS=dynamic in the consumer Podfile and iOS 13+. - use_spm = ENV['FREERASP_USE_SPM'] == '1' && respond_to?(:spm_dependency, true) - - source_globs = [ - 'ios/models/*.{h,m,mm,swift}', - 'ios/utils/*.{h,m,mm,swift}', - 'ios/dispatchers/*.{h,m,mm,swift}', - 'ios/*.{h,m,mm,swift}', - ] + s.source_files = 'ios/models/*.{h,m,mm,swift}', + 'ios/utils/*.{h,m,mm,swift}', + 'ios/dispatchers/*.{h,m,mm,swift}', + 'ios/*.{h,m,mm,swift}' - if use_spm - # TalsecRuntime is resolved via Swift Package Manager (dedicated RN-flavour manifest repo). - # spm_dependency injects an SPM reference into the Pods-generated Xcode project. - # NOTE: the vendored xcframework is intentionally NOT linked here to avoid - # duplicate symbols. - # TODO(SPM infra): placeholders above — this branch is not usable until the infra lands. - s.ios.deployment_target = '13.0' - spm_dependency(s, - url: talsec_spm_url, - requirement: { kind: 'upToNextMajorVersion', minimumVersion: talsec_spm_min_version }, - products: ['TalsecRuntime'] - ) - else - # TalsecRuntime is provided by the vendored xcframework (default). - source_globs << 'ios/TalsecRuntime.xcframework' - s.xcconfig = { 'OTHER_LDFLAGS' => '-framework TalsecRuntime' } - s.ios.vendored_frameworks = 'ios/TalsecRuntime.xcframework' + # TalsecRuntime is resolved exclusively via Swift Package Manager. spm_dependency + # (RN >= 0.75) injects the SPM reference into the Pods-generated Xcode project; the + # binary framework is embedded into the app target by `freerasp_embed_talsec_spm!` + # (freerasp_spm.rb), called from the consumer Podfile post_install. + unless respond_to?(:spm_dependency, true) + raise "[freerasp-react-native] iOS integration requires React Native >= 0.75 (the spm_dependency helper)." end - - s.source_files = source_globs + spm_dependency(s, + url: talsec_spm_url, + requirement: { kind: 'upToNextMajorVersion', minimumVersion: talsec_spm_min_version }, + products: ['TalsecRuntime'] + ) # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0. # See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79. diff --git a/package.json b/package.json index ba22a47..4012a4a 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "peerDependencies": { "expo": ">=47.0.0", "react": "*", - "react-native": "*" + "react-native": ">=0.75.0" }, "peerDependenciesMeta": { "expo": { From 6d804b5348e1ac606e2da8c06a99e71e88595861 Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Thu, 16 Jul 2026 14:04:04 +0200 Subject: [PATCH 06/13] feat: enable Expo iOS SPM support by default --- plugin/build/index.js | 17 +++++++---------- plugin/build/pluginConfig.d.ts | 23 ----------------------- plugin/src/index.ts | 17 +++++++---------- plugin/src/pluginConfig.ts | 24 ------------------------ 4 files changed, 14 insertions(+), 67 deletions(-) diff --git a/plugin/build/index.js b/plugin/build/index.js index 549ffd7..118c780 100644 --- a/plugin/build/index.js +++ b/plugin/build/index.js @@ -97,13 +97,14 @@ const withAndroidR8Version = (expoConfig, props) => { }); }; // --------------------------------------------------------------------------- -// iOS — experimental Swift Package Manager delivery of TalsecRuntime (opt-in). +// iOS — Swift Package Manager delivery of TalsecRuntime. // +// Sets dynamically linked frameworks (required by spm_dependency) and injects the +// TalsecRuntime embed step into the generated Podfile's post_install. // TODO(SPM infra): NOT usable until the dedicated RN-flavour manifest repo + -// GCP-hosted xcframework are ready. Enabled only via `ios.useSpm: true`; off by -// default so existing Expo apps keep using the vendored xcframework (non-breaking). +// GCP-hosted xcframework are ready (see freerasp-react-native.podspec). // TODO(verify): the Podfile anchor and the full `expo prebuild` flow are unverified -// until the SPM infra lands (see freerasp-react-native.podspec "PHASE 2 FLIP"). +// until the SPM infra lands. // --------------------------------------------------------------------------- const FREERASP_SPM_EMBED_TAG = '# @generated freerasp-react-native (SPM embed)'; /** @@ -151,11 +152,7 @@ const withFreeraspIosSpmEmbed = (config) => { }, ]); }; -const withRnTalsecIos = (config, props) => { - // Off by default (non-breaking). Enable via `ios.useSpm: true`. - if (!props?.ios?.useSpm) { - return config; - } +const withRnTalsecIos = (config) => { config = withFreeraspIosDynamicFrameworks(config); config = withFreeraspIosSpmEmbed(config); return config; @@ -164,7 +161,7 @@ const withRnTalsecApp = (config, props) => { config = withBuildscriptDependency(config); config = withAndroidMinSdkVersion(config, props); config = withAndroidR8Version(config, props); - config = withRnTalsecIos(config, props); + config = withRnTalsecIos(config); return config; }; let pkg = { diff --git a/plugin/build/pluginConfig.d.ts b/plugin/build/pluginConfig.d.ts index 9a77c38..3121ba3 100644 --- a/plugin/build/pluginConfig.d.ts +++ b/plugin/build/pluginConfig.d.ts @@ -7,11 +7,6 @@ export interface PluginConfigType { * @platform android */ android?: PluginConfigTypeAndroid; - /** - * Interface representing available configuration for iOS native build. - * @platform ios - */ - ios?: PluginConfigTypeIos; } /** * Interface representing available configuration for Android native build properties. @@ -24,21 +19,3 @@ export interface PluginConfigTypeAndroid { minSdkVersion?: number; R8Version?: string; } -/** - * Interface representing available configuration for iOS native build. - * @platform ios - * - * TODO(SPM infra): experimental Swift Package Manager delivery of TalsecRuntime. - * Not usable until the dedicated RN-flavour manifest repo + GCP-hosted xcframework - * are ready. Defaults to off — the vendored xcframework is used. - */ -export interface PluginConfigTypeIos { - /** - * Opt in to the experimental SPM integration of TalsecRuntime. When enabled the - * plugin sets dynamically linked frameworks and injects the TalsecRuntime embed - * step into the generated Podfile's `post_install`. - * - * @default false - */ - useSpm?: boolean; -} diff --git a/plugin/src/index.ts b/plugin/src/index.ts index bbee851..b58967c 100644 --- a/plugin/src/index.ts +++ b/plugin/src/index.ts @@ -111,13 +111,14 @@ const withAndroidR8Version: ConfigPlugin = ( }; // --------------------------------------------------------------------------- -// iOS — experimental Swift Package Manager delivery of TalsecRuntime (opt-in). +// iOS — Swift Package Manager delivery of TalsecRuntime. // +// Sets dynamically linked frameworks (required by spm_dependency) and injects the +// TalsecRuntime embed step into the generated Podfile's post_install. // TODO(SPM infra): NOT usable until the dedicated RN-flavour manifest repo + -// GCP-hosted xcframework are ready. Enabled only via `ios.useSpm: true`; off by -// default so existing Expo apps keep using the vendored xcframework (non-breaking). +// GCP-hosted xcframework are ready (see freerasp-react-native.podspec). // TODO(verify): the Podfile anchor and the full `expo prebuild` flow are unverified -// until the SPM infra lands (see freerasp-react-native.podspec "PHASE 2 FLIP"). +// until the SPM infra lands. // --------------------------------------------------------------------------- const FREERASP_SPM_EMBED_TAG = '# @generated freerasp-react-native (SPM embed)'; @@ -175,11 +176,7 @@ const withFreeraspIosSpmEmbed: ConfigPlugin = (config) => { ]); }; -const withRnTalsecIos: ConfigPlugin = (config, props) => { - // Off by default (non-breaking). Enable via `ios.useSpm: true`. - if (!props?.ios?.useSpm) { - return config; - } +const withRnTalsecIos: ConfigPlugin = (config) => { config = withFreeraspIosDynamicFrameworks(config); config = withFreeraspIosSpmEmbed(config); return config; @@ -189,7 +186,7 @@ const withRnTalsecApp: ConfigPlugin = (config, props) => { config = withBuildscriptDependency(config); config = withAndroidMinSdkVersion(config, props); config = withAndroidR8Version(config, props); - config = withRnTalsecIos(config, props); + config = withRnTalsecIos(config); return config; }; diff --git a/plugin/src/pluginConfig.ts b/plugin/src/pluginConfig.ts index 97c0108..075ef0f 100644 --- a/plugin/src/pluginConfig.ts +++ b/plugin/src/pluginConfig.ts @@ -7,11 +7,6 @@ export interface PluginConfigType { * @platform android */ android?: PluginConfigTypeAndroid; - /** - * Interface representing available configuration for iOS native build. - * @platform ios - */ - ios?: PluginConfigTypeIos; } /** @@ -25,22 +20,3 @@ export interface PluginConfigTypeAndroid { minSdkVersion?: number; R8Version?: string; } - -/** - * Interface representing available configuration for iOS native build. - * @platform ios - * - * TODO(SPM infra): experimental Swift Package Manager delivery of TalsecRuntime. - * Not usable until the dedicated RN-flavour manifest repo + GCP-hosted xcframework - * are ready. Defaults to off — the vendored xcframework is used. - */ -export interface PluginConfigTypeIos { - /** - * Opt in to the experimental SPM integration of TalsecRuntime. When enabled the - * plugin sets dynamically linked frameworks and injects the TalsecRuntime embed - * step into the generated Podfile's `post_install`. - * - * @default false - */ - useSpm?: boolean; -} From 383d91a35a2201b05a3d7df32870ea036c20b9a1 Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Thu, 16 Jul 2026 14:04:04 +0200 Subject: [PATCH 07/13] ci: build iOS example via SPM only --- .github/workflows/ci.yml | 32 +++++++------------------------- 1 file changed, 7 insertions(+), 25 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8b5b7a3..23aceac 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,33 +82,15 @@ jobs: yarn build:android build-ios: - runs-on: macos-latest - needs: build-library - steps: - - name: Checkout - uses: actions/checkout@v6 - - - name: Setup - uses: ./.github/actions/setup - - - name: Install pods - working-directory: ./example/ios - run: pod install - - - name: Build example for iOS - working-directory: ./example - run: | - yarn build:ios - - build-ios-spm: - # Experimental: smoke-test the opt-in Swift Package Manager integration - # (FREERASP_USE_SPM=1). Non-blocking while SPM support is experimental; the - # vendored build-ios job above remains the gating check. + # iOS build. TalsecRuntime is delivered via Swift Package Manager (dynamic + # frameworks). This builds green once the real registry link replaces the + # placeholder in freerasp-react-native.podspec. + # Kept non-blocking (continue-on-error) only until the SPM infra lands; drop + # that line to make it a required gate. runs-on: macos-latest needs: build-library continue-on-error: true env: - FREERASP_USE_SPM: "1" USE_FRAMEWORKS: dynamic steps: - name: Checkout @@ -117,11 +99,11 @@ jobs: - name: Setup uses: ./.github/actions/setup - - name: Install pods (SPM) + - name: Install pods working-directory: ./example/ios run: pod install - - name: Build example for iOS (SPM) + - name: Build example for iOS working-directory: ./example run: | yarn build:ios From 80ed39320ed953c02eacdd26e68ab598656ddfcf Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Thu, 16 Jul 2026 14:48:34 +0200 Subject: [PATCH 08/13] refactor: default to SPM with vendored fallback (react-native-firebase pattern) --- example/ios/Podfile | 13 ++++--- freerasp-react-native.podspec | 70 +++++++++++++++++++++-------------- package.json | 2 +- 3 files changed, 52 insertions(+), 33 deletions(-) diff --git a/example/ios/Podfile b/example/ios/Podfile index bb52884..d3ec9bb 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -33,14 +33,17 @@ target 'FreeraspRNExample' do # :ccache_enabled => true ) - # freeRASP: embed TalsecRuntime (delivered via SPM) into the app target so it - # ships inside the app bundle (otherwise dyld fails at launch). - # In a real app (freerasp-react-native installed in node_modules) resolve it via: + # freeRASP: when TalsecRuntime is delivered via SPM (the default on RN >= 0.75 + # unless FREERASP_DISABLE_SPM=1), embed it into the app target so it ships inside + # the app bundle (otherwise dyld fails at launch). Skipped on the vendored fallback. + # In a real app (freerasp-react-native installed in node_modules) resolve the helper via: # require Pod::Executable.execute_command('node', ['-p', # 'require.resolve("freerasp-react-native/freerasp_spm.rb", {paths: [process.argv[1]]})', # __dir__]).strip # This example consumes the library from the repo root, so it requires it directly. - require_relative '../../freerasp_spm.rb' - freerasp_embed_talsec_spm!(installer) + if respond_to?(:spm_dependency, true) && ENV['FREERASP_DISABLE_SPM'] != '1' + require_relative '../../freerasp_spm.rb' + freerasp_embed_talsec_spm!(installer) + end end end diff --git a/freerasp-react-native.podspec b/freerasp-react-native.podspec index 9e44c82..82f0336 100644 --- a/freerasp-react-native.podspec +++ b/freerasp-react-native.podspec @@ -4,22 +4,24 @@ package = JSON.parse(File.read(File.join(__dir__, "package.json"))) folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' # --------------------------------------------------------------------------- -# Swift Package Manager (SPM) delivery of TalsecRuntime — STAGE 2 SHAPE. +# TalsecRuntime delivery — Swift Package Manager by default, vendored fallback. +# (Same decision logic as react-native-firebase, non-breaking.) # -# TalsecRuntime is delivered exclusively via SPM (a dedicated RN-flavour manifest -# repo + a GCP-hosted xcframework). CocoaPods stays for React Native itself; -# `spm_dependency` (RN >= 0.75) injects the SPM reference into the Pods project and -# `freerasp_embed_talsec_spm!` (see freerasp_spm.rb) embeds the framework into the -# app target. Requires USE_FRAMEWORKS=dynamic in the consumer Podfile and iOS 13+. +# spm_dependency available (RN >= 0.75) AND FREERASP_DISABLE_SPM != '1' +# -> SPM (dedicated RN-flavour manifest repo) +# otherwise (RN < 0.75, or FREERASP_DISABLE_SPM=1) +# -> vendored TalsecRuntime.xcframework # -# TODO(SPM infra): the manifest repo + GCP-hosted xcframework are NOT ready yet, so -# the values below are PLACEHOLDERS — the iOS build will not resolve until they exist. -# Before releasing, also: `git rm ios/TalsecRuntime.xcframework` + exclude it from npm, -# major version bump, and drop the dSYM check/attach release steps. +# The SPM path requires USE_FRAMEWORKS=dynamic and iOS 13+. The vendored fallback +# keeps working on any RN / linkage, so this is not a breaking change. +# +# TODO(SPM infra): the dedicated RN-flavour manifest repo (which hosts the GCS-backed +# xcframework via binaryTarget) is NOT ready yet, so the values below are PLACEHOLDERS +# — the SPM path won't resolve until it exists; consumers fall back to vendored. # IMPORTANT: point at the RN-flavour manifest repo — NOT talsec/Free-RASP-iOS # (that is the native flavour and the wrong artifact for React Native). # --------------------------------------------------------------------------- -talsec_spm_url = 'https://github.com/talsec/TODO-freerasp-ios-spm' # TODO(SPM infra): real manifest repo +talsec_spm_url = 'https://github.com/talsec/TODO-freerasp-ios-spm' # TODO(SPM infra): real manifest repo git URL talsec_spm_min_version = '0.0.0' # TODO(SPM infra): real version once the manifest repo is tagged Pod::Spec.new do |s| @@ -30,26 +32,40 @@ Pod::Spec.new do |s| s.license = package["license"] s.authors = package["author"] - s.platforms = { :ios => "13.0" } + s.platforms = { :ios => "11.0" } s.source = { :git => "https://github.com/talsec/freerasp-react-native.git", :tag => "#{s.version}" } - s.source_files = 'ios/models/*.{h,m,mm,swift}', - 'ios/utils/*.{h,m,mm,swift}', - 'ios/dispatchers/*.{h,m,mm,swift}', - 'ios/*.{h,m,mm,swift}' + # SPM is the default whenever the spm_dependency helper is available (RN >= 0.75), + # unless the consumer opts out with FREERASP_DISABLE_SPM=1. + use_spm = respond_to?(:spm_dependency, true) && ENV['FREERASP_DISABLE_SPM'] != '1' + + source_globs = [ + 'ios/models/*.{h,m,mm,swift}', + 'ios/utils/*.{h,m,mm,swift}', + 'ios/dispatchers/*.{h,m,mm,swift}', + 'ios/*.{h,m,mm,swift}', + ] - # TalsecRuntime is resolved exclusively via Swift Package Manager. spm_dependency - # (RN >= 0.75) injects the SPM reference into the Pods-generated Xcode project; the - # binary framework is embedded into the app target by `freerasp_embed_talsec_spm!` - # (freerasp_spm.rb), called from the consumer Podfile post_install. - unless respond_to?(:spm_dependency, true) - raise "[freerasp-react-native] iOS integration requires React Native >= 0.75 (the spm_dependency helper)." + if use_spm + # TalsecRuntime resolved via Swift Package Manager (dedicated RN-flavour manifest repo). + # spm_dependency injects the SPM reference into the Pods project; the framework is + # embedded into the app target by `freerasp_embed_talsec_spm!` (freerasp_spm.rb), + # called from the consumer Podfile post_install. + # TODO(SPM infra): placeholders above — this path is not usable until the infra lands. + s.ios.deployment_target = '13.0' + spm_dependency(s, + url: talsec_spm_url, + requirement: { kind: 'upToNextMajorVersion', minimumVersion: talsec_spm_min_version }, + products: ['TalsecRuntime'] + ) + else + # Vendored xcframework fallback (RN < 0.75 or FREERASP_DISABLE_SPM=1). + source_globs << 'ios/TalsecRuntime.xcframework' + s.xcconfig = { 'OTHER_LDFLAGS' => '-framework TalsecRuntime' } + s.ios.vendored_frameworks = 'ios/TalsecRuntime.xcframework' end - spm_dependency(s, - url: talsec_spm_url, - requirement: { kind: 'upToNextMajorVersion', minimumVersion: talsec_spm_min_version }, - products: ['TalsecRuntime'] - ) + + s.source_files = source_globs # Use install_modules_dependencies helper to install the dependencies if React Native version >=0.71.0. # See https://github.com/facebook/react-native/blob/febf6b7f33fdb4904669f99d795eba4c0f95d7bf/scripts/cocoapods/new_architecture.rb#L79. diff --git a/package.json b/package.json index 4012a4a..ba22a47 100644 --- a/package.json +++ b/package.json @@ -71,7 +71,7 @@ "peerDependencies": { "expo": ">=47.0.0", "react": "*", - "react-native": ">=0.75.0" + "react-native": "*" }, "peerDependenciesMeta": { "expo": { From e30a6b6af565f4d8d8e75992632f4ee13abe9de1 Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Thu, 16 Jul 2026 14:48:34 +0200 Subject: [PATCH 09/13] fix: guard Expo iOS SPM embed on SPM-active condition --- plugin/build/index.js | 24 ++++++++++++++---------- plugin/src/index.ts | 24 ++++++++++++++---------- 2 files changed, 28 insertions(+), 20 deletions(-) diff --git a/plugin/build/index.js b/plugin/build/index.js index 118c780..fa7e836 100644 --- a/plugin/build/index.js +++ b/plugin/build/index.js @@ -97,14 +97,16 @@ const withAndroidR8Version = (expoConfig, props) => { }); }; // --------------------------------------------------------------------------- -// iOS — Swift Package Manager delivery of TalsecRuntime. +// iOS — Swift Package Manager delivery of TalsecRuntime (default on RN >= 0.75). // -// Sets dynamically linked frameworks (required by spm_dependency) and injects the -// TalsecRuntime embed step into the generated Podfile's post_install. +// Sets dynamically linked frameworks (required by spm_dependency) and injects a +// guarded TalsecRuntime embed step into the generated Podfile's post_install. The +// embed is skipped when SPM is unavailable or FREERASP_DISABLE_SPM=1 (vendored +// fallback), matching the podspec's decision logic. // TODO(SPM infra): NOT usable until the dedicated RN-flavour manifest repo + -// GCP-hosted xcframework are ready (see freerasp-react-native.podspec). -// TODO(verify): the Podfile anchor and the full `expo prebuild` flow are unverified -// until the SPM infra lands. +// GCS-hosted xcframework are ready (see freerasp-react-native.podspec). +// TODO(verify): the Podfile anchor, the FREERASP_DISABLE_SPM escape hatch, and the +// full `expo prebuild` flow are unverified until the SPM infra lands. // --------------------------------------------------------------------------- const FREERASP_SPM_EMBED_TAG = '# @generated freerasp-react-native (SPM embed)'; /** @@ -137,10 +139,12 @@ const withFreeraspIosSpmEmbed = (config) => { const snippet = [ '', ` ${FREERASP_SPM_EMBED_TAG}`, - " require Pod::Executable.execute_command('node', ['-p',", - ` 'require.resolve("freerasp-react-native/freerasp_spm.rb", {paths: [process.argv[1]]})',`, - ' __dir__]).strip', - ' freerasp_embed_talsec_spm!(installer)', + " if respond_to?(:spm_dependency, true) && ENV['FREERASP_DISABLE_SPM'] != '1'", + " require Pod::Executable.execute_command('node', ['-p',", + ` 'require.resolve("freerasp-react-native/freerasp_spm.rb", {paths: [process.argv[1]]})',`, + ' __dir__]).strip', + ' freerasp_embed_talsec_spm!(installer)', + ' end', ].join('\n'); const insertAt = anchorIndex + anchor.length; contents = diff --git a/plugin/src/index.ts b/plugin/src/index.ts index b58967c..47f0b12 100644 --- a/plugin/src/index.ts +++ b/plugin/src/index.ts @@ -111,14 +111,16 @@ const withAndroidR8Version: ConfigPlugin = ( }; // --------------------------------------------------------------------------- -// iOS — Swift Package Manager delivery of TalsecRuntime. +// iOS — Swift Package Manager delivery of TalsecRuntime (default on RN >= 0.75). // -// Sets dynamically linked frameworks (required by spm_dependency) and injects the -// TalsecRuntime embed step into the generated Podfile's post_install. +// Sets dynamically linked frameworks (required by spm_dependency) and injects a +// guarded TalsecRuntime embed step into the generated Podfile's post_install. The +// embed is skipped when SPM is unavailable or FREERASP_DISABLE_SPM=1 (vendored +// fallback), matching the podspec's decision logic. // TODO(SPM infra): NOT usable until the dedicated RN-flavour manifest repo + -// GCP-hosted xcframework are ready (see freerasp-react-native.podspec). -// TODO(verify): the Podfile anchor and the full `expo prebuild` flow are unverified -// until the SPM infra lands. +// GCS-hosted xcframework are ready (see freerasp-react-native.podspec). +// TODO(verify): the Podfile anchor, the FREERASP_DISABLE_SPM escape hatch, and the +// full `expo prebuild` flow are unverified until the SPM infra lands. // --------------------------------------------------------------------------- const FREERASP_SPM_EMBED_TAG = '# @generated freerasp-react-native (SPM embed)'; @@ -160,10 +162,12 @@ const withFreeraspIosSpmEmbed: ConfigPlugin = (config) => { const snippet = [ '', ` ${FREERASP_SPM_EMBED_TAG}`, - " require Pod::Executable.execute_command('node', ['-p',", - ` 'require.resolve("freerasp-react-native/freerasp_spm.rb", {paths: [process.argv[1]]})',`, - ' __dir__]).strip', - ' freerasp_embed_talsec_spm!(installer)', + " if respond_to?(:spm_dependency, true) && ENV['FREERASP_DISABLE_SPM'] != '1'", + " require Pod::Executable.execute_command('node', ['-p',", + ` 'require.resolve("freerasp-react-native/freerasp_spm.rb", {paths: [process.argv[1]]})',`, + ' __dir__]).strip', + ' freerasp_embed_talsec_spm!(installer)', + ' end', ].join('\n'); const insertAt = anchorIndex + anchor.length; contents = From 2e502d51cd5cdaa6d2d9288e486cc67286861436 Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Thu, 16 Jul 2026 14:48:34 +0200 Subject: [PATCH 10/13] ci: add vendored fallback gate alongside non-blocking SPM build --- .github/workflows/ci.yml | 35 ++++++++++++++++++++++++++++------- 1 file changed, 28 insertions(+), 7 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 23aceac..1b7f2d9 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,16 +82,12 @@ jobs: yarn build:android build-ios: - # iOS build. TalsecRuntime is delivered via Swift Package Manager (dynamic - # frameworks). This builds green once the real registry link replaces the - # placeholder in freerasp-react-native.podspec. - # Kept non-blocking (continue-on-error) only until the SPM infra lands; drop - # that line to make it a required gate. + # Vendored TalsecRuntime fallback (FREERASP_DISABLE_SPM=1) — the required gate. + # Proves the non-breaking fallback keeps building while the SPM infra is not ready. runs-on: macos-latest needs: build-library - continue-on-error: true env: - USE_FRAMEWORKS: dynamic + FREERASP_DISABLE_SPM: "1" steps: - name: Checkout uses: actions/checkout@v6 @@ -107,3 +103,28 @@ jobs: working-directory: ./example run: | yarn build:ios + + build-ios-spm: + # SPM path (default on RN >= 0.75, dynamic frameworks). Non-blocking until the + # real manifest-repo/GCS link replaces the placeholder in the podspec; drop + # continue-on-error to make it a required gate once the SPM infra is live. + runs-on: macos-latest + needs: build-library + continue-on-error: true + env: + USE_FRAMEWORKS: dynamic + steps: + - name: Checkout + uses: actions/checkout@v6 + + - name: Setup + uses: ./.github/actions/setup + + - name: Install pods (SPM) + working-directory: ./example/ios + run: pod install + + - name: Build example for iOS (SPM) + working-directory: ./example + run: | + yarn build:ios From 16543eeb2a106ec222a796bd755402af83c38b07 Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Thu, 16 Jul 2026 16:10:09 +0200 Subject: [PATCH 11/13] feat: point iOS SPM at Free-RASP-ReactNative-SPM manifest repo (exact 6.14.4) --- .github/workflows/ci.yml | 8 ++------ freerasp-react-native.podspec | 34 ++++++++-------------------------- 2 files changed, 10 insertions(+), 32 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 1b7f2d9..4836ead 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -82,8 +82,7 @@ jobs: yarn build:android build-ios: - # Vendored TalsecRuntime fallback (FREERASP_DISABLE_SPM=1) — the required gate. - # Proves the non-breaking fallback keeps building while the SPM infra is not ready. + # Vendored TalsecRuntime fallback (FREERASP_DISABLE_SPM=1). runs-on: macos-latest needs: build-library env: @@ -105,12 +104,9 @@ jobs: yarn build:ios build-ios-spm: - # SPM path (default on RN >= 0.75, dynamic frameworks). Non-blocking until the - # real manifest-repo/GCS link replaces the placeholder in the podspec; drop - # continue-on-error to make it a required gate once the SPM infra is live. + # SPM path (default, dynamic frameworks). runs-on: macos-latest needs: build-library - continue-on-error: true env: USE_FRAMEWORKS: dynamic steps: diff --git a/freerasp-react-native.podspec b/freerasp-react-native.podspec index 82f0336..2ffc722 100644 --- a/freerasp-react-native.podspec +++ b/freerasp-react-native.podspec @@ -3,26 +3,11 @@ require "json" package = JSON.parse(File.read(File.join(__dir__, "package.json"))) folly_compiler_flags = '-DFOLLY_NO_CONFIG -DFOLLY_MOBILE=1 -DFOLLY_USE_LIBCPP=1 -Wno-comma -Wno-shorten-64-to-32' -# --------------------------------------------------------------------------- -# TalsecRuntime delivery — Swift Package Manager by default, vendored fallback. -# (Same decision logic as react-native-firebase, non-breaking.) -# -# spm_dependency available (RN >= 0.75) AND FREERASP_DISABLE_SPM != '1' -# -> SPM (dedicated RN-flavour manifest repo) -# otherwise (RN < 0.75, or FREERASP_DISABLE_SPM=1) -# -> vendored TalsecRuntime.xcframework -# -# The SPM path requires USE_FRAMEWORKS=dynamic and iOS 13+. The vendored fallback -# keeps working on any RN / linkage, so this is not a breaking change. -# -# TODO(SPM infra): the dedicated RN-flavour manifest repo (which hosts the GCS-backed -# xcframework via binaryTarget) is NOT ready yet, so the values below are PLACEHOLDERS -# — the SPM path won't resolve until it exists; consumers fall back to vendored. -# IMPORTANT: point at the RN-flavour manifest repo — NOT talsec/Free-RASP-iOS -# (that is the native flavour and the wrong artifact for React Native). -# --------------------------------------------------------------------------- -talsec_spm_url = 'https://github.com/talsec/TODO-freerasp-ios-spm' # TODO(SPM infra): real manifest repo git URL -talsec_spm_min_version = '0.0.0' # TODO(SPM infra): real version once the manifest repo is tagged +# TalsecRuntime: SPM by default (RN >= 0.75), vendored xcframework fallback. +# Opt out of SPM with FREERASP_DISABLE_SPM=1. Pin the EXACT TalsecRuntime version this +# library release is built against (each RN version maps to a specific TalsecRuntime). +talsec_spm_url = 'https://github.com/talsec/Free-RASP-ReactNative-SPM' +talsec_spm_version = '6.14.4' Pod::Spec.new do |s| s.name = "freerasp-react-native" @@ -47,15 +32,12 @@ Pod::Spec.new do |s| ] if use_spm - # TalsecRuntime resolved via Swift Package Manager (dedicated RN-flavour manifest repo). - # spm_dependency injects the SPM reference into the Pods project; the framework is - # embedded into the app target by `freerasp_embed_talsec_spm!` (freerasp_spm.rb), - # called from the consumer Podfile post_install. - # TODO(SPM infra): placeholders above — this path is not usable until the infra lands. + # SPM injects the reference into the Pods project; the framework is embedded into the + # app target by freerasp_embed_talsec_spm! (freerasp_spm.rb), called from the Podfile. s.ios.deployment_target = '13.0' spm_dependency(s, url: talsec_spm_url, - requirement: { kind: 'upToNextMajorVersion', minimumVersion: talsec_spm_min_version }, + requirement: { kind: 'exactVersion', version: talsec_spm_version }, products: ['TalsecRuntime'] ) else From f49f4f78a5665ca92da56c41c8b7091c2eef0d14 Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Thu, 16 Jul 2026 16:10:09 +0200 Subject: [PATCH 12/13] fix: make SPM embed helper idempotent to avoid duplicate framework embed --- example/ios/Podfile | 17 +++-------- freerasp_spm.rb | 71 ++++++++++++++++++------------------------- plugin/build/index.js | 26 +++++----------- plugin/src/index.ts | 26 +++++----------- 4 files changed, 50 insertions(+), 90 deletions(-) diff --git a/example/ios/Podfile b/example/ios/Podfile index d3ec9bb..8bce2a4 100644 --- a/example/ios/Podfile +++ b/example/ios/Podfile @@ -33,17 +33,10 @@ target 'FreeraspRNExample' do # :ccache_enabled => true ) - # freeRASP: when TalsecRuntime is delivered via SPM (the default on RN >= 0.75 - # unless FREERASP_DISABLE_SPM=1), embed it into the app target so it ships inside - # the app bundle (otherwise dyld fails at launch). Skipped on the vendored fallback. - # In a real app (freerasp-react-native installed in node_modules) resolve the helper via: - # require Pod::Executable.execute_command('node', ['-p', - # 'require.resolve("freerasp-react-native/freerasp_spm.rb", {paths: [process.argv[1]]})', - # __dir__]).strip - # This example consumes the library from the repo root, so it requires it directly. - if respond_to?(:spm_dependency, true) && ENV['FREERASP_DISABLE_SPM'] != '1' - require_relative '../../freerasp_spm.rb' - freerasp_embed_talsec_spm!(installer) - end + # freeRASP: embed TalsecRuntime into the app target on the SPM path (the helper is a + # no-op / cleanup on the vendored fallback, so it is safe to call unconditionally). + # Real apps resolve the helper from node_modules; this example requires it from the repo root. + require_relative '../../freerasp_spm.rb' + freerasp_embed_talsec_spm!(installer) end end diff --git a/freerasp_spm.rb b/freerasp_spm.rb index f824d5c..7ec91db 100644 --- a/freerasp_spm.rb +++ b/freerasp_spm.rb @@ -1,64 +1,51 @@ -# freeRASP — Swift Package Manager (SPM) opt-in helper. -# -# When the opt-in SPM integration is enabled (FREERASP_USE_SPM=1), the podspec -# resolves TalsecRuntime through the `spm_dependency` helper, which attaches the -# package product to the *pod* target only. With dynamically linked frameworks -# (USE_FRAMEWORKS=dynamic) the app then crashes at launch with: -# -# Library not loaded: @rpath/TalsecRuntime.framework/TalsecRuntime -# -# because nothing embeds the Swift package's binary framework into the app bundle. -# -# Call `freerasp_embed_talsec_spm!(installer)` from your Podfile `post_install` -# (after `react_native_post_install`) to add TalsecRuntime to the application -# target(s) so Xcode embeds and signs it automatically. -# -# Keep the version requirement in sync with `freerasp-react-native.podspec`. -# -# TODO(SPM infra): the `url`/`requirement` defaults below are PLACEHOLDERS. They must -# match `freerasp-react-native.podspec` and point at the dedicated RN-flavour manifest -# repo (NOT talsec/Free-RASP-iOS, which is the native flavour). Not usable until the -# GCP-hosted xcframework + manifest repo infra is ready. +# freeRASP — embeds the SPM-delivered TalsecRuntime into the app target(s). spm_dependency +# only attaches it to the pod target, so dyld fails at launch otherwise. Idempotent and safe +# to call unconditionally: removes any stale reference, re-adds only when SPM is active. +# Keep url/requirement in sync with freerasp-react-native.podspec. def freerasp_embed_talsec_spm!(installer, - url: 'https://github.com/talsec/TODO-freerasp-ios-spm', - requirement: { kind: 'upToNextMajorVersion', minimumVersion: '0.0.0' }, + url: 'https://github.com/talsec/Free-RASP-ReactNative-SPM', + requirement: { kind: 'exactVersion', version: '6.14.4' }, product: 'TalsecRuntime') pkg_class = Xcodeproj::Project::Object::XCRemoteSwiftPackageReference ref_class = Xcodeproj::Project::Object::XCSwiftPackageProductDependency + # Mirror the podspec: SPM active unless unavailable (RN < 0.75) or opted out. + spm_active = respond_to?(:spm_dependency, true) && ENV['FREERASP_DISABLE_SPM'] != '1' + installer.aggregate_targets.each do |aggregate_target| project = aggregate_target.user_project next if project.nil? - # Find or create the package reference in the application project. - pkg = project.root_object.package_references.find do |p| + app_targets = aggregate_target.user_targets.select do |t| + t.respond_to?(:product_type) && t.product_type == 'com.apple.product-type.application' + end + + # Remove any previously-added reference first (avoids a double embed on delivery switch). + app_targets.each do |target| + target.package_product_dependencies.delete_if do |r| + r.class == ref_class && r.product_name == product && + r.package.respond_to?(:repositoryURL) && r.package.repositoryURL == url + end + end + project.root_object.package_references.delete_if do |p| p.class == pkg_class && p.repositoryURL == url end - unless pkg + + if spm_active pkg = project.new(pkg_class) pkg.repositoryURL = url pkg.requirement = requirement project.root_object.package_references << pkg - end - aggregate_target.user_targets.each do |target| - next unless target.respond_to?(:product_type) && - target.product_type == 'com.apple.product-type.application' - - # Add the product dependency to the app target; Xcode embeds & signs - # Swift package framework products of an application target automatically. - existing = target.package_product_dependencies.find do |r| - r.class == ref_class && r.package == pkg && r.product_name == product + app_targets.each do |target| + Pod::UI.puts "[freeRASP][SPM] Embedding #{product} into app target #{target.name}" + dep = project.new(ref_class) + dep.package = pkg + dep.product_name = product + target.package_product_dependencies << dep end - next if existing - - Pod::UI.puts "[freeRASP][SPM] Embedding #{product} into app target #{target.name}" - dep = project.new(ref_class) - dep.package = pkg - dep.product_name = product - target.package_product_dependencies << dep end project.save diff --git a/plugin/build/index.js b/plugin/build/index.js index fa7e836..ace016c 100644 --- a/plugin/build/index.js +++ b/plugin/build/index.js @@ -96,18 +96,10 @@ const withAndroidR8Version = (expoConfig, props) => { return config; }); }; -// --------------------------------------------------------------------------- -// iOS — Swift Package Manager delivery of TalsecRuntime (default on RN >= 0.75). -// -// Sets dynamically linked frameworks (required by spm_dependency) and injects a -// guarded TalsecRuntime embed step into the generated Podfile's post_install. The -// embed is skipped when SPM is unavailable or FREERASP_DISABLE_SPM=1 (vendored -// fallback), matching the podspec's decision logic. -// TODO(SPM infra): NOT usable until the dedicated RN-flavour manifest repo + -// GCS-hosted xcframework are ready (see freerasp-react-native.podspec). -// TODO(verify): the Podfile anchor, the FREERASP_DISABLE_SPM escape hatch, and the -// full `expo prebuild` flow are unverified until the SPM infra lands. -// --------------------------------------------------------------------------- +// iOS — SPM delivery of TalsecRuntime (default on RN >= 0.75). Sets dynamic frameworks +// and injects a guarded TalsecRuntime embed into the Podfile post_install (skipped on the +// vendored fallback / FREERASP_DISABLE_SPM=1). Note: the Expo prebuild flow is less +// battle-tested than bare React Native. const FREERASP_SPM_EMBED_TAG = '# @generated freerasp-react-native (SPM embed)'; /** * Force dynamically linked frameworks — required by `spm_dependency`. @@ -139,12 +131,10 @@ const withFreeraspIosSpmEmbed = (config) => { const snippet = [ '', ` ${FREERASP_SPM_EMBED_TAG}`, - " if respond_to?(:spm_dependency, true) && ENV['FREERASP_DISABLE_SPM'] != '1'", - " require Pod::Executable.execute_command('node', ['-p',", - ` 'require.resolve("freerasp-react-native/freerasp_spm.rb", {paths: [process.argv[1]]})',`, - ' __dir__]).strip', - ' freerasp_embed_talsec_spm!(installer)', - ' end', + " require Pod::Executable.execute_command('node', ['-p',", + ` 'require.resolve("freerasp-react-native/freerasp_spm.rb", {paths: [process.argv[1]]})',`, + ' __dir__]).strip', + ' freerasp_embed_talsec_spm!(installer)', ].join('\n'); const insertAt = anchorIndex + anchor.length; contents = diff --git a/plugin/src/index.ts b/plugin/src/index.ts index 47f0b12..0c9e788 100644 --- a/plugin/src/index.ts +++ b/plugin/src/index.ts @@ -110,18 +110,10 @@ const withAndroidR8Version: ConfigPlugin = ( }); }; -// --------------------------------------------------------------------------- -// iOS — Swift Package Manager delivery of TalsecRuntime (default on RN >= 0.75). -// -// Sets dynamically linked frameworks (required by spm_dependency) and injects a -// guarded TalsecRuntime embed step into the generated Podfile's post_install. The -// embed is skipped when SPM is unavailable or FREERASP_DISABLE_SPM=1 (vendored -// fallback), matching the podspec's decision logic. -// TODO(SPM infra): NOT usable until the dedicated RN-flavour manifest repo + -// GCS-hosted xcframework are ready (see freerasp-react-native.podspec). -// TODO(verify): the Podfile anchor, the FREERASP_DISABLE_SPM escape hatch, and the -// full `expo prebuild` flow are unverified until the SPM infra lands. -// --------------------------------------------------------------------------- +// iOS — SPM delivery of TalsecRuntime (default on RN >= 0.75). Sets dynamic frameworks +// and injects a guarded TalsecRuntime embed into the Podfile post_install (skipped on the +// vendored fallback / FREERASP_DISABLE_SPM=1). Note: the Expo prebuild flow is less +// battle-tested than bare React Native. const FREERASP_SPM_EMBED_TAG = '# @generated freerasp-react-native (SPM embed)'; @@ -162,12 +154,10 @@ const withFreeraspIosSpmEmbed: ConfigPlugin = (config) => { const snippet = [ '', ` ${FREERASP_SPM_EMBED_TAG}`, - " if respond_to?(:spm_dependency, true) && ENV['FREERASP_DISABLE_SPM'] != '1'", - " require Pod::Executable.execute_command('node', ['-p',", - ` 'require.resolve("freerasp-react-native/freerasp_spm.rb", {paths: [process.argv[1]]})',`, - ' __dir__]).strip', - ' freerasp_embed_talsec_spm!(installer)', - ' end', + " require Pod::Executable.execute_command('node', ['-p',", + ` 'require.resolve("freerasp-react-native/freerasp_spm.rb", {paths: [process.argv[1]]})',`, + ' __dir__]).strip', + ' freerasp_embed_talsec_spm!(installer)', ].join('\n'); const insertAt = anchorIndex + anchor.length; contents = From 245a6672b5ce137a1a5c259b1b89d8df42447c21 Mon Sep 17 00:00:00 2001 From: martinzigrai Date: Thu, 16 Jul 2026 16:17:58 +0200 Subject: [PATCH 13/13] fix: resolve Expo plugin eslint errors (no-shadow, avoid namespace imports) --- plugin/build/index.js | 43 ++++++++++--------------------------------- plugin/src/index.ts | 23 ++++++++++------------- 2 files changed, 20 insertions(+), 46 deletions(-) diff --git a/plugin/build/index.js b/plugin/build/index.js index ace016c..1d13d91 100644 --- a/plugin/build/index.js +++ b/plugin/build/index.js @@ -1,31 +1,8 @@ "use strict"; -var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - var desc = Object.getOwnPropertyDescriptor(m, k); - if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) { - desc = { enumerable: true, get: function() { return m[k]; } }; - } - Object.defineProperty(o, k2, desc); -}) : (function(o, m, k, k2) { - if (k2 === undefined) k2 = k; - o[k2] = m[k]; -})); -var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) { - Object.defineProperty(o, "default", { enumerable: true, value: v }); -}) : function(o, v) { - o["default"] = v; -}); -var __importStar = (this && this.__importStar) || function (mod) { - if (mod && mod.__esModule) return mod; - var result = {}; - if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k); - __setModuleDefault(result, mod); - return result; -}; Object.defineProperty(exports, "__esModule", { value: true }); const config_plugins_1 = require("@expo/config-plugins"); -const fs = __importStar(require("fs")); -const path = __importStar(require("path")); +const fs_1 = require("fs"); +const path_1 = require("path"); const { createBuildGradlePropsConfigPlugin } = config_plugins_1.AndroidConfig.BuildProperties; const urlFreerasp = 'https://europe-west3-maven.pkg.dev/talsec-artifact-repository/freerasp'; const urlJitpack = 'https://www.jitpack.io'; @@ -105,9 +82,9 @@ const FREERASP_SPM_EMBED_TAG = '# @generated freerasp-react-native (SPM embed)'; * Force dynamically linked frameworks — required by `spm_dependency`. */ const withFreeraspIosDynamicFrameworks = (config) => { - return (0, config_plugins_1.withPodfileProperties)(config, (config) => { - config.modResults['ios.useFrameworks'] = 'dynamic'; - return config; + return (0, config_plugins_1.withPodfileProperties)(config, (cfg) => { + cfg.modResults['ios.useFrameworks'] = 'dynamic'; + return cfg; }); }; /** @@ -117,9 +94,9 @@ const withFreeraspIosDynamicFrameworks = (config) => { const withFreeraspIosSpmEmbed = (config) => { return (0, config_plugins_1.withDangerousMod)(config, [ 'ios', - (config) => { - const podfilePath = path.join(config.modRequest.platformProjectRoot, 'Podfile'); - let contents = fs.readFileSync(podfilePath, 'utf-8'); + (cfg) => { + const podfilePath = (0, path_1.join)(cfg.modRequest.platformProjectRoot, 'Podfile'); + let contents = (0, fs_1.readFileSync)(podfilePath, 'utf-8'); if (!contents.includes(FREERASP_SPM_EMBED_TAG)) { const anchor = 'post_install do |installer|'; const anchorIndex = contents.indexOf(anchor); @@ -139,10 +116,10 @@ const withFreeraspIosSpmEmbed = (config) => { const insertAt = anchorIndex + anchor.length; contents = contents.slice(0, insertAt) + snippet + contents.slice(insertAt); - fs.writeFileSync(podfilePath, contents); + (0, fs_1.writeFileSync)(podfilePath, contents); } } - return config; + return cfg; }, ]); }; diff --git a/plugin/src/index.ts b/plugin/src/index.ts index 0c9e788..8025fd9 100644 --- a/plugin/src/index.ts +++ b/plugin/src/index.ts @@ -8,8 +8,8 @@ import { type ConfigPlugin, } from '@expo/config-plugins'; import { type ExpoConfig } from '@expo/config-types'; -import * as fs from 'fs'; -import * as path from 'path'; +import { readFileSync, writeFileSync } from 'fs'; +import { join } from 'path'; import { type PluginConfigType } from './pluginConfig'; const { createBuildGradlePropsConfigPlugin } = AndroidConfig.BuildProperties; @@ -121,9 +121,9 @@ const FREERASP_SPM_EMBED_TAG = '# @generated freerasp-react-native (SPM embed)'; * Force dynamically linked frameworks — required by `spm_dependency`. */ const withFreeraspIosDynamicFrameworks: ConfigPlugin = (config) => { - return withPodfileProperties(config, (config) => { - config.modResults['ios.useFrameworks'] = 'dynamic'; - return config; + return withPodfileProperties(config, (cfg) => { + cfg.modResults['ios.useFrameworks'] = 'dynamic'; + return cfg; }); }; @@ -134,12 +134,9 @@ const withFreeraspIosDynamicFrameworks: ConfigPlugin = (config) => { const withFreeraspIosSpmEmbed: ConfigPlugin = (config) => { return withDangerousMod(config, [ 'ios', - (config) => { - const podfilePath = path.join( - config.modRequest.platformProjectRoot, - 'Podfile' - ); - let contents = fs.readFileSync(podfilePath, 'utf-8'); + (cfg) => { + const podfilePath = join(cfg.modRequest.platformProjectRoot, 'Podfile'); + let contents = readFileSync(podfilePath, 'utf-8'); if (!contents.includes(FREERASP_SPM_EMBED_TAG)) { const anchor = 'post_install do |installer|'; @@ -162,10 +159,10 @@ const withFreeraspIosSpmEmbed: ConfigPlugin = (config) => { const insertAt = anchorIndex + anchor.length; contents = contents.slice(0, insertAt) + snippet + contents.slice(insertAt); - fs.writeFileSync(podfilePath, contents); + writeFileSync(podfilePath, contents); } } - return config; + return cfg; }, ]); };