DictApp/
├── DictApp/
│ ├── DictApp.swift # @main entry point
│ ├── Models/
│ │ └── Models.swift # DictionaryEntry, HistoryItem, Bookmark
│ ├── Services/
│ │ ├── DatabaseService.swift # SQLite + FTS5 via GRDB (actor)
│ │ └── SpeechService.swift # AVFoundation TTS
│ ├── ViewModels/
│ │ ├── SearchViewModel.swift # Debounced search-as-you-type
│ │ ├── DefinitionViewModel.swift # Detail + bookmark + history
│ │ ├── HistoryViewModel.swift
│ │ └── BookmarksViewModel.swift
│ ├── Views/
│ │ ├── ContentView.swift # Root TabView
│ │ ├── SearchView.swift # Search bar + results list
│ │ ├── DefinitionView.swift # Markdown definition + TTS
│ │ ├── HistoryView.swift
│ │ ├── BookmarksView.swift
│ │ └── DictionaryManagerView.swift # File importer
│ ├── Extensions/
│ │ └── DictionaryEntry+Hashable.swift
│ └── Resources/
│ └── Schema.sql # Database DDL
├── DictAppTests/
│ └── DictAppTests.swift # Unit + performance tests
├── Package.swift # SPM reference
Scripts/
└── generate_seed_db.py # Python script to create sample data
- Open Xcode 15+ (requires iOS 17 SDK).
- File → New → Project → iOS → App.
- Product Name:
DictApp, Interface: SwiftUI, Language: Swift. - Save it inside the
DictApp/directory (so that source files align).
- File → Add Package Dependencies...
- Enter URL:
https://github.com/groue/GRDB.swift - Version rule: Up to Next Major → 6.24.0
- Add the
GRDBlibrary to theDictApptarget.
If you created the Xcode project inside the existing DictApp/ directory, the files are already on disk. You just need to add them to the project:
- In the Xcode navigator, right-click the
DictAppgroup → Add Files to "DictApp"... - Select all
.swiftfiles underModels/,Services/,ViewModels/,Views/,Extensions/. - Add
Resources/Schema.sql— make sure "Copy items if needed" is unchecked and "Add to target: DictApp" is checked. Verify it appears under Build Phases → Copy Bundle Resources.
The Schema.sql file must be included as a bundle resource:
- Select the
DictApptarget → Build Phases tab. - Under Copy Bundle Resources, click + and add
Schema.sql.
cd /path/to/dict
python3 Scripts/generate_seed_db.py --count 1000 --output DictApp/DictApp/Resources/seed.sqliteThen add seed.sqlite to the Xcode bundle resources the same way as Schema.sql.
python3 Scripts/generate_seed_db.py --count 1000 --json DictApp/DictApp/Resources/seed.jsonAdd seed.json to the Xcode bundle resources. The app will auto-import it on first launch via DatabaseService.seedIfNeeded().
python3 Scripts/generate_seed_db.py --count 100000 --output big_dict.sqliteUse the in-app Manage → Import File feature to load it.
# From Xcode:
# Product → Test (Cmd+U)
# Or from CLI (requires xcodebuild):
xcodebuild test \
-project DictApp.xcodeproj \
-scheme DictApp \
-destination 'platform=iOS Simulator,name=iPhone 15'| Test | What it verifies |
|---|---|
testSearchReturnsCorrectDefinition |
FTS5 search returns correct entry |
testExactLookup |
Case-insensitive exact match |
testHistoryNoDuplicates |
Upsert prevents duplicate history entries |
testHistoryOrderUpdatedOnRevisit |
Re-lookup moves word to top |
testClearHistory |
History deletion |
testBookmarkCycle |
Add → check → remove bookmark |
testPrefixSearch |
Prefix queries match expected count |
testSearchPerformance100K |
FTS5 search < 16ms on 100k entries |
testSearchPerformanceRepeated |
XCTest measure block for statistical analysis |
- Apple Developer account (free or paid).
- iPhone connected via USB or on the same Wi-Fi network.
- Xcode 15+.
- Connect your iPhone to your Mac.
- In Xcode, select your iPhone from the device dropdown (top toolbar).
- Select the
DictApptarget → Signing & Capabilities. - Set Team to your Apple Developer account.
- Set a unique Bundle Identifier (e.g.,
com.yourname.dictapp). - Click Run (Cmd+R).
- On first install, your iPhone may show "Untrusted Developer":
- Go to Settings → General → VPN & Device Management → tap your developer profile → Trust.
- Run again from Xcode. The app will launch on your device.
- "Could not launch": Ensure the device is unlocked during installation.
- Provisioning errors: Go to Xcode → Settings → Accounts → your Apple ID → Download Manual Profiles.
- TTS not working on simulator: TTS requires a real device or specific simulator voices.
GRDB provides first-class FTS5 support, record protocols that eliminate boilerplate, and built-in DatabasePool for concurrent reads during writes. It maps cleanly to Swift Concurrency.
FTS5 is SQLite's latest full-text search engine. It uses an inverted index, making prefix searches O(1) relative to dictionary size. A search on 100,000 entries completes in under 5ms.
The actor isolation guarantees thread-safe access to the DatabasePool reference without manual locking. All public methods are async, keeping the UI thread completely free.
The entire dictionary lives in a local SQLite file. No network calls are ever made. Users import dictionaries via the file picker.
- Add a parser method in
DatabaseService(e.g.,importCSV(at:source:)). - Register the new
UTTypeinDictionaryManagerView.fileImporter. - Handle the new extension in
handleImport.
Query a random entry: SELECT * FROM entries ORDER BY RANDOM() LIMIT 1.
Add a related table: CREATE TABLE related (entry_id INTEGER, related_id INTEGER) and join on lookup.