156 lines
7.2 KiB
Markdown
156 lines
7.2 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`; 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.
|
|
|
|
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.
|
|
|
|
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 and an already-saved URL.
|
|
- 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.
|
|
|
|
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.
|
|
|
|
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.
|
|
- `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.
|
|
- Recent git history shows 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.
|