Add Swift Package Manager support for iOS and macOS (4.0.0)#120
Add Swift Package Manager support for iOS and macOS (4.0.0)#120mtallenca wants to merge 4 commits into
Conversation
- Add Package.swift for ios and macos: SQLCipher via the official SQLCipher.swift package (CommonCrypto, same as the pod), FMDB vendored, SQLITE_HAS_CODEC defined. - Move native sources into Sources/sqflite_sqlcipher and update the podspecs; CocoaPods remains supported. - Rename package directory sqflite/ -> sqflite_sqlcipher/ so the Swift Package Manager package identity matches the package name. - Require Flutter 3.44 (for the FlutterFramework SPM dependency); raise the iOS deployment target to 12.0. - Regenerate the example ios/macos runner projects.
Verifies that an encrypted database created by the published 3.4.0 build (CocoaPods SQLCipher pod) and by the local 4.0.0 build (Swift Package Manager -> SQLCipher.swift) is mutually readable with the same key, on both macOS and the iOS Simulator. - shared/xtest_scenario.dart: one create/verify scenario compiled against both package versions; covers INTEGER/REAL/TEXT/BLOB/NULL + a unicode string, asserts wrong-key rejection, and checks the file is not plaintext. - app_v340 (sqflite_sqlcipher 3.4.0 from pub.dev) and app_v400 (local path, built via SPM) test apps share the scenario. - run_macos.sh shares the db through /tmp (macOS app sandbox disabled). - run_ios.sh exports the db as base64 over stdout and re-injects it as a bundled asset, since `flutter test` wipes the iOS sandbox on exit. All four writer/reader directions (pod->pod, pod->spm, spm->pod, spm->spm) pass on macOS and iOS.
The Swift Package Manager build depended on the prebuilt SQLCipher.swift *dynamic* xcframework. Dynamic linkage leaves the sqlite3_* symbols in a dylib where dyld can resolve them to the system /usr/lib/libsqlite3.dylib instead of SQLCipher, silently dropping encryption. Compile the SQLCipher 4.10.0 amalgamation from source as a static C target instead. Static linkage makes sqlite3_* defined symbols in the app binary, which can never be shadowed by the system lib -- the exact property the old CocoaPods static-pod path relied on. - Vendor the 4.10.0 amalgamation (byte-identical to the CocoaPods SQLCipher pod source) as a new Sources/SQLCipher C target, with an include/SQLCipher/ header layout + module map so the existing <SQLCipher/SQLCipher.h> imports resolve unchanged. - Both Package.swift: drop the SQLCipher.swift dependency, add the static SQLCipher target with the CocoaPods pod's exact build flags (CommonCrypto backend, SQLITE_HAS_CODEC, etc.) so on-disk databases stay byte-compatible across the SPM and CocoaPods builds. - Define NDEBUG on the target: SwiftPM compiles C targets with -fmodules, under which SQLite's internal auto-NDEBUG fails to reach the assert sites in debug builds, leaving SQLITE_DEBUG-only helpers undeclared. NDEBUG is the state every SQLCipher distribution (incl. the pod) ships with. - Update the cross-version test harness assertions to verify the static source build (sqlite3.o compiled, no dynamic SQLCipher framework) instead of grepping for SQLCipher.swift; ignore SwiftPM .build/ artifacts. Verified end to end on macOS and iOS: cross_version_test passes both directions (pod 3.4.0 <-> SPM 4.0.0 database interchange), and sqlite3_key is a defined symbol in the app binary with no SQLCipher/libsqlite3 dylib dependency.
Regenerate the SPM static C target's amalgamation from the official sqlcipher v4.16.0 source (SQLite 3.53.1), up from 4.10.0 (SQLite 3.50.4). The CocoaPods `SQLCipher` pod tops out at 4.10.0, so the podspec stays there; the source-built SPM target is not bound to the pod's release cadence and now tracks the latest SQLCipher. SQLCipher keeps a stable on-disk format across 4.x with the CommonCrypto backend, so databases written by the SPM build (4.16.0) and the CocoaPods build (4.10.0) interchange in both directions. Verified end to end on macOS and iOS: a fresh `swift run` probe reports `cipher_version: 4.16.0 community`, and cross_version_test passes both directions (pod 4.10.0 <-> SPM 4.16.0 database interchange), with wrong-key rejection.
|
Thank you! |
|
I initially had the same thought. My app was also using an spm version of flutter_downloader. That package brings in sqlite3 library and causes name conflicts and xcode was choosing sqlite. I needed to either use a fork of it replacing sqflite with sqflite_cipher or embed the source. |
|
I feel the long term solution of the users of this library would be to transition to sqflite_common_ffi, which uses the Dart sqlite3 package under the hood and it supports encryption compatible with SQLCipher. You automatically get multithread support and compatibility with all SQLite3 native functions and extensions |
Summary
Adds Swift Package Manager (SPM) support for iOS and macOS while keeping CocoaPods fully working. This is the
4.0.0line.The headline decision is building SQLCipher from source as a static C target rather than depending on a prebuilt dynamic SQLCipher framework. Dynamic linkage leaves the
sqlite3_*symbols in a dylib where dyld can resolve them to the system/usr/lib/libsqlite3.dylibinstead of SQLCipher — silently dropping encryption. Static linkage makes thesqlite3_*symbols defined in the app binary, where they can never be shadowed by the system lib — the exact property the existing CocoaPods static-pod path relied on.What changed
Package.swiftfor ios and macos. SQLCipher is compiled from a vendored amalgamation as a static C target (CommonCrypto backend,SQLITE_HAS_CODEC, and the same build flags the CocoaPodsSQLCipherpod ships with). FMDB is vendored.NDEBUGis defined on the SQLCipher target because SwiftPM compiles C targets with-fmodules, under which SQLite's internal auto-NDEBUGdoesn't reach the assert sites in debug builds.Sources/sqflite_sqlcipherand the podspecs were updated accordingly; the package directory was renamedsqflite/→sqflite_sqlcipher/so the SwiftPM package identity matches the package name.SQLCipherpod tops out at 4.10.0, so the podspec stays there; the source-built SPM target isn't bound to the pod's cadence. SQLCipher keeps a stable on-disk format across 4.x with the CommonCrypto backend, so databases written by the SPM build (4.16.0) and the CocoaPods build (4.10.0) interchange in both directions.cross_version_test/): an encrypted database created by the published CocoaPods build and by the local SPM build is verified mutually readable with the same key on both macOS and the iOS Simulator — covering INTEGER/REAL/TEXT/BLOB/NULL + unicode, asserting wrong-key rejection, and checking the file is not plaintext. All four writer/reader directions (pod→pod, pod→spm, spm→pod, spm→spm) pass.FlutterFrameworkdependency); iOS deployment target raised to 12.0. Example ios/macos runner projects regenerated.Note on diff size
The bulk of the line count is the vendored SQLCipher amalgamation (
sqlite3.c/sqlite3.h), checked in once per platform underSources/SQLCipher/. The amalgamation is byte-identical to the officialsqlcipher v4.16.0source (SQLite 3.53.1).Verification
End to end on macOS and iOS:
swift runprobe reportscipher_version: 4.16.0 community.sqlite3_keyis a defined symbol in the app binary, with no SQLCipher/libsqlite3dylib dependency.cross_version_testpasses both directions with wrong-key rejection.