Skip to content

appfair/Stanza-Redux

 
 

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

98 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Stanza Redux

Stanza Redux is a cross-platform ebook reader for iOS and Android, built with Skip and powered by the Readium SDK. A single Swift codebase powers a platform-native app that utilizes SwiftUI on iOS and Jetpack Compose on Android, while each platform uses its own native Readium toolkit for EPUB parsing, rendering, and navigation.

Architecture

Stanza Redux demonstrates how Skip can bridge platform-specific native libraries from a shared codebase. The app is organized into two Swift Package Manager modules:

  • Stanza — The UI layer (SwiftUI views), built in Sources/Stanza/
  • StanzaModel — The data layer (database, OPDS parsing, settings), built in Sources/StanzaModel/

Readium Integration

The Readium SDK is published as two independent toolkits:

Stanza Redux uses #if SKIP / #if !SKIP conditional compilation to call the appropriate platform SDK while sharing all UI and business logic. Platform-specific types are abstracted behind thin wrappers defined in StanzaModel:

Wrapper iOS Type Android Type
Pub ReadiumShared.Publication org.readium.r2.shared.publication.Publication
Loc ReadiumShared.Locator org.readium.r2.shared.publication.Locator
Lnk ReadiumShared.Link org.readium.r2.shared.publication.Link
Man ReadiumShared.Manifest org.readium.r2.shared.publication.Manifest

These wrappers expose cross-platform properties (title, href, progression, etc.) that the UI layer consumes without needing to know which platform SDK is providing the data.

The EPUB navigator is embedded differently on each platform:

  • iOS: EPUBNavigatorViewController is wrapped in a UIViewControllerRepresentable and hosted in a SwiftUI view
  • Android: EpubNavigatorFragment is embedded via Jetpack Compose's AndroidFragment composable within a ComposeView

Data Flow

StanzaApp (entry point)
  └── ContentView (tab bar)
        ├── LibraryView (book management)
        ├── BrowseView (OPDS catalogs)
        └── SettingsView (preferences)
              └── AdvancedSettingsView

Shared state is managed through the SwiftUI environment:

  • StanzaSettings@Observable class persisting user preferences to UserDefaults
  • LibraryManager@Observable class managing the book database and file operations
  • ErrorManager@Observable class providing centralized error alert presentation

Library

The Library tab is the main screen of the app. It displays all imported books with cover art, titles, authors, and reading progress.

Screenshot 2026-03-23 at 17 52 36 Screenshot 2026-03-23 at 17 53 26

Features

  • Import books from the device's file system using the system document picker
  • Cover art extraction — automatically extracts and caches cover images from EPUB files
  • Reading progress — displays percentage complete for each book
  • Search — filter the library by title or author
  • Book management — long-press context menu to view details, edit metadata, or delete books
  • Resume reading — tap a book to open it in the reader; the app remembers your last reading position
  • Sample book — a bundled copy of Alice's Adventures in Wonderland can be imported to try the reader immediately

Book Detail

Each book has a detail view showing metadata (title, author, identifier), reading progress, chapter count, and file path. An edit mode allows modifying the title, author, and identifier.

Reader

The reader presents EPUB content in a paginated view with customizable typography and an overlay HUD for navigation controls.

Stanza_Reader_Android Stanza_Reader_iOS

Navigation

  • Tap zones — tap the left or right third of the screen to go backward or forward; tap the center to toggle the HUD
  • Animated page turns — smooth page transition animations on both platforms
  • Table of Contents — hierarchical chapter navigation with the current chapter highlighted
  • Bookmarks — add, view, edit notes on, and navigate to bookmarks
  • Reading position persistence — your position is saved on every page turn and restored on next launch

HUD Controls

The heads-up display provides:

  • Progress bar with chapter title and percentage
  • Font size controls (increase/decrease buttons)
  • Font picker — horizontal scrolling panel with font previews
  • Spacing controls — cycle through presets for line height, character spacing, word spacing, and page margins
  • Table of Contents and Bookmark buttons

Themes and Appearance

  • Light, Dark, and Sepia reading themes
  • System appearance follows the device setting
  • Status bar hiding for immersive reading (iOS)

Catalogs

The Catalogs tab (enabled via Advanced Settings) allows browsing and downloading books from OPDS catalog feeds.

Stanza_Browse_Android Stanza_Browse_iOS

Features

  • Pre-configured catalogs — Standard Ebooks, Project Gutenberg, and Ebooks Gratuits
  • Custom catalogs — add any OPDS feed URL
  • Catalog browsing — navigate categories, groups, and facets
  • Search — search within catalogs that support OpenSearch
  • Book detail — view cover art, author, summary, and available download formats
  • Multiple format support — when a book is available in multiple EPUB variants (e.g., "Recommended compatible epub", "Advanced epub"), a menu lets you choose which to download
  • Direct download — download and import books directly into the library
  • About This Catalog — displays feed metadata including icon, description, total book count, and informational links

OPDS Implementation

The OPDS service (OPDSService) handles:

  • Parsing both OPDS 1 (Atom/XML) and OPDS 2 (JSON) feeds via the Readium OPDS parsers
  • Extracting navigation images from feed entries using raw XML parsing with SkipXML
  • Promoting Atom rel="enclosure" entries to proper publication entries for feeds that don't use standard OPDS acquisition links
  • OpenSearch template resolution for catalog search
  • HTML-to-Markdown conversion of book summaries using HTMLMarkdown

Settings

The Settings tab provides controls for reading preferences, text layout, and spacing.

Stanza_Settings_Android Stanza_Settings_iOS

Reading Preferences

  • Appearance — System, Light, or Dark mode
  • Sepia Theme — warm-toned reading theme
  • Font — choose from system fonts and bundled custom fonts (Montserrat, Noto Serif, Noto Sans)
  • Font Size — adjustable from 50% to 300%
  • Animate Page Turns — toggle animated transitions
  • Left Tap Advances — swap the left-tap direction
  • Hide Status Bar in Reader — immersive reading mode
  • Open Web Pages in Embedded Browser — use SFSafariViewController (iOS) or Chrome Custom Tabs (Android)

Text Layout

  • Columns (auto, one, two)
  • Content fit (auto, page, width)
  • Hyphenation, text alignment, text normalization
  • Publisher styles toggle

Spacing

  • Line height, page margins, paragraph spacing, word spacing (all with slider controls)

Advanced Settings

  • Enable Catalogs — show/hide the Catalogs tab

Error Handling

The app uses a centralized ErrorManager that provides consistent error alerts across all screens. When an error occurs anywhere in the app, errorManager.errorOccurred(info:) is called with structured ErrorInfo containing a title, message, underlying error, and optional help URL. The error manager logs the error and presents an alert with "OK" to dismiss and "Help" to search the project's issue tracker.

The ErrorManager is safe to call from any thread — it dispatches to the main actor internally.

Building

This project is both a stand-alone Swift Package Manager module, as well as an Xcode project that builds and transpiles the project into a Kotlin Gradle project for Android using the Skip plugin.

Building the module requires that Skip be installed using Homebrew with brew install skiptools/skip/skip.

This will also install the necessary transpiler prerequisites: Kotlin, Gradle, and the Android build tools.

Installation prerequisites can be confirmed by running skip checkup.

Testing

The module can be tested using the standard swift test command or by running the test target for the macOS destination in Xcode, which will run the Swift tests as well as the transpiled Kotlin JUnit tests in the Robolectric Android simulation environment.

Parity testing can be performed with skip test, which will output a table of the test results for both platforms.

Running

Xcode and Android Studio must be downloaded and installed in order to run the app in the iOS simulator / Android emulator. An Android emulator must already be running, which can be launched from Android Studio's Device Manager.

To run both the Swift and Kotlin apps simultaneously, launch the StanzaApp target from Xcode. A build phases runs the "Launch Android APK" script that will deploy the transpiled app a running Android emulator or connected device. Logging output for the iOS app can be viewed in the Xcode console, and in Android Studio's logcat tab for the transpiled Kotlin app.

License

This software is licensed under the GNU General Public License v2.0 or later.

About

A native ebook reader for iOS and Android based on the Readium toolkit

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages

  • Swift 96.6%
  • Kotlin 2.4%
  • Ruby 1.0%