GTD-style reading app for iPadOS. Capture URLs via Share Sheet, read with clean typography, process into OmniFocus actions.
-
Open Xcode 15.2+
-
File → New → Project
-
Choose App (iOS)
-
Settings:
- Product Name:
InboxReader - Team: Your team
- Organization Identifier:
com.yourname(or your identifier) - Interface: SwiftUI
- Language: Swift
- Storage: None (we're using SwiftData manually)
- Uncheck "Include Tests" for now
- Product Name:
-
Save to:
~/Documents/Github/InboxReader- Important: Save in the existing folder, Xcode will create the .xcodeproj alongside the existing files
Add the swift-readability package for article extraction. The package has been cloned locally to Packages/swift-readability.
- File → Add Package Dependencies...
- Click "Add Local..." at the bottom of the package picker
- Navigate to
InboxReader/Packages/swift-readability - Click "Add Package"
- Select the
Readabilitylibrary (not ReadabilityUI) - Add to target: InboxReader (main app only, not extension)
Note: This package requires Swift 6.0 (Xcode 16+). If you're on Xcode 15, you may need to modify Packages/swift-readability/Package.swift to change swift-tools-version: 6.0 to swift-tools-version: 5.9.
- In Xcode's Project Navigator, right-click on
InboxReaderfolder - Choose "Add Files to InboxReader..."
- Select all
.swiftfiles from:InboxReader/Models/InboxReader/Views/InboxReader/Services/InboxReader/Shared/
- Make sure "Copy items if needed" is unchecked (files are already in place)
- Target:
InboxReader
- File → New → Target
- Choose Share Extension
- Settings:
- Product Name:
ShareExtension - Language: Swift
- Uncheck "Include UI Extension (Storyboard)"
- Product Name:
- When prompted "Activate ShareExtension scheme?" click Activate
- Delete the auto-generated
ShareViewController.swiftin the ShareExtension folder - Add existing files from
ShareExtension/folder:ShareViewController.swiftShareView.swift
- Target:
ShareExtension
The Share Extension needs access to the models and DataConfiguration.
-
Select
ReadingItem.swiftin Project Navigator -
In File Inspector (right panel), under "Target Membership", check both:
- ✅ InboxReader
- ✅ ShareExtension
-
Repeat for:
Schema.swiftDataConfiguration.swift
Note: Do NOT add ContentExtractor.swift or ContentProcessor.swift to the Share Extension - these use the Readability package which would exceed extension memory limits.
Both targets need access to the same data store.
-
Select the project in Navigator (blue icon at top)
-
Select InboxReader target
-
Go to "Signing & Capabilities" tab
-
Click "+ Capability" → App Groups
-
Click "+" and add:
group.com.inboxreader.shared -
Select ShareExtension target
-
Go to "Signing & Capabilities"
-
Add App Groups capability
-
Check the same group:
group.com.inboxreader.shared
Select ShareExtension/Info.plist and ensure it contains:
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsWebPageWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsText</key>
<true/>
</dict>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
<key>NSExtensionPrincipalClass</key>
<string>$(PRODUCT_MODULE_NAME).ShareViewController</string>
</dict>For the main app to check if OmniFocus is installed:
- Select
InboxReader/Info.plist(or Info tab in target settings) - Add key:
LSApplicationQueriesSchemes(Array) - Add item:
omnifocus
- Select project → General tab
- Set "Minimum Deployments" to iOS 17.0 for both targets
- Select an iPad Simulator (e.g., iPad Air)
- Build and run the main app (Cmd+R)
- To test Share Extension:
- Run the app once to install it
- Open Safari on the simulator
- Navigate to any webpage
- Tap Share → Select "InboxReader"
InboxReader/
├── InboxReader/
│ ├── InboxReaderApp.swift # App entry point + sidebar
│ ├── Models/
│ │ ├── ReadingItem.swift # SwiftData model
│ │ └── Schema.swift # VersionedSchema
│ ├── Views/
│ │ ├── InboxListView.swift # Inbox list + row + empty state
│ │ └── StatusListView.swift # Ideas/Reference/Trash views
│ ├── Services/
│ │ ├── ContentExtractor.swift # Readability wrapper
│ │ └── ContentProcessor.swift # Background extraction actor
│ └── Shared/
│ └── DataConfiguration.swift # App Group + ModelContainer
├── ShareExtension/
│ ├── ShareViewController.swift
│ ├── ShareView.swift
│ └── Info.plist
└── README.md
- User shares a URL via Share Extension
- Extension saves URL to SwiftData with
needsProcessing = true - Main app launches and calls
ContentProcessor.processQueue() - ContentProcessor extracts article using swift-readability
- Extracted content (title, HTML, text) saved to the ReadingItem
- Item displays in Inbox with extracted title
- Make sure both targets have the same App Group
- Check that the extension's Info.plist has correct NSExtension configuration
- Clean build folder (Cmd+Shift+K) and rebuild
- Verify App Group identifier matches exactly in both targets
- Check that shared files have both targets checked in Target Membership
- Add the model files to ShareExtension target membership
- Make sure you added the swift-readability local package (Step 2)
- The package is in
Packages/swift-readability- add it via File → Add Package → Add Local... - Only the main app target should import Readability, not the extension
- If using Xcode 15, edit
Packages/swift-readability/Package.swiftto use Swift 5.9
- Check network connectivity
- Some sites block automated requests
- Fallback to basic HTML fetch is attempted if Readability fails