---
title: "Memory.Wiki iOS — Spec"
url: https://memory.wiki/fD70bEXD
updated: 2026-05-27T18:25:21.545Z
hub: https://memory.wiki/hub/raymindai
concept_count: 12
source: "mcp"
---

# Memory.Wiki iOS — Spec

> **Status (2026-05-28)**: W9 scaffold + brand parity + Supabase auth + Apple/Google/GitHub/Email providers shipped. Real OAuth wire-up live (Supabase dashboard configured). Share Extension, Camera, Widget, Spotlight, background sync still queued for W9–W12.

> **Repo path**: `apps/ios-native/` &nbsp;·&nbsp; **Bundle ID**: `wiki.memory.MemoryWiki` &nbsp;·&nbsp; **URL scheme**: `memorywiki://` &nbsp;·&nbsp; **Deployment target**: iOS 17

---

# 1. North star

iOS native companion to memory.wiki. The web is the canonical surface for the URL architecture (`/<id>` doc, `/b/<id>` bundle, `/@<slug>` profile); iOS gives the user a fast capture path from any other app on their phone and a polished reader for their own timeline. The product story stays the same: **one URL every AI can read.**

The iOS app must read as a continuation of memory.wiki — same fonts, same colors, same micro-affordances. Not a "mobile version of a web app." A native surface that happens to share the brand exactly.

---

# 2. Scope

## In (W9–W12)

| Surface | Status |
| --- | --- |
| App shell (Timeline / Capture / Profile) | done 1adec1e5 |
| Brand system (Cal Sans, JetBrains Mono, Noto Sans, dark zinc, lime micro-accent) | done |
| Auth — Apple, Google, GitHub, Email via Supabase SDK | done 2386944e |
| Animated brand blob (WKWebView, same SVG as web) | done 1adec1e5 |
| Share Extension (any app → Memory.Wiki) | queued |
| Camera + screenshot capture (OCR) | queued |
| Widget (home + lock screen) | queued |
| Spotlight indexing | queued |
| Background sync | queued |
| Push notifications | queued |

## Out (post-v8 / never)

- Voice memo capture (low Korean cultural fit, scope creep)
- Live Activities, Action Extension, custom keyboard (v8.1 at earliest)
- Built-in chat with an AI (conflicts with cross-AI thesis)
- Mac Catalyst (use the web)

---

# 3. URL architecture (shared with web)

The iOS app uses the existing API surface. No iOS-specific endpoints.

| Memory.Wiki URL | iOS open behaviour |
| --- | --- |
| `https://memory.wiki/<id>` | viewer (in-app) for own docs, ShareLink for outbound |
| `https://memory.wiki/@<slug>` | the "use" surface — shown on Profile tab with **Copy for AI** |
| `memorywiki://auth-callback` | OAuth redirect handled by `AuthManager.handleCallback` |
| `memorywiki://...` (future) | Universal Link target after apple-app-site-association lands |

---

# 4. Brand system (parity)

| Token | Value | Notes |
| --- | --- | --- |
| `Brand.background` | `#09090B` (zinc-950) | App canvas. Forced via `UIUserInterfaceStyle: Dark`. |
| `Brand.surface` | `#18181B` | Card / row fill. |
| `Brand.borderDim` | `rgba(39,39,42,0.6)` | Hairline. |
| `Brand.textPrimary` | `#FAFAFA` | Body + headings. |
| `Brand.textFaint` | `#8A8A91` | Captions, mono labels. |
| `Brand.accent` | `#B5FF1A` (lime) | **Micro-color only** — dots, badges. Never as button fill / chip fill / body text per the v8 color-balance rule. |
| `Brand.display(size:)` | Cal Sans Regular | Display headings + the wordmark. |
| `Brand.body(size:weight:)` | Noto Sans 400/500/600 | All prose. |
| `Brand.mono(size:weight:)` | JetBrains Mono 400/500 | Captions, URLs, numeric data. |

### Logo

`MemoryWikiLogo` ports `apps/web/src/components/MemoryWikiLogo.tsx`:

- Inline lockup: mark left + `memory.wiki` (lowercase) wordmark right
- Mark scales `size × 1.55` ≤30pt, `size × 1.25` >30pt
- Mark = `AnimatedBlob`: bundled `mwblob_morph.svg` (web's morph SVG) inside a transparent `WKWebView` so the 10.867s breathing animation lives — asset catalog SVG renderer strips `<animate>`

### Rule list (locked from memory.wiki/SKaY7VJP)

- No em-dash / middle-dot / emoji in UI strings
- Quiet pickers: `--surface` lift + ink check, no colored fills
- Selected state never uses lime border
- "Sign in" buttons are ink, not lime (color-balance)

---

# 5. Project layout

```
apps/ios-native/
├── project.yml                      # XcodeGen spec, single source of truth
├── Config/
│   ├── Secrets.xcconfig.example     # checked in
│   └── Secrets.xcconfig              # gitignored — Supabase URL + anon key
├── MemoryWiki/
│   ├── MemoryWikiApp.swift          # @main, onOpenURL → AuthManager
│   ├── BrandTheme.swift             # color tokens + font helpers + QuietButtonStyle
│   ├── MemoryWiki.entitlements      # com.apple.developer.applesignin
│   ├── Info.plist                   # xcodegen-rewritten; do not hand-edit
│   ├── Assets.xcassets/             # brand SVGs (logo-full, icon-app, mwblob static fallback) + Providers/{google,github}
│   ├── Fonts/                       # CalSans, JetBrainsMono, NotoSans TTFs
│   ├── Resources/                   # mwblob_morph.svg (animated, WKWebView consumer)
│   ├── Models/Document.swift
│   ├── Networking/
│   │   ├── SupabaseConfig.swift     # shared SupabaseClient from xcconfig
│   │   ├── AuthManager.swift        # @MainActor session + 4-provider sign-in
│   │   └── APIClient.swift          # REST wrappers around /api/user/documents + /api/docs
│   └── Views/
│       ├── RootView.swift           # custom TabBar (Timeline / Capture / Profile)
│       ├── AuthView.swift           # 4-provider sign-in with brand surfaces
│       ├── EmailAuthSheet.swift     # half-sheet, sign in / create-account toggle
│       ├── TimelineView.swift       # bordered card list of /api/user/documents
│       ├── CaptureView.swift        # fast textarea → POST /api/docs
│       ├── ProfileView.swift        # the use surface: hub URL + Copy for AI
│       ├── MemoryWikiLogo.swift     # canonical inline mark + wordmark
│       └── AnimatedBlob.swift       # WKWebView host for the morph SVG
├── scripts/
│   └── generate-apple-client-secret.sh   # ES256 JWT generator (Apple sign-in secret)
└── README.md
```

---

# 6. Auth model

Supabase-iOS SDK is the canonical client. The SDK persists the session in the Keychain automatically; `AuthManager` is a thin `@MainActor` `ObservableObject` over it that publishes a `UserSession` value type for SwiftUI to drive UI off.

| Provider | Flow | Notes |
| --- | --- | --- |
| Apple | `ASAuthorizationController` → `signInWithIdToken(.apple, idToken:nonce:)` | Required by App Store guideline 4.8 when offering social SSO. Nonce: random URL-safe string + SHA-256 sent in `request.nonce`. |
| Google | `signInWithOAuth(.google)` → `ASWebAuthenticationSession` | Real multi-color G logo, not SF Symbol. |
| GitHub | `signInWithOAuth(.github)` → `ASWebAuthenticationSession` | Real Octocat. |
| Email | `signIn(email:password:)` / `signUp(...)` | Native form in `EmailAuthSheet`. |

### Apple client secret rotation

Apple caps the client-secret JWT lifetime at ~6 months. `.p8` private key never expires; the JWT signed with it does.

`scripts/generate-apple-client-secret.sh` shells out to `openssl` + `python3` to produce the ES256 JWT (Apple's spec — ASN.1 DER signature unpacked to raw R‖S):

```bash
./scripts/generate-apple-client-secret.sh \
  --p8 ~/Downloads/AuthKey_XXXXXXXXXX.p8 \
  --team-id XXXXXXXXXX \
  --key-id XXXXXXXXXX \
  --client-id wiki.memory.MemoryWiki.signinservice
```

Paste output into Supabase dashboard → Authentication → Providers → Apple → **Secret Key (for OAuth)**. Re-run 1 week before expiry. Current expiry: **2026-11-25**.

### Crash already fixed

`ASWebAuthenticationPresentationContextProviding.presentationAnchor` is `nonisolated` per protocol; the early implementation called `DispatchQueue.main.sync` from the main thread inside that method, which is an instant deadlock. Replaced with `MainActor.assumeIsolated` (the correct pattern — ASKit guarantees the call is on main, we just need to read window state without dispatching).

---

# 7. Networking

`APIClient` is a hand-written `URLSession` wrapper around the existing web API. Identity headers (`Authorization: Bearer <session.accessToken>` + `x-user-id` + `x-user-email`) are pulled from `AuthManager.shared.session` on every call so a sign-in elsewhere in the app picks up without recreating the client.

Endpoints in use today:

- `GET /api/user/documents` — Timeline list
- `POST /api/docs` — Capture write (source: `ios`)

Endpoints used implicitly via Supabase SDK:

- `/auth/v1/*` — sign in / sign up / OAuth
- `profiles` table SELECT for `hub_slug` lookup (RLS gated)

The doc / bundle / `@username` URLs are NOT fetched as JSON — they're shared via `ShareLink` (URL out) or rendered inside an in-app browser later. Dual-response on the server side means any AI client paste-fetching them gets clean markdown automatically.

---

# 8. Build & run

```bash
# one-time setup
brew install xcodegen

# regenerate the project file (after editing project.yml or
# adding / removing source files)
cd apps/ios-native
xcodegen generate

# resolve SPM packages once
xcodebuild -resolvePackageDependencies \
  -project MemoryWiki.xcodeproj -scheme MemoryWiki

# build for the simulator
xcodebuild -project MemoryWiki.xcodeproj -scheme MemoryWiki \
  -destination 'platform=iOS Simulator,name=iPhone 17' \
  -configuration Debug -derivedDataPath build build

# open in Xcode (recommended day-to-day)
open MemoryWiki.xcodeproj
```

`Config/Secrets.xcconfig` must exist before the build will compile — copy `Config/Secrets.xcconfig.example` and fill in the values from `apps/web/.env.local`.

---

# 9. Out-of-repo prerequisites

Done (founder):

- **Supabase**: project URL + anon key in `Config/Secrets.xcconfig`; `memorywiki://auth-callback` allow-listed in Auth → URL Configuration → Redirect URLs; Apple / Google / GitHub providers turned on
- **Apple Developer**: App ID `wiki.memory.MemoryWiki` with Sign in with Apple capability; Service ID `wiki.memory.MemoryWiki.signinservice` bound to the Supabase callback; `.p8` key generated, stored in `~/Downloads/` (gitignored, not in repo); Supabase Apple-provider JWT generated (expires 2026-11-25)
- **Xcode**: Team selected under Signing & Capabilities; Sign in with Apple capability added (writes the entitlement file the spec already commits)

Pending:

- **Web `/auth`**: accept `?provider=google|github|email` so iOS's `signInWithOAuth(provider:)` jumps straight into the provider's flow without bouncing through the chooser
- **Apple Universal Links**: ship `apple-app-site-association` on `memory.wiki/.well-known/` once the team ID is finalised so `https://memory.wiki/<id>` opens the iOS app on tap
- **TestFlight**: first build push for on-device validation of the Apple flow (simulator can render the button but can't complete the iCloud sign-in)

---

# 10. Current verification

`xcodebuild -destination 'iOS Simulator' build` → **BUILD SUCCEEDED**

Fresh-install launch via `xcrun simctl install / launch`:

- Dark zinc canvas paints at first frame (no white flash)
- Brand lockup renders with the morph blob actually breathing (verified by 3 sequential screenshots showing shape change)
- 4 provider buttons render with real logos (Apple system button, multi-color Google G, Octocat, envelope)
- Google button taps now open `ASWebAuthenticationSession` cleanly (previous nonisolated deadlock fixed)

---

# 11. What's next

Next 3 commits (in order):

1. **Web `/auth?provider=`** — accept the param + redirect into the requested provider directly. Unblocks Google / GitHub real round-trip.
2. **Share Extension target** — `ShareViewController` with App Group glue, posts the captured URL/text to a tiny on-device queue the main app drains on launch. Lets the user grab anything from anywhere → `memory.wiki/<id>` in two taps.
3. **Widget (home screen, small)** — last 3 captures + a "+ Capture" button that deep-links into the Capture tab.

After that: camera + OCR, Spotlight, background sync, push, then channel updates (Chrome / VSCode / Desktop / CLI / MCP) align with the iOS scope.

---

# 12. Linked decisions

- Founder pushback on iOS scaffold being "generic SwiftUI shell" → full brand rewrite (this spec)
- Comments feature dropped on web → not in iOS scope either
- Type 3 synthesis docs dropped → paste-anywhere via `ProfileView` covers the iOS use surface
- `/hub/<slug>` → `/@<slug>` 301 on web → iOS Profile shows `/@<slug>` directly

Plan parent: [memory.wiki/SKaY7VJP](https://memory.wiki/SKaY7VJP)


---

## Summary
Memory.Wiki iOS is a native companion app to the web platform that provides fast capture and a polished reader interface while maintaining exact brand parity with the web surface. The app ships with authentication via Apple, Google, GitHub, and Email through Supabase, with Share Extension, Camera, Widget, Spotlight, and background sync queued for development.

## Themes
- brand parity over mobile adaptation
- native surface with web URLs
- lean scope with queued features

## Key takeaways
- The iOS app is a native companion surface using the existing web API with no iOS-specific endpoints, opening docs as in-app viewers or ShareLinks depending on ownership.
- Apple client secret JWTs expire every 6 months and must be regenerated via openssl and python3, with the current secret expiring 2026-11-25.
- The brand system enforces lime as micro-color only (dots, badges) and never as button fill, chip fill, or body text per a v8 color-balance rule locked from memory.wiki/SKaY7VJP.
- Four authentication providers (Apple, Google, GitHub, Email) are live via Supabase SDK with real OAuth wire-up, but the web /auth endpoint still needs to accept a provider query parameter to unblock Google and GitHub round-trips.
- Dark mode is forced via UIUserInterfaceStyle: Dark with zinc-950 background, zinc-900 surfaces, and custom quiet button styles that never use colored borders for selected state.
- The app uses XcodeGen as the single source of truth for the project file, requiring regeneration after editing project.yml or changing source files.

## Insights
- The app deliberately uses WKWebView for a single animated SVG rather than a native animation library, prioritizing exact parity with the web's morph blob breathing animation.
- OAuth credential management is split between Supabase SDK (automatic keychain persistence) and a thin SwiftUI observable layer, allowing sign-in elsewhere to pick up without client recreation.
- The Share Extension and camera capture features are explicitly queued rather than shipped, suggesting the product team prioritized authentication and brand fidelity as the v9 release gates.

## Open questions / gaps
- How will the app handle conflicts if a user captures the same content both on iOS and web simultaneously, or will Supabase's RLS rules prevent duplication?
- What is the strategy for syncing widget state (last 3 captures) with the main app when background sync is still queued?

## Concepts in this document
- **URL architecture** _(concept)_
  Shared with web surface; iOS app consumes existing API endpoints with no iOS-specific routes and uses memorywiki:// scheme for deep linking.
- **Memory.Wiki iOS** _(entity)_
  Native iOS companion app that provides fast capture and polished reading experience while maintaining exact visual parity with the web product.
- **Supabase authentication** _(entity)_
  Multi-provider OAuth integration (Apple, Google, GitHub, Email) that handles the core sign-in flow and callbacks via the memorywiki:// scheme.
- **Share Extension** _(concept)_
  Queued feature enabling cross-app capture (any app → Memory.Wiki) through a dedicated target with App Group persistence.
- **Brand parity** _(concept)_
  iOS app must visually and functionally read as a native extension of memory.wiki using identical fonts, colors, tokens, and micro-affordances—not a mobile wrapper.
- **Brand system parity** _(concept)_
  iOS must read as a visual and behavioral continuation of memory.wiki using identical typography, colors, and micro-affordances rather than a mobile version.
- **One URL every AI can read** _(concept)_
  Product thesis that iOS share and profile surfaces deliver memory URLs (/@<slug> for AI consumption) as the core value proposition.
- **Cal Sans, JetBrains Mono, Noto Sans** _(entity)_
  Three-font typography system imported as TTF bundles to achieve exact brand parity in headings, body prose, and data captions.
- **Widget and Spotlight** _(concept)_
  Queued features for home/lock screen widgets showing last 3 captures and Spotlight indexing for system-level discoverability.
- **Background sync** _(concept)_
  Queued feature for asynchronous content synchronization between iOS app and web backend.
- **Camera and OCR capture** _(concept)_
  Queued feature enabling screenshot and live camera capture with optical character recognition for document digitization.
- **Apple Universal Links** _(concept)_
  Pending infrastructure to make https://memory.wiki URLs open natively in iOS app when apple-app-site-association is deployed.

## Concept relations (within this doc's concepts)
- **Memory.Wiki iOS** enforces visual **Brand parity**
- **Memory.Wiki iOS** depends on for login **Supabase authentication**
- **Memory.Wiki iOS** shares routing with **URL architecture**
- **Brand parity** implemented via typography **Cal Sans, JetBrains Mono, Noto Sans**
- **Memory.Wiki iOS** enables capture via **Share Extension**
- **Memory.Wiki iOS** planned feature for **Camera and OCR capture**
- **Memory.Wiki iOS** extends reach through **Widget and Spotlight**
- **Supabase authentication** routes callbacks via **URL architecture**
- **Memory.Wiki iOS** preserves principle of **One URL every AI can read**
- **Memory.Wiki iOS** enforces design principle **Brand parity**
- **Share Extension** queued feature for **Memory.Wiki iOS**
- **Memory.Wiki iOS** uses shared **URL architecture**
- **Memory.Wiki iOS** relies on **Supabase authentication**
- **URL architecture** realizes **One URL every AI can read**
- **Background sync** synchronizes through **Supabase authentication**
- **Brand parity** implemented via **Cal Sans, JetBrains Mono, Noto Sans**
- **Memory.Wiki iOS** implements **Brand system parity**
- **Memory.Wiki iOS** uses **URL architecture**
- **Memory.Wiki iOS** depends on **Supabase authentication**
- **Memory.Wiki iOS** enables **Camera and OCR capture**

_Hub canonical:_ https://memory.wiki/hub/raymindai
_Concept digest:_ https://memory.wiki/raw/hub/raymindai?digest=1&compact=1
