Skip to content

Add Keychain-based LCP repositories, deprecate SQLite adapter#713

Merged
mickael-menu merged 6 commits intodevelopfrom
lcp-keychain
Feb 5, 2026
Merged

Add Keychain-based LCP repositories, deprecate SQLite adapter#713
mickael-menu merged 6 commits intodevelopfrom
lcp-keychain

Conversation

@mickael-menu
Copy link
Member

Changelog

Added

LCP

  • New Keychain-based implementations of the LCP license and passphrase repositories: LCPKeychainLicenseRepository and LCPKeychainPassphraseRepository.
    • Stored securely in the iOS/macOS Keychain.
    • Persist across app reinstalls.
    • Optionally synchronized across devices via iCloud Keychain.

Deprecated

LCP

  • ReadiumAdapterLCPSQLite is now deprecated in favor of the built-in Keychain repositories. See the migration guide for instructions.

Migration Guide

Migrating LCP Repositories from SQLite to the Keychain

The ReadiumAdapterLCPSQLite module is now deprecated. ReadiumLCP provides built-in Keychain-based repositories that are more secure, persist across app reinstalls, and optionally synchronize across devices via iCloud Keychain.

Updating the LCPService initialization

Replace the SQLite repositories with their Keychain equivalents:

-import ReadiumAdapterLCPSQLite
 import ReadiumLCP

 let lcpService = LCPService(
     client: LCPClient(),
-    licenseRepository: try! LCPSQLiteLicenseRepository(),
-    passphraseRepository: try! LCPSQLitePassphraseRepository(),
+    licenseRepository: LCPKeychainLicenseRepository(),
+    passphraseRepository: LCPKeychainPassphraseRepository(),
     assetRetriever: assetRetriever,
     httpClient: httpClient
 )

Then remove ReadiumAdapterLCPSQLite from your project dependencies:

  • Swift Package Manager: Remove the ReadiumAdapterLCPSQLite product from your target dependencies.
  • Carthage: Remove ReadiumAdapterLCPSQLite.xcframework and SQLite.xcframework from your project.
  • CocoaPods: Remove pod 'ReadiumAdapterLCPSQLite' from your Podfile and run pod install.

Migrating existing data

If your app already stores LCP data in the SQLite database, you can migrate it to the Keychain using the built-in migration helpers. Run this migration once, for example during an app update:

import ReadiumAdapterLCPSQLite
import ReadiumLCP

let keychainLicenseRepository = LCPKeychainLicenseRepository()
let keychainPassphraseRepository = LCPKeychainPassphraseRepository()

let sqliteLicenseRepository = try LCPSQLiteLicenseRepository()
let sqlitePassphraseRepository = try LCPSQLitePassphraseRepository()

try await sqliteLicenseRepository.migrate(to: keychainLicenseRepository)
try await sqlitePassphraseRepository.migrate(to: keychainPassphraseRepository)

Copy link

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR introduces new Keychain-based implementations for LCP license and passphrase repositories, deprecating the existing SQLite adapter. The new repositories provide better security by leveraging the iOS/macOS Keychain, persist data across app reinstalls, and optionally synchronize across devices via iCloud Keychain.

Changes:

  • Added LCPKeychainLicenseRepository and LCPKeychainPassphraseRepository as built-in Keychain-based implementations
  • Added Keychain utility class in ReadiumInternal for low-level Keychain operations
  • Deprecated ReadiumAdapterLCPSQLite module with migration helpers to facilitate transition
  • Extended LCPPassphraseRepository protocol with passphrases() method for fallback passphrase lookup

Reviewed changes

Copilot reviewed 23 out of 23 changed files in this pull request and generated 2 comments.

Show a summary per file
File Description
Sources/Internal/Keychain.swift New utility class for Keychain operations with proper error handling and Sendable compliance
Sources/LCP/Repositories/Keychain/LCPKeychainLicenseRepository.swift Actor-based Keychain implementation for license storage with typed throws
Sources/LCP/Repositories/Keychain/LCPKeychainPassphraseRepository.swift Actor-based Keychain implementation for passphrase storage with typed throws
Sources/LCP/LCPPassphraseRepository.swift Extended protocol with passphrases() method for legacy compatibility
Sources/LCP/Services/PassphrasesService.swift Updated to support fallback passphrase lookup for legacy SQLite compatibility
Sources/Adapters/LCPSQLite/SQLiteLCPLicenseRepository.swift Added migration helper to transfer data to Keychain repository
Sources/Adapters/LCPSQLite/SQLiteLCPPassphraseRepository.swift Added migration helper and passphrases() method implementation
Tests/LCPTests/Repositories/Keychain/LCPKeychainLicenseRepositoryTests.swift Comprehensive test coverage for license repository
Tests/LCPTests/Repositories/Keychain/LCPKeychainPassphraseRepositoryTests.swift Comprehensive test coverage for passphrase repository
Tests/InternalTests/KeychainTests.swift Keychain tests (commented out due to entitlement requirements)
TestApp/Sources/App/Readium.swift Updated to use new Keychain repositories
TestApp/Sources/LCP/LCPModule.swift Removed SQLite adapter import
TestApp/Integrations/*/project+lcp.yml Removed SQLite adapter dependencies from integration configurations
Package.swift Added ReadiumInternal dependency to ReadiumLCP
CHANGELOG.md Documented new features and deprecations
docs/Migration Guide.md Added comprehensive migration instructions

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@mickael-menu mickael-menu merged commit bc32270 into develop Feb 5, 2026
5 checks passed
@mickael-menu mickael-menu deleted the lcp-keychain branch February 5, 2026 19:11
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