rename: Jarvis -> Jervis target, scheme, and folder throughout
The bundle-ID and display-name renames weren't enough — the actual
Xcode target/product name was still "Jarvis", which drives CFBundleName,
Xcode's Organizer archive list, the .xcodeproj filename, and the scheme
name. Renamed all the way through:
- project.yml: top-level name, target key, PRODUCT_NAME, source/info paths
- Jarvis/ -> Jervis/ (source folder, git-tracked as renames, no content
diffs on the moved files)
- .gitignore, CI workflows, README, CONTRIBUTING: Jarvis.xcodeproj /
scheme Jarvis -> Jervis.xcodeproj / scheme Jervis
Verified: xcodebuild -scheme Jervis succeeds, produces Jervis.app,
CFBundleName/CFBundleExecutable/CFBundleDisplayName all read "Jervis".
CFBundleIdentifier intentionally stays com.kisani.jarvis (per prior
decision — bundle ID isn't user-facing anywhere including Organizer).
Swift type names (JarvisApp, JarvisWordmark, etc.) and the Gitea repo
name/URL are unchanged — pure internal source identifiers, not user or
developer-facing product identity.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 03:49:31 +03:00
|
|
|
|
# Jervis (iOS)
|
2026-06-18 18:04:59 +03:00
|
|
|
|
|
|
|
|
|
|
A SwiftUI iOS client for a self-hosted RSS **news-correlation** platform. It shows
|
|
|
|
|
|
stories ranked by a server-computed **signal score**, caches full articles for offline
|
|
|
|
|
|
reading, and receives live updates over WebSocket.
|
|
|
|
|
|
|
|
|
|
|
|
> This repo is the **iOS app only**. The backend (RSS ingest, clustering, scoring,
|
|
|
|
|
|
> REST + WebSocket) is a separate service — see [`docs/backend/`](docs/backend/) for the
|
|
|
|
|
|
> build/handoff spec and [`API/contract.md`](API/contract.md) for the wire contract.
|
|
|
|
|
|
|
|
|
|
|
|
## Architecture
|
|
|
|
|
|
|
|
|
|
|
|
- **REST on launch → WebSocket for live updates.** The server computes signal scores;
|
|
|
|
|
|
the app only renders them.
|
|
|
|
|
|
- **Offline-first** via SwiftData (`CachedStory`, `CachedArticle`).
|
|
|
|
|
|
- **No auth** — local network / self-hosted (reach it over LAN or Tailscale).
|
|
|
|
|
|
- Data flows through `StoryStore` / dedicated view-models; views never call the API
|
|
|
|
|
|
directly.
|
|
|
|
|
|
|
|
|
|
|
|
```
|
rename: Jarvis -> Jervis target, scheme, and folder throughout
The bundle-ID and display-name renames weren't enough — the actual
Xcode target/product name was still "Jarvis", which drives CFBundleName,
Xcode's Organizer archive list, the .xcodeproj filename, and the scheme
name. Renamed all the way through:
- project.yml: top-level name, target key, PRODUCT_NAME, source/info paths
- Jarvis/ -> Jervis/ (source folder, git-tracked as renames, no content
diffs on the moved files)
- .gitignore, CI workflows, README, CONTRIBUTING: Jarvis.xcodeproj /
scheme Jarvis -> Jervis.xcodeproj / scheme Jervis
Verified: xcodebuild -scheme Jervis succeeds, produces Jervis.app,
CFBundleName/CFBundleExecutable/CFBundleDisplayName all read "Jervis".
CFBundleIdentifier intentionally stays com.kisani.jarvis (per prior
decision — bundle ID isn't user-facing anywhere including Organizer).
Swift type names (JarvisApp, JarvisWordmark, etc.) and the Gitea repo
name/URL are unchanged — pure internal source identifiers, not user or
developer-facing product identity.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 03:49:31 +03:00
|
|
|
|
Jervis/
|
2026-06-18 18:04:59 +03:00
|
|
|
|
Models/ Codable + SwiftData models
|
|
|
|
|
|
Networking/ APIClient (REST), WebSocketManager
|
|
|
|
|
|
Store/ StoryStore, ServerSettings
|
|
|
|
|
|
Connectivity/ ConnectivitySettings, ConnectivityManager (LAN ⇄ Tailscale)
|
|
|
|
|
|
Views/
|
|
|
|
|
|
Home/ Story/ Reader/ Feeds/ Connectivity/ Shared/
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Requirements
|
|
|
|
|
|
|
|
|
|
|
|
- Xcode 16+ (developed on Xcode 26), iOS 17.0+ deployment target (SwiftData).
|
|
|
|
|
|
- [`XcodeGen`](https://github.com/yonaskolb/XcodeGen) — the `.xcodeproj` is generated
|
|
|
|
|
|
from [`project.yml`](project.yml) and is **not** committed.
|
|
|
|
|
|
|
|
|
|
|
|
## Build & run
|
|
|
|
|
|
|
|
|
|
|
|
```bash
|
|
|
|
|
|
brew install xcodegen # once
|
rename: Jarvis -> Jervis target, scheme, and folder throughout
The bundle-ID and display-name renames weren't enough — the actual
Xcode target/product name was still "Jarvis", which drives CFBundleName,
Xcode's Organizer archive list, the .xcodeproj filename, and the scheme
name. Renamed all the way through:
- project.yml: top-level name, target key, PRODUCT_NAME, source/info paths
- Jarvis/ -> Jervis/ (source folder, git-tracked as renames, no content
diffs on the moved files)
- .gitignore, CI workflows, README, CONTRIBUTING: Jarvis.xcodeproj /
scheme Jarvis -> Jervis.xcodeproj / scheme Jervis
Verified: xcodebuild -scheme Jervis succeeds, produces Jervis.app,
CFBundleName/CFBundleExecutable/CFBundleDisplayName all read "Jervis".
CFBundleIdentifier intentionally stays com.kisani.jarvis (per prior
decision — bundle ID isn't user-facing anywhere including Organizer).
Swift type names (JarvisApp, JarvisWordmark, etc.) and the Gitea repo
name/URL are unchanged — pure internal source identifiers, not user or
developer-facing product identity.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 03:49:31 +03:00
|
|
|
|
xcodegen generate # produces Jervis.xcodeproj
|
|
|
|
|
|
open Jervis.xcodeproj # ⌘R in Xcode, or:
|
2026-06-18 18:04:59 +03:00
|
|
|
|
|
rename: Jarvis -> Jervis target, scheme, and folder throughout
The bundle-ID and display-name renames weren't enough — the actual
Xcode target/product name was still "Jarvis", which drives CFBundleName,
Xcode's Organizer archive list, the .xcodeproj filename, and the scheme
name. Renamed all the way through:
- project.yml: top-level name, target key, PRODUCT_NAME, source/info paths
- Jarvis/ -> Jervis/ (source folder, git-tracked as renames, no content
diffs on the moved files)
- .gitignore, CI workflows, README, CONTRIBUTING: Jarvis.xcodeproj /
scheme Jarvis -> Jervis.xcodeproj / scheme Jervis
Verified: xcodebuild -scheme Jervis succeeds, produces Jervis.app,
CFBundleName/CFBundleExecutable/CFBundleDisplayName all read "Jervis".
CFBundleIdentifier intentionally stays com.kisani.jarvis (per prior
decision — bundle ID isn't user-facing anywhere including Organizer).
Swift type names (JarvisApp, JarvisWordmark, etc.) and the Gitea repo
name/URL are unchanged — pure internal source identifiers, not user or
developer-facing product identity.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-07-13 03:49:31 +03:00
|
|
|
|
xcodebuild -project Jervis.xcodeproj -scheme Jervis \
|
2026-06-18 18:04:59 +03:00
|
|
|
|
-sdk iphonesimulator -destination 'platform=iOS Simulator,name=iPhone 17' build
|
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
## Connecting to a backend
|
|
|
|
|
|
|
|
|
|
|
|
On first launch, enter the server as `host:port` (the app prepends `http://` and
|
|
|
|
|
|
`ws://`), e.g. `10.10.1.70:8098`. Remote access via Tailscale is configured in the
|
|
|
|
|
|
in-app **Connectivity → Remote Access** card; the app auto-selects the LAN address when
|
|
|
|
|
|
it's reachable and the Tailscale address when away.
|
|
|
|
|
|
|
|
|
|
|
|
## CI
|
|
|
|
|
|
|
|
|
|
|
|
GitHub Actions builds a matrix of macOS runners × Debug/Release on every push and PR —
|
|
|
|
|
|
see [`.github/workflows/ci.yml`](.github/workflows/ci.yml).
|
|
|
|
|
|
|
|
|
|
|
|
## Status
|
|
|
|
|
|
|
|
|
|
|
|
Frontend complete and verified against a live backend (signal feed, story detail,
|
|
|
|
|
|
article reader, feed manager, connectivity). Open work is tracked in
|
|
|
|
|
|
[Issues](../../issues) / [`docs/BACKLOG.md`](docs/BACKLOG.md).
|