Skip to content

fix: load SQLCipher.framework explicitly on iOS/macOS#381

Merged
simolus3 merged 2 commits intopowersync-ja:mainfrom
qtpi-bonding:fix-ios-sqlcipher-extension-loading
Mar 18, 2026
Merged

fix: load SQLCipher.framework explicitly on iOS/macOS#381
simolus3 merged 2 commits intopowersync-ja:mainfrom
qtpi-bonding:fix-ios-sqlcipher-extension-loading

Conversation

@qtpi-bonding
Copy link
Copy Markdown
Contributor

Problem

On iOS (and macOS), PowerSyncSQLCipherOpenFactory fails with:

SqliteException(21): Could not load extension

This happens in PowerSyncOpenFactory.enableExtension() when it calls
sqlite3.ensureExtensionLoaded(), which internally calls sqlite3_auto_extension() and
gets SQLITE_MISUSE (21) back.

Root cause: The sqlite3 Dart package's _defaultOpen() on iOS tries
sqlite3.framework/sqlite3 and CSQLite.framework/CSQLite first (for
sqlite3_flutter_libs). Neither exists when using sqlcipher_flutter_libs, so it falls
back to DynamicLibrary.process() (RTLD_DEFAULT). RTLD_DEFAULT resolves to Apple's
system sqlite3 before SQLCipher, and Apple's system sqlite3 has extension loading
disabled — so sqlite3_auto_extension returns SQLITE_MISUSE.

Fix

The Android override already handles this correctly by explicitly loading
libsqlcipher.so via openCipherOnAndroid(). This PR adds the equivalent for iOS and
macOS: explicitly load SQLCipher.framework/SQLCipher by path, bypassing RTLD_DEFAULT
entirely.

Testing

Verified on a physical iOS device (iPhone). Before this fix the app crashed
immediately on launch. After this fix SQLCipher initialises correctly and PRAGMA
cipher_version returns a result.

DynamicLibrary.process() (RTLD_DEFAULT) on iOS resolves to Apple's
system sqlite3 before SQLCipher. Apple's sqlite3 has extension loading
disabled and returns SQLITE_MISUSE (21) from sqlite3_auto_extension,
causing PowerSync's extension registration to fail with:

  SqliteException(21): Could not load extension

The Android override already uses openCipherOnAndroid() to explicitly
load libsqlcipher.so. This commit adds the equivalent for iOS/macOS:
explicitly load SQLCipher.framework/SQLCipher by path so the sqlite3
package uses SQLCipher rather than the system library.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
simolus3
simolus3 previously approved these changes Mar 16, 2026
Copy link
Copy Markdown
Contributor

@simolus3 simolus3 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It's a bit odd that these changes are necessary since the native plugin should link and load SQLCipher already, but I ran integration tests with and without this change and thing worked.

So in case some build setups make this required, these changes look good to me.

Co-authored-by: Simon Binder <oss@simonbinder.eu>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants