Skip to content

Commit 5cceb96

Browse files
authored
fix: SPM build issues (#845)
1 parent 44d65b4 commit 5cceb96

6 files changed

Lines changed: 142 additions & 62 deletions

File tree

example/ios/Podfile

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -59,14 +59,14 @@ target 'QuickCryptoExample' do
5959
end
6060
end
6161

62-
# Embed OpenSSL.framework from SPM into the app bundle
62+
# Embed SPM frameworks from QuickCrypto into the app bundle
6363
# SPM frameworks added to Pods project need manual embedding
6464
main_project_path = File.join(installer.sandbox.root.parent, 'QuickCryptoExample.xcodeproj')
6565
main_project = Xcodeproj::Project.open(main_project_path)
6666
app_target = main_project.targets.find { |t| t.name == 'QuickCryptoExample' }
6767

6868
if app_target
69-
embed_phase_name = 'Embed SPM Frameworks (OpenSSL)'
69+
embed_phase_name = 'Embed SPM Frameworks (QuickCrypto)'
7070
existing_phase = app_target.shell_script_build_phases.find { |p| p.name == embed_phase_name }
7171

7272
unless existing_phase
@@ -78,10 +78,8 @@ target 'QuickCryptoExample' do
7878
7979
if [ -d "$OPENSSL_FRAMEWORK" ]; then
8080
echo "Found OpenSSL.framework at $OPENSSL_FRAMEWORK"
81-
mkdir -p "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}"
8281
rsync -av --delete "$OPENSSL_FRAMEWORK" "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/"
8382
84-
# Code sign if required
8583
if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" ] && [ "${CODE_SIGNING_REQUIRED:-}" != "NO" ]; then
8684
/usr/bin/codesign --force --sign "${EXPANDED_CODE_SIGN_IDENTITY}" --preserve-metadata=identifier,entitlements "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework"
8785
fi
@@ -98,7 +96,7 @@ target 'QuickCryptoExample' do
9896
end
9997

10098
main_project.save
101-
Pod::UI.puts "[QuickCrypto] Added 'Embed SPM Frameworks (OpenSSL)' build phase"
99+
Pod::UI.puts "[QuickCrypto] Added 'Embed SPM Frameworks (QuickCrypto)' build phase"
102100
end
103101
end
104102
end

example/ios/Podfile.lock

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2773,7 +2773,7 @@ SPEC CHECKSUMS:
27732773
glog: 5683914934d5b6e4240e497e0f4a3b42d1854183
27742774
hermes-engine: 4f8246b1f6d79f625e0d99472d1f3a71da4d28ca
27752775
NitroModules: 1715fe0e22defd9e2cdd48fb5e0dbfd01af54bec
2776-
QuickCrypto: 389afd05e4f7d62ec5e2be215aeaf0dd4745b5e1
2776+
QuickCrypto: 2f6d1588cda78b7b4daf5213c5fad3437d99616f
27772777
RCT-Folly: 846fda9475e61ec7bcbf8a3fe81edfcaeb090669
27782778
RCTDeprecation: c4b9e2fd0ab200e3af72b013ed6113187c607077
27792779
RCTRequired: e97dd5dafc1db8094e63bc5031e0371f092ae92a
@@ -2845,6 +2845,6 @@ SPEC CHECKSUMS:
28452845
SocketRocket: d4aabe649be1e368d1318fdf28a022d714d65748
28462846
Yoga: 11c9686a21e2cd82a094a723649d9f4507200fb0
28472847

2848-
PODFILE CHECKSUM: bc958092bb9060694d04c6fcf716262b0549cded
2848+
PODFILE CHECKSUM: a55db5270505ab83586da7672ed603a4ad498447
28492849

28502850
COCOAPODS: 1.15.2

example/ios/QuickCryptoExample.xcodeproj/project.pbxproj

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,6 +115,7 @@
115115
13B07F8E1A680F5B00A75B9A /* Resources */,
116116
00DD1BFF1BD5951E006B06BC /* Bundle React Native code and images */,
117117
8C63FDE6D14F20AAC744357A /* Embed SPM Frameworks (OpenSSL) */,
118+
F008C3222B5CD8109E252C6E /* Embed SPM Frameworks (QuickCrypto) */,
118119
00EEFC60759A1932668264C0 /* [CP] Embed Pods Frameworks */,
119120
E235C05ADACE081382539298 /* [CP] Copy Pods Resources */,
120121
);
@@ -262,6 +263,24 @@
262263
shellScript = "\"${PODS_ROOT}/Target Support Files/Pods-QuickCryptoExample/Pods-QuickCryptoExample-resources.sh\"\n";
263264
showEnvVarsInLog = 0;
264265
};
266+
F008C3222B5CD8109E252C6E /* Embed SPM Frameworks (QuickCrypto) */ = {
267+
isa = PBXShellScriptBuildPhase;
268+
buildActionMask = 2147483647;
269+
files = (
270+
);
271+
inputFileListPaths = (
272+
);
273+
inputPaths = (
274+
);
275+
name = "Embed SPM Frameworks (QuickCrypto)";
276+
outputFileListPaths = (
277+
);
278+
outputPaths = (
279+
);
280+
runOnlyForDeploymentPostprocessing = 0;
281+
shellPath = /bin/sh;
282+
shellScript = "# Embed SPM frameworks (OpenSSL, Clibsodium) from QuickCrypto into app bundle\n# SPM builds frameworks to BUILT_PRODUCTS_DIR but doesn't embed them automatically\n\nmkdir -p \"${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}\"\n\n# Embed OpenSSL.framework (always required for ML-DSA)\nOPENSSL_FRAMEWORK=\"${BUILT_PRODUCTS_DIR}/OpenSSL.framework\"\nif [ -d \"$OPENSSL_FRAMEWORK\" ]; then\n echo \"Found OpenSSL.framework at $OPENSSL_FRAMEWORK\"\n rsync -av --delete \"$OPENSSL_FRAMEWORK\" \"${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/\"\n\n if [ -n \"${EXPANDED_CODE_SIGN_IDENTITY:-}\" ] && [ \"${CODE_SIGNING_REQUIRED:-}\" != \"NO\" ]; then\n /usr/bin/codesign --force --sign \"${EXPANDED_CODE_SIGN_IDENTITY}\" --preserve-metadata=identifier,entitlements \"${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework\"\n fi\n echo \"OpenSSL.framework embedded successfully\"\nelse\n echo \"warning: OpenSSL.framework not found at $OPENSSL_FRAMEWORK\"\nfi\n\n# Embed Clibsodium.framework (optional, when SODIUM_ENABLED=1)\nCLIBSODIUM_FRAMEWORK=\"${BUILT_PRODUCTS_DIR}/Clibsodium.framework\"\nif [ -d \"$CLIBSODIUM_FRAMEWORK\" ]; then\n echo \"Found Clibsodium.framework at $CLIBSODIUM_FRAMEWORK\"\n rsync -av --delete \"$CLIBSODIUM_FRAMEWORK\" \"${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/\"\n\n if [ -n \"${EXPANDED_CODE_SIGN_IDENTITY:-}\" ] && [ \"${CODE_SIGNING_REQUIRED:-}\" != \"NO\" ]; then\n /usr/bin/codesign --force --sign \"${EXPANDED_CODE_SIGN_IDENTITY}\" --preserve-metadata=identifier,entitlements \"${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Clibsodium.framework\"\n fi\n echo \"Clibsodium.framework embedded successfully\"\nfi\n";
283+
};
265284
/* End PBXShellScriptBuildPhase section */
266285

267286
/* Begin PBXSourcesBuildPhase section */

example/ios/QuickCryptoExample.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist

Lines changed: 0 additions & 8 deletions
This file was deleted.
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
# iOS Setup Guide
2+
3+
## SPM Framework Embedding Required
4+
5+
QuickCrypto uses Swift Package Manager (SPM) dependencies that must be manually embedded in your app bundle:
6+
7+
- **OpenSSL 3.6+** (required) - For ML-DSA post-quantum cryptography support
8+
- **Sodium** (optional) - For XSalsa20 cipher support via libsodium (when `SODIUM_ENABLED=1`)
9+
10+
### Why is this needed?
11+
12+
CocoaPods doesn't automatically embed SPM frameworks into the final app bundle. Without this configuration, you'll encounter runtime errors:
13+
14+
```
15+
dyld: Library not loaded: @rpath/OpenSSL.framework/OpenSSL
16+
```
17+
18+
This is a temporary limitation of mixing CocoaPods + SPM. It will be resolved when React Native fully migrates to SPM (expected 2026).
19+
20+
## Configuration
21+
22+
Add the following to your `ios/Podfile` inside the `post_install` hook:
23+
24+
```ruby
25+
post_install do |installer|
26+
# ... your existing post_install code (react_native_post_install, etc.) ...
27+
28+
# Embed SPM frameworks from QuickCrypto
29+
main_project_path = File.join(installer.sandbox.root.parent, 'YourAppName.xcodeproj')
30+
main_project = Xcodeproj::Project.open(main_project_path)
31+
app_target = main_project.targets.find { |t| t.name == 'YourAppName' }
32+
33+
if app_target
34+
embed_phase_name = 'Embed SPM Frameworks (QuickCrypto)'
35+
existing_phase = app_target.shell_script_build_phases.find { |p| p.name == embed_phase_name }
36+
37+
unless existing_phase
38+
phase = app_target.new_shell_script_build_phase(embed_phase_name)
39+
phase.shell_script = <<~SCRIPT
40+
mkdir -p "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}"
41+
42+
# Embed OpenSSL.framework (required for ML-DSA)
43+
if [ -d "${BUILT_PRODUCTS_DIR}/OpenSSL.framework" ]; then
44+
rsync -av --delete "${BUILT_PRODUCTS_DIR}/OpenSSL.framework" "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/"
45+
if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" ]; then
46+
/usr/bin/codesign --force --sign "${EXPANDED_CODE_SIGN_IDENTITY}" --preserve-metadata=identifier,entitlements "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/OpenSSL.framework"
47+
fi
48+
fi
49+
50+
# Embed Sodium.framework (optional, if SODIUM_ENABLED=1)
51+
if [ -d "${BUILT_PRODUCTS_DIR}/Sodium.framework" ]; then
52+
rsync -av --delete "${BUILT_PRODUCTS_DIR}/Sodium.framework" "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/"
53+
if [ -n "${EXPANDED_CODE_SIGN_IDENTITY:-}" ]; then
54+
/usr/bin/codesign --force --sign "${EXPANDED_CODE_SIGN_IDENTITY}" --preserve-metadata=identifier,entitlements "${BUILT_PRODUCTS_DIR}/${FRAMEWORKS_FOLDER_PATH}/Sodium.framework"
55+
fi
56+
fi
57+
SCRIPT
58+
59+
# Insert before the CocoaPods embed frameworks phase
60+
embed_pods_phase = app_target.shell_script_build_phases.find { |p| p.name == '[CP] Embed Pods Frameworks' }
61+
if embed_pods_phase
62+
app_target.build_phases.move(phase, app_target.build_phases.index(embed_pods_phase))
63+
end
64+
65+
main_project.save
66+
end
67+
end
68+
end
69+
```
70+
71+
**Important:** Replace `YourAppName` with your actual Xcode target name (usually matches your app name).
72+
73+
## Example
74+
75+
See the [example app's Podfile](../../example/ios/Podfile) for a complete working reference.
76+
77+
## Enabling libsodium (Optional)
78+
79+
To enable XSalsa20 cipher support, set the environment variable before installing pods:
80+
81+
```ruby
82+
# At the top of your Podfile
83+
ENV['SODIUM_ENABLED'] = '1'
84+
```
85+
86+
Then run:
87+
88+
```bash
89+
cd ios && pod install
90+
```
91+
92+
## Troubleshooting
93+
94+
### Error: "Library not loaded: @rpath/OpenSSL.framework/OpenSSL"
95+
96+
This means the SPM frameworks aren't being embedded. Verify:
97+
98+
1. The `post_install` hook is properly configured in your Podfile
99+
2. You're using `use_frameworks! :linkage => :dynamic` (required for SPM dependencies)
100+
3. Run `cd ios && pod install` after modifying the Podfile
101+
4. Clean build folder in Xcode (Cmd+Shift+K) and rebuild
102+
103+
### Dynamic Frameworks Required
104+
105+
QuickCrypto requires dynamic framework linking due to SPM dependencies. Add this to your Podfile:
106+
107+
```ruby
108+
use_frameworks! :linkage => :dynamic
109+
```
110+
111+
## Future
112+
113+
When React Native completes its migration to Swift Package Manager (expected 2026), this manual embedding step will no longer be necessary. SPM packages will be properly integrated by default.

packages/react-native-quick-crypto/QuickCrypto.podspec

Lines changed: 5 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -23,63 +23,21 @@ Pod::Spec.new do |s|
2323
Pod::UI.puts("[QuickCrypto] 🧂 has libsodium #{sodium_enabled ? "enabled" : "disabled"}!")
2424

2525
if sodium_enabled
26-
# cocoapod for Sodium has not been updated for a while, so we need to build it ourselves
27-
# https://github.com/jedisct1/swift-sodium/issues/264#issuecomment-2864963850
26+
# Build libsodium from source for XSalsa20 cipher support
27+
# CocoaPods packages are outdated (1.0.12) and SPM causes module conflicts
2828
s.prepare_command = <<-CMD
29-
set -e # Exit on any error
30-
set -x # Print commands as they execute
31-
32-
# Create ios directory if it doesn't exist
29+
set -e
3330
mkdir -p ios
34-
35-
# Download libsodium with verbose output
36-
echo "Downloading libsodium..."
37-
curl -L -v -o ios/libsodium.tar.gz https://download.libsodium.org/libsodium/releases/libsodium-1.0.20-stable.tar.gz
38-
39-
# Verify download
40-
if [ ! -f ios/libsodium.tar.gz ]; then
41-
echo "ERROR: Failed to download libsodium.tar.gz"
42-
exit 1
43-
fi
44-
45-
echo "Download size: $(wc -c < ios/libsodium.tar.gz) bytes"
46-
47-
# Clean previous extraction
48-
rm -rf ios/libsodium-stable
49-
50-
# Extract the full tarball
51-
echo "Extracting libsodium..."
31+
curl -L -o ios/libsodium.tar.gz https://download.libsodium.org/libsodium/releases/libsodium-1.0.20-stable.tar.gz
5232
tar -xzf ios/libsodium.tar.gz -C ios
53-
54-
# Verify extraction
55-
if [ ! -d ios/libsodium-stable ]; then
56-
echo "ERROR: Failed to extract libsodium"
57-
exit 1
58-
fi
59-
60-
# Run configure and make to generate all headers including private ones
61-
echo "Configuring libsodium..."
6233
cd ios/libsodium-stable
6334
./configure --disable-shared --enable-static
64-
65-
echo "Building libsodium..."
6635
make -j$(sysctl -n hw.ncpu)
67-
68-
# Verify build success
69-
if [ ! -f src/libsodium/.libs/libsodium.a ]; then
70-
echo "ERROR: libsodium build failed - static library not found"
71-
exit 1
72-
fi
73-
74-
echo "libsodium build completed successfully"
75-
76-
# Cleanup
7736
cd ../../
7837
rm -f ios/libsodium.tar.gz
7938
CMD
8039
else
8140
s.prepare_command = <<-CMD
82-
# Clean up libsodium files if they exist
8341
rm -rf ios/libsodium-stable
8442
rm -f ios/libsodium.tar.gz
8543
CMD
@@ -164,7 +122,7 @@ Pod::Spec.new do |s|
164122
"\"$(PODS_ROOT)/../../packages/react-native-quick-crypto/ios/libsodium-stable/src/libsodium/include/sodium\""
165123
]
166124
xcconfig["HEADER_SEARCH_PATHS"] = (cpp_headers + sodium_headers).join(' ')
167-
xcconfig["GCC_PREPROCESSOR_DEFINITIONS"] = "$(inherited) BLSALLOC_SODIUM=1"
125+
xcconfig["GCC_PREPROCESSOR_DEFINITIONS"] = "$(inherited) FOLLY_NO_CONFIG FOLLY_CFG_NO_COROUTINES BLSALLOC_SODIUM=1"
168126
else
169127
xcconfig["HEADER_SEARCH_PATHS"] = cpp_headers.join(' ')
170128
end

0 commit comments

Comments
 (0)