A local-first iOS assistant for a parent who does not read English and is not comfortable navigating a phone. Everything is translated to Simplified Chinese and read aloud. Personal data is processed on-device — no third-party cloud AI.
Built from
ParentHelper/Parent helper.md. This README covers setup; the code is heavily commented inline.
ParentHelper is a native SwiftUI iPhone app that acts as a personal, easy-to-use assistant for a parent who does not read English and cannot navigate a phone independently. It exists to let that person handle everyday digital chores — reading email and understanding a government or account website — without needing to read English or hunt through unfamiliar screens.
Core promises
- Everything in Simplified Chinese, read aloud. Every screen, summary, and action is translated to Chinese and spoken via text-to-speech. Nothing requires reading English.
- Privacy-first, on-device AI. Personal data (emails, bills, account info) is never sent to a third-party cloud AI. All AI reasoning (summarize, classify, pick next step) runs on-device via Apple's Foundation Models; translation runs on-device via Apple's Translation framework. The AI backend is swappable and defaults to fully on-device.
- Task-based, not open-ended. The home screen is a grid of large icon tiles, each a scripted "flow" with fixed steps. The AI only fills variable parts (translation, summarization, next-step / on-screen-action selection) — it never improvises whole flows or generates things like CSS selectors.
- Accessible by design. Big icon-based buttons, minimal text, oversized components, spoken guidance — no reliance on reading or precise navigation.
Scope guardrails
- Reliability, privacy, and simplicity are prioritized over feature breadth.
- Email uses the Gmail API inside our own native mail screen (not an embedded
Gmail WebView), with bounded 30-day + incremental
historyIdsync — never a full mailbox re-pull. - Website help is a guided in-app browser over a small set of known sites with hand-authored action taxonomies + selectors, not a general-purpose agent.
- Scenario: User want to go read their email, and understand it.
- Trigger (what makes them open the app): to go through email
- What they should see / hear: list of their email: opened/unopened, past 30 days
- What "success" looks like: user opens an email, and the user can choose to summarize or click on the screen to box or select which part they want to translate, the app also explain what this is about in short sentences. The app reads them in mandarine.
- Scenario: The email shows a bill, the user want to understand it, but it requires user to go into company website
- Trigger: user click on the link provided in the email
- What they should see / hear: if they press on the summarize button, they should hear the app summarizing what this is about, and tells them that there is a link. If they click on the link they see the website. then the app also has the 2 buttons for either select parts or summarize whole thing, and read them actional actions.
- Success: The user sucessfully sees the bill
- Scenario: Similar to scenario 2, but for paying bills.
- Trigger:
- What they should see / hear:
- Success:
- Replying to / composing messages. The app is read-and-understand only; it does not send email or compose replies on the user's behalf.
- Foundation layer (build order §1):
TranslationService(Apple Translation framework, on-device) andSpeechService(AVSpeechSynthesizer,zh-CN) as standalone pieces. - "Read New Emails" end-to-end (build order §2): Gmail OAuth → bounded 30-day
sync + incremental
historyIdsync → on-device summarization in Chinese (Foundation Models) → read aloud. - "Reply to a Message": compose in Chinese → translate to English → send via Gmail API.
- "Understand a Website" guided browser:
WKWebView+ injected snapshot / highlight user scripts, on-device action classification against an authored taxonomy, icon action buttons. The mechanism is complete; the site data inFeatures/Browser/ActionTaxonomy.swiftis placeholder — fill in real hosts + hand-verified CSS selectors. - Swappable AI backend (§5):
RemoteAIAssistantstub conforms to the sameAIAssistantprotocol as the on-device backend; swap inAIAssistantProvider.
ParentHelper/
App/ AppServices (DI), AppTheme, Strings
Models/ EmailMessage, HelperTask
Services/
Speech/ SpeechService (§1)
Translation/ TranslationService (§1)
AI/ AIAssistant protocol, FoundationModelsAssistant (default,
on-device), RemoteAIAssistant (stub), AIAssistantProvider
Gmail/ Auth (OAuth+PKCE), API, SyncEngine (30-day + historyId),
MessageParser, TokenStore (Keychain), models
Features/
Home/ HomeView (task tiles)
Email/ List/Detail/Reply views + view models (MVVM, @Observable)
Browser/ Guided browser (scaffold)
Settings/ Account + AI status
Config/ Secrets.swift ← fill this in
Support/ Reusable oversized components
Architecture: MVVM with @Observable (Observation, iOS 17+). Views are thin;
all state lives in @Observable view models constructed with injected services.
- Xcode 26+, deployment target iOS 26.0 (already set).
- Apple Intelligence-capable device for on-device AI: iPhone 15 Pro / Pro Max or newer (A17 Pro+). The Foundation Models summarizer reports "unavailable" on other devices and the app falls back to translating the email snippet.
- The Simulator can run the UI; Foundation Models availability depends on the Mac/Simulator supporting Apple Intelligence. Translation + speech work broadly.
The project already has a bundle id com.ParentHelper and a development team.
Adjust PRODUCT_BUNDLE_IDENTIFIER / DEVELOPMENT_TEAM in project settings for
your own Apple Developer account.
No credentials are bundled. To enable email:
-
Google Cloud Console → create a project.
-
Enable the Gmail API (APIs & Services → Library → Gmail API).
-
OAuth consent screen: User type External. While unverified, add the parent's Gmail address under Test users. Scopes used:
gmail.readonly,gmail.send,userinfo.email. -
Credentials → Create OAuth client ID → Application type: iOS. Use bundle id
com.ParentHelper. Google gives you:- a client id like
1234-abcd.apps.googleusercontent.com - a reversed client id like
com.googleusercontent.apps.1234-abcd
- a client id like
-
Paste both into
ParentHelper/Config/Secrets.swift(googleClientID,googleRedirectScheme= the reversed client id). -
Register the redirect URL scheme so
ASWebAuthenticationSessioncan return: Target → Info → URL Types → add a URL Type whose URL Schemes is the reversed client id (e.g.com.googleusercontent.apps.1234-abcd).Because this target uses
GENERATE_INFOPLIST_FILE = YES, add the URL Type via the target's Info tab in Xcode (it writes to the generated Info.plist), or add a customInfo.plistwith aCFBundleURLTypesarray and pointINFOPLIST_FILEat it.
Until real values are in Secrets.swift, the email screen shows a friendly
"setup needed" state instead of attempting OAuth.
Build & run on an Apple-Intelligence device (or Simulator for UI). Tap 读新邮件 ("Read new emails") → 登录邮箱 to start the Gmail OAuth flow.
- Email/website content is summarized/classified on-device via Foundation Models. Translation is on-device via Apple's Translation framework.
- OAuth tokens are stored in the Keychain (
TokenStore). - The only network calls are directly to Google's Gmail API (the user's own mailbox). No third-party AI service is contacted.
RemoteAIAssistantis disabled by default; if ever enabled it must point only at a family-controlled machine on a private VPN (e.g. Tailscale), never a public cloud AI API.
- Task-based home, not open-ended. Each tile is an authored flow; the AI only fills variable parts (summarize / classify / pick next known step).
- No selectors from the model. The guided browser keeps a hardcoded
(site, action) → selectormap; the model only classifies which authored actions are on screen (avoids hallucinated selectors). - No embedded Gmail WebView. Google blocks OAuth in embedded browsers, so we
use the Gmail API inside our own SwiftUI mail screen with
ASWebAuthenticationSessionfor login. - Bounded sync. Initial pull is last 30 days only; thereafter incremental via
historyId. On an expired cursor (404) we re-run the 30-day sync — never a full mailbox re-pull. - Swappable AI. The UI depends only on the
AIAssistantprotocol.