Files
linkding-ios/openwiki/operations/development-and-testing.md
Krishna Kumar f7735431a8
All checks were successful
CI / build-and-deploy (push) Successful in 16s
Update OpenWiki for source ingest
2026-07-02 01:20:18 -05:00

171 lines
8.6 KiB
Markdown

# Development and Testing
This page covers local setup, generated project guidance, runtime configuration/storage, and verification strategy for changes.
## Local setup
Requirements from `/README.md` and `/project.yml`:
- macOS with Xcode capable of building the declared iOS 26 / Swift 6 project.
- [XcodeGen](https://github.com/yonaskolb/XcodeGen).
- A linkding server with API access for real sync testing.
- Access to the Marks backend for AI, semantic search, smart collections, analytics, and podcast generation flows.
Setup:
```bash
brew install xcodegen
xcodegen generate
open Marks.xcodeproj
```
Build and run the `Marks` scheme in Xcode. `/project.yml` declares schemes for `Marks`, `MarksWidget`, and `ShareExtension`; the `Marks` scheme builds the app, share extension, and widget extension and runs `MarksTests` in the test action.
## Project generation rules
Use `/project.yml` as the source of truth for targets, source membership, entitlements, and dependencies. Regenerate the Xcode project after changing target membership:
```bash
xcodegen generate
```
Common target-membership traps:
- The main app includes `/MarksWidget/WidgetDataStore.swift` directly so it can write widget JSON.
- The share extension includes selected shared files from `/Marks/Models` and `/Marks/Services`, including `IngestPayload.swift` for URL extraction; if the extension needs new model/service code, add it in `/project.yml`.
- The app, widget, and share extension all depend on matching app-group entitlements.
## Testing
Current automated test coverage is concentrated in `/MarksTests/AppIntentsTests.swift`.
Covered today:
- `BookmarkEntity` mapping from `Bookmark`, fallback title behavior, and round-trip fields.
- `EntityIdentifier` encoding.
- `IntentRouter` open/search setters.
- Direct no-network `perform()` tests for Search and Open intents.
- App Shortcuts registration count.
- `IngestPayloadParser` URL extraction from direct URLs, plain text, HTML-ish text, and notes.
- `IngestedSourceStore` save/load behavior plus plain-text file import into a temporary root.
Not currently covered by repository tests:
- `BookmarksViewModel` load/cache/pagination/mutation flows.
- Linkding networking with mocks.
- AI enrichment, semantic search, smart collections, or podcast backend calls.
- Share extension save/update/tag suggestion flows.
- Widget timeline rendering/deep links.
- Browser reading progress/reader mode.
- Podcast generation, playback, queue, played/unplayed, and background audio behavior.
- Sources tab UI, QuickLook presentation, PDF extraction edge cases, and source-backed podcast backend behavior.
Suggested test command:
```bash
xcodebuild test -scheme Marks -destination 'platform=iOS Simulator,name=iPhone 16'
```
Use an installed simulator name if `iPhone 16` is unavailable.
## Manual verification checklist by area
Bookmark sync/list changes:
- Launch with valid linkding config.
- Confirm cached bookmarks render quickly, then network refresh updates the list.
- Test pull-to-refresh, pagination, unread filter, add, edit, archive, delete.
- Confirm widget bookmark JSON updates and Spotlight indexing still happens.
Search/tag changes:
- Test linkding search and semantic search separately.
- Verify unread filter interactions.
- Confirm Tags tab counts include expected linkding and AI tags.
AI changes:
- Test enrichment on add, background enrichment, Enrich All, semantic search, Smart Collections, and Summarize intent if affected.
- Test Ask on a device/simulator environment where Apple on-device model availability can be observed; handle unavailable states.
- Preserve backend privacy expectations: URLs/titles/tags/summaries may be sent to the Marks backend for backend AI paths.
Share extension changes:
- Share a new URL, an already-saved URL, and text/HTML/RTF content that contains a URL plus notes.
- Verify duplicate-aware update behavior.
- Test linkding auto-tags, AI suggestion chips, tag parsing, notes, read-later, and Create Podcast.
- Confirm the extension works when shared config is missing; current UI shows an "Open Marks first" style failure.
Source ingest changes:
- Import typed text with title/tags and confirm it appears in Sources search.
- Import a text file and a PDF through the file importer; confirm originals copy into Application Support and QuickLook opens imported files.
- Confirm PDF text extraction works for text PDFs and handles image-only PDFs gracefully.
- Delete a source and confirm metadata, copied file, and Spotlight entry are removed.
- Create a podcast from a source and confirm cached playback uses the same `marks-source://` entry.
Podcast changes:
- Generate from bookmark row and Browser view.
- Start generation while another podcast is playing; confirm background generation does not interrupt playback.
- Test queue playback, played/unplayed state, delete, sleep timer, saved speed, background audio, remote controls, now-playing metadata, relaunch restore, and widget recent podcasts.
- Share a URL with Create Podcast and confirm the main app drains and starts the queued request.
- Generate from a local text/PDF source and confirm backend support for `text` and `source_kind`.
Widget/deep-link changes:
- Confirm `widget_bookmarks.json` and `widget_podcasts.json` are refreshed by main-app writes.
- Test Recent Bookmarks, Random Bookmark, and Recent Podcasts widgets.
- Tap bookmark and podcast widget entries and verify `marks://bookmark` and `marks://podcast` routing in `MainContainer`.
App Intents changes:
- Run `MarksTests`.
- Test Shortcuts/Siri flows for Add Bookmark, Search, Show Unread, Summarize, and Open Bookmark.
- Verify entity queries still fetch/resolve bookmarks correctly with a configured linkding server.
## Configuration and persistence map
Do not read or document live secret values.
Main app / standard defaults:
- `serverConfig` — linkding config saved by `ServerConfig.save()`.
- `marksDeviceId`, `marksJWT`, `marksJWTExpiry` — Marks backend auth state from `MarksAuth`.
- `aiData` — AI summaries/tags by bookmark ID.
- `aiSummaryByURL` — summary mirror used outside the main bookmark array.
- `podcastProgress`, `podcastPlaybackSpeed`, `podcastSession` — podcast playback state.
- `readingProgress` — in-browser reading progress.
App-group defaults/files (`group.com.magicive.marks`):
- `serverConfig` — shared linkding config for extensions.
- `pendingPodcastRequests` — share extension to main app podcast queue.
- `podcastAutoTags` — tags that auto-enable podcast creation in the share extension.
- `widget_bookmarks.json` — recent bookmark widget cache.
- `widget_podcasts.json` — recent podcast widget cache.
Application Support files:
- `bookmarks_cache.json` or `bookmarks_{host}.json` — cached bookmarks.
- `Sources/sources.json` — local source metadata.
- `Sources/files/{uuid}.{ext}` — copied originals for imported PDFs/files.
- `podcasts/{hash}.mp3` — generated podcast audio.
- `podcasts/index.json` — podcast metadata and played state.
## Known caveats and stale-doc risks
- `/Marks/MarksApp.swift` contains a built-in default linkding configuration with a token-like value. Treat it as sensitive and consider replacing it with a safer onboarding/configuration flow before distribution.
- `OnboardingView` exists but is not the active first-launch path in `MarksApp` as currently written.
- Disconnect behavior currently resets via the default config callback path instead of clearly deleting config and returning to onboarding.
- README AI provider text appears older than source: current backend AI/podcast features go through `MarksAuth` + `ClaudeService`, while Ask uses Apple's on-device `FoundationModels`.
- `MarksAuth` stores backend JWT/device state in `UserDefaults`, not Keychain.
- App-group ID strings are duplicated in code and entitlements; update all occurrences together if changing it.
- Share-extension podcast queue draining clears requests before generation success, so crash/retry semantics deserve care.
- Source-backed podcast generation sends extracted text to the Marks backend; if the backend only accepts URL fetches, local source podcasts will fail even though bookmark podcasts still work.
- Recent git history shows source ingest, podcast, and on-device AI features were added across many files; avoid narrow changes that ignore cross-target data flow.
## Existing design guidance
`/docs/design-wwdc26.md` is a useful source for UI direction. It emphasizes native iOS 26 UI/Liquid Glass behavior, editorial bookmark rows as the brand canvas, Dynamic Type-safe row layout, bottom/reachable search patterns, WidgetKit app-group data, and standard platform affordances. Link to it rather than duplicating the full design brief.