AIArticleDigest is a SwiftUI news reader portfolio app focused on practical app architecture:
- clean repository-based data flow
- SwiftData bookmarks
- mock/live runtime switching
- AI summary experience (mock and live-capable)
- stable UI automation with accessibility identifiers
- Articles tab with:
- search, pull-to-refresh, and recently viewed hint
- navigation to article detail
- stable row identifiers for UI testing
- Article detail screen with:
- hero thumbnail (when available)
- bookmark toggle and share actions
- in-app Safari for original article links
- AI summary modes (3-Line / Detailed) with loading, error, retry, and cache
- summary copy/share actions and toast feedback
- Bookmarks tab with:
- persisted bookmarks (SwiftData)
- swipe-to-delete support
- sort options (Newest / Oldest / Source A-Z)
- navigation to article detail
The app follows a lightweight layered architecture:
App/*: composition root (AppEnvironment) and runtime configurationCore/*: cross-cutting modules (AI, networking, persistence, UI helpers)Features/*: UI + ViewModels per feature
Key design points:
ArticleRepositoryandSummarizationServiceare protocol boundaries.AppEnvironmentbuilds concrete dependencies and injects them.BookmarkStateStore(@MainActor,@Observable) provides immediate UI sync, whileBookmarkRepositoryhandles persistence.- Runtime behavior is centralized in
RuntimeConfig(AIArticleDigestApp.swift).
The app supports mock/live switching via launch environment values.
| Mode | Articles | Summary | Required env vars |
|---|---|---|---|
| Mock (default) | MockArticleRepository |
MockSummarizationService |
None |
| Live articles only | RemoteArticleRepository (NewsAPI) |
MockSummarizationService |
USE_LIVE_API=1, NEWS_API_KEY |
| Live summary only | MockArticleRepository |
LiveSummarizationService (OpenAI) |
USE_LIVE_SUMMARIZATION=1, OPENAI_API_KEY |
| Fully live | RemoteArticleRepository (NewsAPI) |
LiveSummarizationService (OpenAI) |
USE_LIVE_API=1, NEWS_API_KEY, USE_LIVE_SUMMARIZATION=1, OPENAI_API_KEY |
OPENAI_SUMMARY_MODEL is optional (default: gpt-4o-mini).
FAIL_ONCE_SUMMARY=1
Forces the first summary request to fail once (DEBUG only), useful for retry verification.
- NewsAPI: Documentation
- OpenAI API: API Platform Docs
Runtime configuration is read from Scheme Environment Variables.
- Scheme environment variables are local developer settings and are not committed to this repository, so each developer must configure them manually.
- In Xcode, open Edit Scheme → Run → Arguments → Environment Variables.
- Add entries as needed (for example: Name
USE_LIVE_API, Value1). - Ensure each variable is enabled (checked).
| Name | Purpose | Example |
|---|---|---|
USE_LIVE_API |
Enable live NewsAPI article fetching | 1 |
NEWS_API_KEY |
NewsAPI key used by RemoteArticleRepository |
your_newsapi_key |
USE_LIVE_SUMMARIZATION |
Enable live OpenAI-based summary generation | 1 |
OPENAI_API_KEY |
OpenAI API key used by LiveSummarizationService |
your_openai_api_key |
OPENAI_SUMMARY_MODEL |
Optional override for summary model | gpt-4o-mini |
FAIL_ONCE_SUMMARY |
DEBUG helper: fail the first summary request once | 1 |
For UI tests, the launcher also uses:
| Name | Type | Purpose |
|---|---|---|
UITEST_IN_MEMORY_STORE |
Launch argument | Runs SwiftData with an in-memory store |
Do not commit real keys.
- Keep local secrets outside Git tracking (for example via Scheme Environment Variables).
Config/Secrets.xcconfigis ignored in.gitignore.- For public repos, never include real API keys in source/history.
If you prefer local key management with .xcconfig, you can wire it through your run scheme.
- Create a local
Secrets.xcconfig(not committed), for example:
NEWS_API_KEY=your_newsapi_key
OPENAI_API_KEY=your_openai_api_key
- In Xcode, open Edit Scheme → Run → Arguments → Environment Variables.
- Add entries like:
- Name:
NEWS_API_KEY, Value:$(NEWS_API_KEY) - Name:
OPENAI_API_KEY, Value:$(OPENAI_API_KEY)
- Name:
- Ensure each variable is enabled (checked).
Covered areas include:
RemoteArticleRepositoryDTO mapping and filtering behaviorArticleDetailViewModelsummary state transitionsBookmarksViewModelsort behaviorRecentArticleStorepersistence behaviorBookmarkStateStorestate transitionsMockSummarizationServicebehavior
Main flow and empty bookmarks flow are covered.
UI tests use accessibilityIdentifier-based selectors to reduce brittle text matching.
The UI test launcher uses:
UITEST_IN_MEMORY_STORElaunch argument
to run with an in-memory SwiftData store.
- Live article data quality depends on the upstream NewsAPI response. Some items may not include image URLs or complete metadata.
- Live summary requires a valid OpenAI API key and network connectivity.
- API rate limits or provider errors can temporarily affect live article fetching or summary generation.
- Mock mode is intended for development and demonstration; it does not represent real-time news updates.
- Xcode 26.x
- iOS Simulator 26.x
- Swift 6 (as configured by project settings)
MIT. See LICENSE.


