Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
28 commits
Select commit Hold shift + click to select a range
de16a90
Refactor public classes, structs, and enums to be Sendable (#761)
stevenzeck Apr 1, 2026
6ac8bb7
Refactor HTTP client (#764)
stevenzeck May 21, 2026
3247998
Fix `FormatSniffer` for strict concurrency (#768)
stevenzeck May 23, 2026
1997017
Replace `AnyHashable` with `AnySendableHashable` (#772)
stevenzeck May 29, 2026
f53a32e
Update `Logger` to be Swift 6 compliant (#798)
stevenzeck Jun 1, 2026
a7bd2f7
Update Publication Services to be Swift 6 compliant (#769)
stevenzeck Jun 1, 2026
6e885cf
Make `Streamable` and `Resource` protocols `Sendable` (#801)
stevenzeck Jun 2, 2026
b2f6dad
Cleanup unused files and properties (#779)
grighakobian Jun 12, 2026
4b66591
Remove `Atomic` in favor of `Mutex` (#807)
grighakobian Jun 12, 2026
16b6097
Adopt `Sendable` across PDF, LCP and `Container` APIs (#803)
stevenzeck Jun 17, 2026
9ff9241
Migrate media and utility types to Swift 6 concurrency (#808)
stevenzeck Jun 19, 2026
b4ba3b5
Fix HTTP streaming in Swift 6 (#819)
stevenzeck Jun 19, 2026
d22ea40
Migrate `ReadiumShared` to Swift 6 (#820)
grighakobian Jun 24, 2026
ca46d90
Remove deprecated APIs (#826)
mickael-menu Jun 24, 2026
2544e51
Migrate `ReadiumStreamer` to Swift 6 (#828)
grighakobian Jun 24, 2026
5ca805c
Fix `Sendable` conformance in `Mutex` (#827)
grighakobian Jun 24, 2026
cbbf4d6
Fix `Sendable` conformance for `Weak` type (#831)
grighakobian Jun 25, 2026
879998b
Migrate `ReadiumLCP` to Swift 6 (#829)
stevenzeck Jun 26, 2026
c9c1391
Migrate `ReadiumOPDS` to Swift 6 (#830)
grighakobian Jul 1, 2026
8ab6c3e
Migrate `ReadiumNavigator` to Swift 6 (#832)
stevenzeck Jul 3, 2026
33e8f72
Remove the `sender` parameter from `PublicationOpener` and LCP APIs (…
grighakobian Jul 3, 2026
841b4db
Fix LCP hashed-passphrase validation
mickael-menu Jul 3, 2026
08b779f
Fix data races for PublicationOpener
stevenzeck Jul 8, 2026
54d7f08
Fix delayed audio playback state updates
stevenzeck Jul 8, 2026
95c871e
Remove swiftLanguageMode targeting
stevenzeck Jul 8, 2026
bc332c0
Revert "Fix data races for PublicationOpener"
stevenzeck Jul 8, 2026
06248b9
Adopt Swift 6.2 approachable concurrency and harden the strict-concur…
mickael-menu Jul 12, 2026
2615f74
Merge branch 'swift6' into swift6-fixes
stevenzeck Jul 13, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
135 changes: 68 additions & 67 deletions .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -9,35 +9,32 @@ concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true

permissions:
contents: read

env:
platform: ${{ 'iOS Simulator' }}
device: ${{ 'iPhone 16 Pro' }}
platform: iOS Simulator
device: iPhone 17 Pro
commit_sha: ${{ github.sha }}
DEVELOPER_DIR: /Applications/Xcode_16.4.app/Contents/Developer
DEVELOPER_DIR: /Applications/Xcode_26.4.app/Contents/Developer

jobs:
build:
name: Build
runs-on: macos-15
runs-on: macos-26
if: ${{ !github.event.pull_request.draft }}
timeout-minutes: 60
env:
scheme: ${{ 'Readium-Package' }}
scheme: Readium-Package

steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
- name: Install dependencies
run: |
brew update
brew install xcodegen
# Preload the list of simulator for xcodebuild. The workflow is flaky without it.
xcrun simctl list
- name: Check CocoaPods podspecs
run: |
# Check that the podspecs are up to date.
make podspecs
git diff --exit-code Support/CocoaPods/
if git ls-files --others --exclude-standard Support/CocoaPods/ | grep -q .; then echo "Untracked podspec files found. Run 'make podspecs' and commit the result."; exit 1; fi
- name: Build
run: |
set -eo pipefail
Expand All @@ -50,66 +47,49 @@ jobs:
run: |
jq -r '.pins[] | "\(.identity): \(.state.version)"' Package.resolved

# navigator-ui-tests:
# name: Navigator UI Tests
# runs-on: macos-15
# if: ${{ !github.event.pull_request.draft }}
# steps:
# - name: Checkout
# uses: actions/checkout@v3
# - name: Install dependencies
# run: |
# brew update
# brew install xcodegen
# # Preload the list of simulator for xcodebuild. The workflow is flaky without it.
# xcrun simctl list
# - name: Test
# run: |
# set -eo pipefail
# make navigator-ui-tests-project
# xcodebuild test -project Tests/NavigatorTests/UITests/NavigatorUITests.xcodeproj -scheme NavigatorTestHost -destination "platform=$platform,name=$device" | xcbeautify --renderer github-actions
# Runs the test suite under the Thread Sanitizer to validate the
# `@unchecked Sendable` and `nonisolated(unsafe)` sites at runtime.
# Only runs on pushes to main/develop to keep pull requests light.
test-tsan:
name: Test (Thread Sanitizer)
runs-on: macos-26
if: github.event_name == 'push'
timeout-minutes: 60
env:
scheme: Readium-Package

playground:
name: Playground
runs-on: macos-15
if: ${{ !github.event.pull_request.draft }}
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
- name: Install dependencies
run: |
brew update
brew install xcodegen
# Preload the list of simulator for xcodebuild. The workflow is flaky without it.
xcrun simctl list
- name: Check Playground project is up-to-date
run: |
make playground
git diff --exit-code Playground/.xcodegen
- name: Build
- name: Test
run: |
set -eo pipefail
xcodebuild build -scheme Playground -project Playground/Playground.xcodeproj -destination "platform=$platform,name=$device" | xcbeautify --renderer github-actions
xcodebuild test -scheme "$scheme" -destination "platform=$platform,name=$device" -enableThreadSanitizer YES | xcbeautify --renderer github-actions

lint:
name: Lint
runs-on: macos-15
runs-on: macos-26
if: ${{ !github.event.pull_request.draft }}
timeout-minutes: 60
env:
scripts: ${{ 'Sources/Navigator/EPUB/Scripts' }}
scripts: Sources/Navigator/EPUB/Scripts

steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
- name: Install pnpm
uses: pnpm/action-setup@v2
uses: pnpm/action-setup@v6
with:
package_json_file: Sources/Navigator/EPUB/Scripts/package.json
run_install: false
- name: Setup cache
uses: actions/setup-node@v3
uses: actions/setup-node@v6
with:
node-version: 20
node-version: 24
cache: 'pnpm'
cache-dependency-path: Sources/Navigator/EPUB/Scripts/pnpm-lock.yaml
- name: Install dependencies
Expand All @@ -122,44 +102,66 @@ jobs:
run: |
make scripts
git diff --exit-code --name-only Sources/Navigator/EPUB/Assets/Static/scripts/*.js
if git ls-files --others --exclude-standard Sources/Navigator/EPUB/Assets/Static/scripts/ | grep -q .; then echo "Untracked bundled scripts found. Run 'make scripts' and commit the result."; exit 1; fi
- name: Lint Swift formatting
run: make lint-format
- name: Check CocoaPods podspecs
run: |
# Check that the podspecs are up to date.
make podspecs
git diff --exit-code Support/CocoaPods/
if git ls-files --others --exclude-standard Support/CocoaPods/ | grep -q .; then echo "Untracked podspec files found. Run 'make podspecs' and commit the result."; exit 1; fi

int-dev:
name: Integration (Local)
runs-on: macos-15
runs-on: macos-26
if: ${{ !github.event.pull_request.draft }}
defaults:
run:
working-directory: TestApp
environment: LCP
timeout-minutes: 60
environment:
name: LCP
deployment: false
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
- name: Install dependencies
run: |
brew update
brew install xcodegen
# Preload the list of simulator for xcodebuild. The workflow is flaky without it.
xcrun simctl list
- name: Generate project
run: make dev lcp=${{ secrets.LCP_URL_SPM }}
- name: Build
- name: Check Playground project is up-to-date
run: |
make playground
git diff --exit-code Playground/.xcodegen
if git ls-files --others --exclude-standard Playground/Playground.xcodeproj/ | grep -q .; then echo "Untracked Playground project files found. Run 'make playground' and commit the result."; exit 1; fi
- name: Build Playground
run: |
set -eo pipefail
xcodebuild build -scheme Playground -project Playground/Playground.xcodeproj -destination "platform=$platform,name=$device" | xcbeautify --renderer github-actions
- name: Generate TestApp project
working-directory: TestApp
env:
LCP_URL_SPM: ${{ secrets.LCP_URL_SPM }}
run: make dev lcp="$LCP_URL_SPM"
- name: Build TestApp
working-directory: TestApp
run: |
set -eo pipefail
xcodebuild build -scheme TestApp -destination "platform=$platform,name=$device" | xcbeautify --renderer github-actions

int-spm:
name: Integration (Swift Package Manager)
runs-on: macos-15
runs-on: macos-26
if: ${{ !github.event.pull_request.draft }}
timeout-minutes: 60
defaults:
run:
working-directory: TestApp
environment: LCP
environment:
name: LCP
deployment: false
steps:
- name: Checkout
uses: actions/checkout@v3
uses: actions/checkout@v6
# We can't use the current github.sha with pull_request event, because they will
# reference the merge commit which cannot be fetched with SPM.
- name: Set commit SHA
Expand All @@ -168,15 +170,14 @@ jobs:
echo "commit_sha=${{ github.event.pull_request.head.sha }}" >> "$GITHUB_ENV"
- name: Install dependencies
run: |
brew update
brew install xcodegen
# Preload the list of simulator for xcodebuild. The workflow is flaky without it.
xcrun simctl list
- name: Generate project
run: make spm lcp=${{ secrets.LCP_URL_SPM }} commit=$commit_sha
env:
LCP_URL_SPM: ${{ secrets.LCP_URL_SPM }}
run: make spm lcp="$LCP_URL_SPM" commit="$commit_sha"
- name: Build
run: |
set -eo pipefail
xcodebuild build -scheme TestApp -destination "platform=$platform,name=$device" | xcbeautify --renderer github-actions


10 changes: 7 additions & 3 deletions .github/workflows/docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ concurrency:

jobs:
build-and-deploy:
runs-on: macos-15
runs-on: macos-26
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v6
Expand All @@ -28,16 +29,19 @@ jobs:
run: |
git fetch --tags --force
VERSION=$(git describe --tag --match "[0-9]*" --abbrev=0)
echo "READIUM_VERSION=$VERSION" >> $GITHUB_OUTPUT
echo "READIUM_VERSION=$VERSION" >> "$GITHUB_OUTPUT"
if [[ $GITHUB_REF == refs/tags/* ]]; then
echo "folder=${GITHUB_REF#refs/tags/}" >> $GITHUB_OUTPUT
else
echo "folder=latest" >> $GITHUB_OUTPUT
fi

# The docs are generated twice because the version is baked into the
# output (DocC --hosting-base-path and the 404/redirect pages embed
# /swift-toolkit/<version>/ URLs), so the "latest" site cannot be a
# plain copy of the versioned one.
- name: Generate Documentation
run: |
chmod +x scripts/generate-docs.sh
./scripts/generate-docs.sh ${{ steps.versioning.outputs.READIUM_VERSION }}
./scripts/generate-docs.sh latest

Expand Down
28 changes: 28 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,34 @@

All notable changes to this project will be documented in this file. Take a look at [the migration guide](docs/Migration%20Guide.md) to upgrade between two major versions.


## [Unreleased: swift6]

### Changed

* The toolkit is migrated to Swift 6 with strict concurrency checking. All packages compile in the Swift 6 language mode. See [the migration guide](docs/Migration%20Guide.md).
* The toolkit adopts the `NonisolatedNonsendingByDefault` (SE-0461), `InferIsolatedConformances` and `MemberImportVisibility` upcoming Swift features.

#### Shared

* OPDS models (`Feed`, `Group`, `Facet`, `OpdsMetadata`) are now structs with value semantics.
* `Publication`, `Resource`, `Container` and related types are now `Sendable`. Custom implementations of `Resource`, `Container`, `HTTPClient` or `PublicationService` must be `Sendable` too.
* `Resource.stream()` now cooperates with task cancellation: the built-in resources fail with `ReadError.cancelled` when the surrounding task is cancelled, and custom implementations are expected to do the same.

#### Navigator

* The `Navigator` and `VisualNavigator` protocols and their delegates are now isolated to the main actor.

#### LCP

* `LCPService.init` now requires an explicit `deviceName` parameter. We recommend passing `UIDevice.current.name`. See [the migration guide](docs/Migration%20Guide.md).
* `LCPDialogAuthentication` no longer takes a `sender` view controller. It now presents its passphrase dialog through a new `LCPDialogAuthenticationDelegate` that you implement and retain for the lifetime of the authentication. See [the Readium LCP guide](docs/Guides/Readium%20LCP.md) and [the migration guide](docs/Migration%20Guide.md).

### Removed

* The deprecated `ReadiumAdapterGCDWebServer` and `ReadiumAdapterLCPSQLite` adapter packages have been removed.


## [Unreleased]

### Added
Expand Down
6 changes: 6 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,12 @@ help:
update-locales\tUpdate the localization files\n\
"

.PHONY: test
test:
xcodebuild test -project "TestApp/TestApp.xcodeproj" -scheme TestApp -destination "platform=iOS Simulator,name=iPhone Air" 2> /dev/null \
| xcbeautify --quieter --disable-logging \
| grep -Ev "^Executed |Test Suite 'All tests'|Test run started\.|Test session results:"; true

.SILENT:
.PHONY: playground
playground:
Expand Down
39 changes: 14 additions & 25 deletions Package.swift
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
// swift-tools-version:5.10
// swift-tools-version:6.2
//
// Copyright 2026 Readium Foundation. All rights reserved.
// Use of this source code is governed by the BSD-style license
Expand All @@ -17,20 +17,14 @@ let package = Package(
.library(name: "ReadiumNavigator", targets: ["ReadiumNavigator"]),
.library(name: "ReadiumOPDS", targets: ["ReadiumOPDS"]),
.library(name: "ReadiumLCP", targets: ["ReadiumLCP"]),

// Adapters to third-party dependencies.
.library(name: "ReadiumAdapterGCDWebServer", targets: ["ReadiumAdapterGCDWebServer"]),
.library(name: "ReadiumAdapterLCPSQLite", targets: ["ReadiumAdapterLCPSQLite"]),
],
dependencies: [
.package(url: "https://github.com/krzyzanowskim/CryptoSwift.git", from: "1.10.0"),
.package(url: "https://github.com/marmelroy/Zip.git", from: "2.1.2"),
.package(url: "https://github.com/ra1028/DifferenceKit.git", from: "1.3.0"),
.package(url: "https://github.com/readium/Fuzi.git", from: "4.0.0"),
.package(url: "https://github.com/readium/GCDWebServer.git", from: "4.0.0"),
.package(url: "https://github.com/readium/ZIPFoundation.git", from: "3.0.1"),
.package(url: "https://github.com/scinfu/SwiftSoup.git", from: "2.13.5"),
.package(url: "https://github.com/stephencelis/SQLite.swift.git", from: "0.16.0"),
.package(url: "https://github.com/apple/swift-docc-plugin", from: "1.5.0"),
],
targets: [
Expand Down Expand Up @@ -154,24 +148,6 @@ let package = Package(
// path: "Tests/LCPTests"
// ),

.target(
name: "ReadiumAdapterGCDWebServer",
dependencies: [
.product(name: "ReadiumGCDWebServer", package: "GCDWebServer"),
"ReadiumShared",
],
path: "Sources/Adapters/GCDWebServer"
),

.target(
name: "ReadiumAdapterLCPSQLite",
dependencies: [
.product(name: "SQLite", package: "SQLite.swift"),
"ReadiumLCP",
],
path: "Sources/Adapters/LCPSQLite"
),

.target(
name: "ReadiumInternal",
path: "Sources/Internal"
Expand All @@ -192,3 +168,16 @@ let package = Package(
),
]
)

for target in package.targets {
// Adopt the future language defaults now, to avoid a second
// behavioral break for integrators when they become the default.
// In particular, `NonisolatedNonsendingByDefault` (SE-0461) runs
// `nonisolated async` functions on the caller's actor; CPU-heavy
// implementations are marked `@concurrent` to stay off-actor.
target.swiftSettings = (target.swiftSettings ?? []) + [
.enableUpcomingFeature("NonisolatedNonsendingByDefault"),
.enableUpcomingFeature("InferIsolatedConformances"),
.enableUpcomingFeature("MemberImportVisibility"),
]
}
3 changes: 2 additions & 1 deletion Playground/.xcodegen
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,8 @@
"platform" : "iOS",
"settings" : {
"SWIFT_APPROACHABLE_CONCURRENCY" : true,
"SWIFT_DEFAULT_ACTOR_ISOLATION" : "MainActor"
"SWIFT_DEFAULT_ACTOR_ISOLATION" : "MainActor",
"SWIFT_VERSION" : 6
},
"sources" : [
{
Expand Down
Loading