A Swift library that wraps @mozilla/readability and generalizes the Firefox Reader, which enhances web pages for better reading. This library provides a seamless way to detect, parse, and display reader-friendly content from any web page by integrating with WKWebView.
light | dark | sepia |
---|---|---|
![]() |
![]() |
![]() |
- Parsing
Parse a URL or HTML string into a structured article using @mozilla/readability. - WKWebView Integration
Easily integrate with WKWebView. - Reader Mode Overlay
Easily toggle a reader overlay with customizable themes and font sizes.
- Swift: 6.0 or later
- Xcode: 16.0 or later
swift-readability is available via the Swift Package Manager
.package(url: "https://github.com/Ryu0118/swift-readability", exact: "0.1.0")
You can parse an article either from a URL or directly from an HTML string.
Parsing from a URL:
import Readability
let readability = Readability()
let result = try await readability.parse(url: URL(string: "https://example.com/article")!)
Parsing from an HTML string:
import Readability
let html = """
<html>
<!-- Your HTML content here -->
</html>
"""
let result = try await readability.parse(html: html)
swift-readability provides ReadabilityWebCoordinator
that prepares a WKWebView
configuration, and exposes two asynchronous streams: contentParsed
(emitting generated reader HTML) and availabilityChanged
(emitting reader mode availability updates). This configuration enables your WKWebView
to detect when a web page is suitable for reader mode, generate a reader-friendly HTML overlay, and toggle reader mode dynamically.
import ReadabilityUI
let coordinator = ReadabilityWebCoordinator(initialStyle: ReaderStyle(theme: .dark, fontSize: .size5))
let configuration = try await coordinator.createReadableWebViewConfiguration()
let webView = WKWebView(frame: .zero, configuration: configuration)
// Process generated reader HTML asynchronously.
for await html in coordinator.contentParsed {
do {
try await webView.showReaderContent(with: html)
} catch {
// Handle the error here.
}
}
// Monitor reader mode availability asynchronously.
for await availability in coordinator.availabilityChanged {
// For example, update your UI to enable or disable the reader mode button.
}
Below are usage examples for each of the functions provided by the ReaderControllable
protocol extension. Since WKWebView
conforms to ReaderControllable
, you can call these methods directly on your WKWebView
instance.
Warning
Changes to the reader style (theme and font size) are only available when the web view is in Reader Mode.
import ReadabilityUI
// Set the entire reader style (theme and font size)
try await webView.set(style: ReaderStyle(theme: .dark, fontSize: .size5))
// Set only the reader theme (supports sepia, light, and dark).
try await webView.set(theme: .sepia)
// Set only the font size
try await webView.set(fontSize: .size7)
// Show the reader overlay using the HTML received from the ReadabilityWebCoordinator.contentParsed(_:) event.
try await webView.showReaderContent(with: html)
// Hide the reader overlay.
try await webView.hideReaderContent()
// Determine if the web view is currently in reader mode.
let isReaderMode = try await webView.isReaderMode()
If you are using a SwiftUI wrapper library for WKWebView (such as Cybozu/WebUI) that does not expose the WKWebView instance, you can conform any object that has an evaluateJavaScript method to ReaderControllable. For example:
import WebUI
import ReadabilityUI
extension WebViewProxy: @retroactive ReaderControllable {
public func evaluateJavaScript(_ javascriptString: String) async throws -> Any {
let result: Any? = try await evaluateJavaScript(javascriptString)
return result ?? ()
}
}
By conforming WebViewProxy
to ReaderControllable
, you can control the reader from the proxy, for example:
WebViewReader { proxy in
WebView(configuration: configuration)
.task {
for await html in coordinator.contentParsed {
if let url = proxy.url {
try? await proxy.showReaderContent(with: html)
try? await proxy.set(theme: .dark)
try? await proxy.set(fontSize: .size8)
}
}
}
}
For a more detailed implementation of integrating swift-readability with SwiftUI using Cybozu/WebUI, please refer to the Example provided in this repository.
Before building the Swift Package, please complete the following steps:
- Verify that npm is installed.
- Then, run
make bootstrap
.
If you modify the source code in the webpack-resources folder, please run npm run build
This project leverages several open source projects:
- @mozilla/readability for parsing web pages and generating reader-friendly content (licensed under the MIT License).
- mozilla-mobile/firefox-ios for inspiration on Reader Mode functionality (licensed under the MPL 2.0).
- Cybozu/WebUI for the SwiftUI integration example (licensed under the MIT License).
- cure53/DOMPurify for sanitizing HTML content (licensed under the MIT License).
In addition, the following files are distributed under the Mozilla Public License, Version 2.0 (MPL 2.0):
- webpack-resources/AtDocumentStart.js
- webpack-resources/ReadabilitySanitized.js
- webpack-resources/Reader.html
- webpack-resources/Reader.css
- Sources/ReadabilityUI/Resources/AtDocumentStart.js
- Sources/ReadabilityUI/Resources/Reader.html
- Sources/Readability/Resources/ReadabilitySanitized.js
- Sources/ReadabilityCore/ReaderStyle.swift