Shipping an Agent iOS App From Zero in Two Weeks: What Survived, What Didn't
Native iOS clients are still the highest-fidelity surface for an AI agent — push, haptics, secure enclave, real local state. Here are the five engineering calls that survived contact with production, and the two I'd undo if I were starting over.
Most agent products in 2026 ship a web client first, a desktop wrapper second, and a native iOS app somewhere on the roadmap. That order is upside down. The native client is the surface where push notifications work, where haptics work, where the keyboard is your keyboard, where streaming chat doesn't get throttled by mobile Safari, and where the secure enclave is sitting right there for token storage. If your agent has a memory of who the user is between sessions, the iOS client is the version that actually feels like the agent has a memory.
I shipped a native iOS / iPadOS app from zero in two weeks for the founding-engineer stretch I just finished. It went into TestFlight on day 9 and to App Store on day 14. The codebase ended up around 350 Swift files; about a third of those were generated from the agent's tool schema. This post is the field report — the five engineering calls that held up under production load, and the two I would not repeat.
1. Apple Sign-In with a Redis fallback for ASWebAuthenticationSession token loss
If you implement Apple Sign-In on iOS, you will discover that ASWebAuthenticationSession occasionally hands back a token that the system's keychain refuses to persist — a state the documentation does not mention. The user signs in, sees the success screen, and on relaunch is signed out. The fix that worked: I generate a short-lived correlation ID before kicking off the auth flow, drop it in Redis on the backend with a 5-minute TTL, and embed it in the OAuth state parameter. If the iOS keychain drops the token, the next launch sees the device's signed correlation ID, validates it server-side, and re-issues a session without making the user re-authenticate. Net cost: ~40 lines of Swift, ~20 lines of FastAPI, one Redis key per auth attempt. Net gain: zero re-login complaints in the first month.
2. MarkdownUI for streaming chat — but with a custom buffer
MarkdownUI is the right rendering library for streaming agent output. Off the shelf it does about 80% of what you need: rich Markdown, code blocks with syntax highlighting, links. The 20% that bites: it re-parses the entire message on every token append. For a 4,000-token response, that means 4,000 parses. The phone gets warm.
The fix is a thin debounce buffer in front of MarkdownUI. Append tokens to a String accumulator, flush on \n or every 80ms, whichever comes first. Re-parse only on flush. The user perceives the same streaming flow; the GPU stops cooking. About 30 lines of Swift, including the test that gates a regression.
3. Event-sourced chat replays on reconnect
Mobile networks drop. The iOS client goes background. The agent is mid-tool-call. What does the UI look like when the user comes back?
The version that worked: every chat session has a server-side event log (Postgres, append-only, monotonic seq). The iOS client tracks the last seq it has rendered. On reconnect — explicit or auto — it asks the server for everything since seq=N, applies them deterministically to local state, and the chat just looks complete. No spinning loaders, no "recovering session", no missing messages. The same pattern handles screen-locked-while-agent-runs, app-killed-while-agent-runs, and bad-cell-tower scenarios with the same code path.
4. The 3-column iPad workspace — keep, don't kill
On iPhone, the agent is one column: chat. On iPad, the same app gets three: a left rail of conversations, a middle chat thread, and a right pane that becomes whatever the agent is working on (a document, a diff, a tool result, a chart). The temptation is to ship iPhone first, then squeeze the iPad UI in later. The temptation is wrong. SwiftUI's NavigationSplitView makes the 3-column layout cheaper to build correctly than to bolt on later, and the iPad version is what every AEC, legal, and design buyer evaluates the product on.
5. Custom animations and gesture-based navigation — yes, even on day 9
It is fashionable in 2026 engineering culture to dismiss UI craft as a distraction from the "real work." In a B2B agent product on iOS, this is incorrect. The buyer is opening 12 AI apps to evaluate. The one with the swipe-to-archive gesture, the springy push transition, and the haptic confirmation feels three generations newer than the one with the default NavigationLink animation. Total time investment for the polish layer was about a day and a half spread over two weeks. Compounded over every demo, every screenshot, every TestFlight review — easily the highest-ROI day and a half of the project.
What I would not repeat
- ▸Hand-writing the SwiftData models. The agent's tool schema is the source of truth; I should have generated the Swift model layer from a single OpenAPI / Protobuf spec from day one. Hand-syncing two parallel type systems consumed real time and produced exactly one bug per week. The right answer is a code-gen step in CI.
- ▸Optimistic UI on every action. For sub-second tool calls (sending a message, toggling a setting) optimistic UI is correct. For multi-second agent actions (run a query, generate a document), optimistic UI is a lie that creates more support tickets than it saves time. Use determinate progress indicators backed by the event log instead — the user can read "agent is reading file X (3 of 12)" and trust it.
What this signals about agent surface design more broadly
The agent's surface is not the chat box — it's every place the user might encounter the agent's state. Push notifications, lock-screen widgets, Apple Watch glances, Shortcuts integration, the iPad multitasking pane the user has open while doing something else. Each surface has a different latency budget, a different fidelity tolerance, and a different authentication context. Designing for the surface, not just the chat, is the difference between an iOS app that is the agent and one that is the agent's website in a wrapper.
The most underweighted lesson from the two-week ship: the parts of the project that look like "native craft" — the OAuth fallback, the streaming buffer, the event-sourced replay — are also the parts that determined whether the agent felt reliable. There's no separating production reliability from native client craft on iOS in 2026. They are the same project.
References
- ▸Apple —
ASWebAuthenticationSessiondocumentation and known-issue notes (developer.apple.com) - ▸MarkdownUI — gonzalezreal/swift-markdown-ui (GitHub)
- ▸Apple —
NavigationSplitViewand three-column SwiftUI layouts (developer.apple.com) - ▸Greg Young — "Event Sourcing" (the canonical primer)
- ▸Postgres — append-only log patterns for monotonic event ordering
// RELATED READING
- POSTDesigning Tool Surfaces for LLM Agents
- POSTMulti-Vendor Agent Design: Why One Model Isn't Enough in 2026
- POSTBuilding a Zero-Data-Retention Layer for Production LLM Agents
- CASE STUDYStructured AI — Founding Engineer Reps
- CASE STUDYReal-Time Sales-Conversation Coaching Agent — multi-vendor in production