Femte trin mod iOS-appen. Fladen fra trin 3 pakket ind i en rigtig app. - `app/`: Capacitor 8 med SPM (ingen CocoaPods). `npm run sync` bygger web med VERDANDE_TARGET=app og kopierer det ind i Xcode-projektet; `npm run open` åbner det på en Mac. Bundle-id `dk.nolimit.verdande`. - `app/ios/` er committet. Webbygget og den genererede konfiguration i det er ikke — sync lægger dem der. - `NSAllowsLocalNetworking`: en hjemmeserver uden certifikat kan nås med http på det lokale net; alt på internettet skal stadig være https. - Ikon og opstartsbillede tegnes fra web/static/icon.svg (scripts/generate-ios-assets.js). Kvadratisk og uden gennemsigtighed, fordi iOS runder hjørnerne selv. - CI-jobbet "iOS app shell" bygger og synker på Linux og tjekker, at app-bygget ikke registrerer en service worker. Ingen Xcode i CI; det dækker alt op til den. - docs/ios.md: hvordan appen adskiller sig fra en browser, hvordan den bygges, hvad der er ejerens skridt (Apple-konto, signering, TestFlight), og hvad der mangler — notifikationer via APNs venter på en beslutning om en relay. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
89 lines
3.6 KiB
Markdown
89 lines
3.6 KiB
Markdown
# The iOS app
|
|
|
|
The iOS app is the web interface bundled into a native shell with
|
|
[Capacitor](https://capacitorjs.com). It is not a second interface: the same
|
|
Svelte build runs in both, and everything that differs between a browser and the
|
|
app lives in `web/src/lib/platform.js`.
|
|
|
|
It talks to **any** Verdande server. The first screen asks for the address, checks
|
|
it with `/api/v1/ping`, and then signs in there.
|
|
|
|
## How the app differs from a browser
|
|
|
|
| | Browser | App |
|
|
|---|---|---|
|
|
| Where the interface comes from | the server | inside the app, `capacitor://localhost` |
|
|
| Which server | the one it was loaded from | the one its owner typed in |
|
|
| Session | httpOnly cookie | a session token, sent as `Authorization: Bearer` |
|
|
| Live sync | WebSocket with the cookie | WebSocket opened with a one-use ticket (`POST /ws-ticket`) |
|
|
| Pictures and downloads | plain `<img>` and links | fetched with the token and shown from a blob (`nativemedia.js`) |
|
|
| Service worker, Web Push | yes | no — WKWebView has neither |
|
|
| Passkeys | yes | not yet — a passkey is bound to the server's domain |
|
|
| Creating the first account | yes | no — done in a browser, then sign in from the app |
|
|
|
|
The server side of this is `client: "app"` on `/auth/login`, the
|
|
`capacitor://localhost` CORS rule, and the ticket. All three are in
|
|
`docs/openapi.yaml`.
|
|
|
|
An app session slides: every use moves its end forward, up to
|
|
`VERDANDE_APP_SESSION_TTL` (180 days) unused. It is listed under *Enheder* in
|
|
settings with the name the app gave itself, and can be signed out from there.
|
|
|
|
## Building
|
|
|
|
Everything up to Xcode runs anywhere, and runs in CI (`iOS app shell`):
|
|
|
|
```sh
|
|
cd app
|
|
npm ci
|
|
npm run sync # builds web with VERDANDE_TARGET=app, copies it into ios/
|
|
```
|
|
|
|
On a Mac with Xcode:
|
|
|
|
```sh
|
|
npm run open # opens ios/App/App.xcodeproj
|
|
```
|
|
|
|
Choose a simulator and press Run. In the app, type the address of a Verdande —
|
|
`http://localhost:8080` reaches a dev server on the same Mac from the simulator.
|
|
|
|
Plain `http` is allowed to **local** addresses only (`NSAllowsLocalNetworking` in
|
|
`Info.plist`), so a home server without a certificate works on the same network;
|
|
anything on the internet has to be `https`.
|
|
|
|
`app/ios/` is committed. The web build inside it (`App/App/public`) and the
|
|
generated config are not — `npm run sync` puts them there.
|
|
|
|
The icon and launch image come from `web/static/icon.svg`:
|
|
|
|
```sh
|
|
cd web && node scripts/generate-ios-assets.js
|
|
```
|
|
|
|
## Putting it on a phone
|
|
|
|
These steps are the account owner's — they involve Apple's terms, a payment and
|
|
signing keys.
|
|
|
|
1. **Apple Developer Program** membership (99 USD a year) on the Apple ID that
|
|
should own the app.
|
|
2. In Xcode → *Signing & Capabilities*: choose that team. The bundle id is
|
|
`dk.nolimit.verdande`; it can change until the first upload and never after.
|
|
3. *Product → Archive*, then *Distribute App → TestFlight*.
|
|
4. In App Store Connect, add testers to the TestFlight build.
|
|
|
|
## Not yet
|
|
|
|
- **Notifications.** Web Push does not exist in the app. Notifications on an
|
|
iPhone go through Apple's APNs, and the key that sends them is bound to the
|
|
app's bundle id — so it cannot live on every self-hosted server. That means a
|
|
small relay run by the app's publisher, which every instance with an app user
|
|
would talk to. That is a new connection from other people's servers, and it
|
|
waits on a decision before it is built.
|
|
- **The session token in the Keychain.** It is in the WebView's own storage for
|
|
now, behind one object in `platform.js` so the Keychain plugin can replace it
|
|
without anything else changing.
|
|
- **Passkeys** in the app.
|
|
- **Share sheet** (share text or a link *into* Verdande from another app).
|