Skip to content

Commit cb5e52a

Browse files
fix(ios/spm): link FronteggSwift into the app target so the binary resolves its symbols
Under use_frameworks! :static, FronteggRN builds as a static framework and does not embed FronteggSwift's objects, so the application binary linked with unresolved FronteggSwift.* symbols (undefined symbols for arm64). frontegg_spm.rb previously *removed* the app-target SPM link assuming FronteggRN provides it; instead link the FronteggSwift SPM product directly into the application target (packageProductDependencies + Frameworks build file), located generically via the product-type.application target. Idempotent; result is plutil-lint valid. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
1 parent 284ad35 commit cb5e52a

1 file changed

Lines changed: 64 additions & 18 deletions

File tree

ios/frontegg_spm.rb

Lines changed: 64 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ module FronteggSPM
1414
FRONTEGG_RN_FRAMEWORKS_PHASE_ID = '46EB2E000076A0'
1515
SPM_TARGET_DEP_ID = '7A1B2C3D4E5F6000100140001'
1616
SPM_FRAMEWORK_BUILD_ID = '7A1B2C3D4E5F6000100140002'
17+
APP_SPM_FRAMEWORK_BUILD_ID = '7A1B2C3D4E5F6000100140003'
1718

1819
module_function
1920

@@ -24,7 +25,7 @@ def link_frontegg_rn_pod(installer)
2425
patch_app_pods_xcconfigs(installer.sandbox.root)
2526

2627
app_pbxproj = user_app_pbxproj_path(installer)
27-
dedupe_app_frontegg_spm_link(app_pbxproj) if app_pbxproj
28+
link_frontegg_spm_into_app(app_pbxproj) if app_pbxproj
2829
end
2930

3031
def user_app_pbxproj_path(installer)
@@ -53,26 +54,34 @@ def patch_app_pods_xcconfigs(pods_root)
5354
end
5455
end
5556

56-
# App Swift files import FronteggSwift but must not link it (FronteggRN already does).
57-
# Keep package resolution on the app project; drop target-level package products only.
58-
def dedupe_app_frontegg_spm_link(app_pbxproj_path)
57+
# The app's Swift files (and the FronteggRN pod) reference FronteggSwift symbols, so the
58+
# application binary must LINK the FronteggSwift SPM product. Under use_frameworks! :static,
59+
# FronteggRN builds as a static framework and does NOT embed FronteggSwift's objects, so the
60+
# link has to live on the application target itself — otherwise the final link fails with
61+
# "Undefined symbols for architecture arm64: ... FronteggSwift.*".
62+
def link_frontegg_spm_into_app(app_pbxproj_path)
5963
return unless File.exist?(app_pbxproj_path)
6064

6165
content = File.read(app_pbxproj_path)
6266
content = content.sub(%r{^// FRONTEGG_SPM_DEDUPED_MARKER\n}, '')
6367

64-
content = content.gsub(
65-
/\n\t\t\tpackageProductDependencies = \(\n\t\t\t\t#{PRODUCT_DEP_ID} \/\* #{PRODUCT_NAME} \*\/,\n\t\t\t\);/,
66-
''
67-
)
68+
content = ensure_app_package_reference(content)
69+
content = add_app_target_product_dependency(content)
70+
content = add_app_target_framework_build_file(content)
6871

69-
unless content.include?('XCRemoteSwiftPackageReference "frontegg-ios-swift.git"')
70-
content = content.sub(
71-
/(mainGroup = [A-F0-9]+;\n)(\t\t\tproductRefGroup)/,
72-
"\\1\t\t\tpackageReferences = (\n\t\t\t\t#{PACKAGE_REF_ID} /* XCRemoteSwiftPackageReference \"frontegg-ios-swift.git\" */,\n\t\t\t);\n\t\t\t\\2"
73-
)
72+
File.write(app_pbxproj_path, content)
73+
end
74+
75+
# Project-level Swift package resolution (XCRemoteSwiftPackageReference + product dependency).
76+
def ensure_app_package_reference(content)
77+
return content if content.include?('XCRemoteSwiftPackageReference "frontegg-ios-swift.git"')
7478

75-
package_sections = <<~PBX
79+
content = content.sub(
80+
/(mainGroup = [A-F0-9]+;\n)(\t\t\tproductRefGroup)/,
81+
"\\1\t\t\tpackageReferences = (\n\t\t\t\t#{PACKAGE_REF_ID} /* XCRemoteSwiftPackageReference \"frontegg-ios-swift.git\" */,\n\t\t\t);\n\t\t\t\\2"
82+
)
83+
84+
package_sections = <<~PBX
7685
7786
/* Begin XCRemoteSwiftPackageReference section */
7887
\t\t#{PACKAGE_REF_ID} /* XCRemoteSwiftPackageReference "frontegg-ios-swift.git" */ = {
@@ -92,12 +101,49 @@ def dedupe_app_frontegg_spm_link(app_pbxproj_path)
92101
\t\t\tproductName = #{PRODUCT_NAME};
93102
\t\t};
94103
/* End XCSwiftPackageProductDependency section */
95-
PBX
104+
PBX
96105

97-
content.sub!(/\n\t\};\n\trootObject = /, "#{package_sections}\n\t};\n\trootObject = ")
98-
end
106+
content.sub(/\n\t\};\n\trootObject = /, "#{package_sections}\n\t};\n\trootObject = ")
107+
end
99108

100-
File.write(app_pbxproj_path, content)
109+
# Attach the FronteggSwift product to the application target so Xcode links it into the app binary.
110+
def add_app_target_product_dependency(content)
111+
return content if content.match?(
112+
/packageProductDependencies = \(\n\t\t\t\t#{PRODUCT_DEP_ID} \/\* #{PRODUCT_NAME} \*\/,\n\t\t\t\);\n\t\t\tproductType = "com\.apple\.product-type\.application";/
113+
)
114+
115+
content.sub(
116+
/(\n\t\t\tproductReference = [A-F0-9]+ \/\* [^*]+\.app \*\/;\n)(\t\t\tproductType = "com\.apple\.product-type\.application";)/,
117+
"\\1\t\t\tpackageProductDependencies = (\n\t\t\t\t#{PRODUCT_DEP_ID} /* #{PRODUCT_NAME} */,\n\t\t\t);\n\\2"
118+
)
119+
end
120+
121+
# Add FronteggSwift to the application target's Frameworks (link binary) build phase.
122+
def add_app_target_framework_build_file(content)
123+
return content if content.include?("#{APP_SPM_FRAMEWORK_BUILD_ID} /* #{PRODUCT_NAME} in Frameworks */")
124+
125+
phase_id = application_target_frameworks_phase_id(content)
126+
return content unless phase_id
127+
128+
build_file = "\t\t#{APP_SPM_FRAMEWORK_BUILD_ID} /* #{PRODUCT_NAME} in Frameworks */ = {isa = PBXBuildFile; productRef = #{PRODUCT_DEP_ID} /* #{PRODUCT_NAME} */; };\n"
129+
content = content.sub('/* Begin PBXBuildFile section */', "/* Begin PBXBuildFile section */\n#{build_file}")
130+
131+
content.sub(
132+
/(#{Regexp.escape(phase_id)} \/\* Frameworks \*\/ = \{[\s\S]*?files = \(\n)/m,
133+
"\\1\t\t\t\t#{APP_SPM_FRAMEWORK_BUILD_ID} /* #{PRODUCT_NAME} in Frameworks */,\n"
134+
)
135+
end
136+
137+
# The id of the Frameworks (link binary) build phase that belongs to the application target.
138+
def application_target_frameworks_phase_id(content)
139+
content.scan(/\t\t[A-F0-9]+ \/\* [^*]+ \*\/ = \{\n\t\t\tisa = PBXNativeTarget;[\s\S]*?\n\t\t\};/) do |block|
140+
next unless block.include?('productType = "com.apple.product-type.application";')
141+
phases = block[/buildPhases = \(\n([\s\S]*?)\t\t\t\);/, 1]
142+
next unless phases
143+
fid = phases[/([A-F0-9]+) \/\* Frameworks \*\//, 1]
144+
return fid if fid
145+
end
146+
nil
101147
end
102148

103149
def patch_frontegg_rn_xcconfigs(pods_root)

0 commit comments

Comments
 (0)