Plainly is an experimental iOS app for a consented Stanford research study. It evaluates whether conversational artificial intelligence can help participants understand FHIR-formatted health records and navigate the healthcare system.
During a study session, participants complete study surveys and can ask questions about health records made available through Apple Health. Plainly generates summaries and explanations using language models; it does not provide medical advice, diagnosis, or treatment.
Important
Plainly is only for invited participants who have completed the study consent process. Do not install or use the app outside the study. The signed consent form, HIPAA authorization, and other study information govern participation and the handling of participant information.
![]() |
![]() |
![]() |
| Welcome | Research Disclaimer | Study |
You can build and run the application using Xcode by opening Plainly.xcodeproj.
For development without participant data, the app includes Synthea-based synthetic patients.
All chat requests are dispatched to the Firebase chat function, which holds the inference credentials and resolves the provider and endpoint from the model identifier a study defines. The app never talks to an inference API directly, so local testing requires either the Firebase emulator (see below) or the staging backend.
When running Plainly via Xcode, you can use the --mode CLI flag to control the behavior of the app (configurable via the Run scheme):
--mode testloads the bundled synthetic patients instead of health records;--mode study:<study-id>launches Plainly into its study mode, loads the study with the specified ID fromPlainlyStudyDefinitions, and automatically opens it;--mode studylaunches Plainly into its study mode, showing a "Scan QR Code" button to select and open a study.
The Firebase emulator UI test exercises anonymous authentication, streaming chat through the callable function, and study-report upload to Storage without any real inference credentials:
scripts/run-firebase-e2e.shThe script initializes the Plainly-Firebase submodule, starts the local emulators with a deterministic OpenAI-compatible response, and runs only the dedicated end-to-end UI test.
Everything a study does — its prompts, tasks, model, retrieval, and chat function — is defined by the Study type in PlainlyStudyDefinitions and versioned with the code.
The UserStudyConfig.plist file therefore carries only what cannot live in an open-source repository:
- Firebase configuration: connects the app to a Firebase environment for chat completions and study report uploads
- app launch mode: controls how the app behaves upon launch (e.g., whether to directly launch a study)
The file bundled with the repository carries placeholder Firebase credentials and must be regenerated for a real deployment.
Use the export-config tool in the PlainlyShared folder to do so:
swift run PlainlyCLI export-config -f ~/GoogleService-Info.plist ../Plainly/Supporting\ Files/UserStudyConfig.plistStudy reports are uploaded to Firebase Storage. A report that cannot be uploaded is kept in Application Support, surfaced on the study home screen, and retried when the participant returns to that screen or relaunches the app.
The PlainlyShared subpackage contains a tool that lets you simulate user chat sessions.
During a simulated chat session, the LLM is provided with the same context and data it would receive during normal app usage, except that the inputs (both the patient's health records and the questions being asked by the user) are predefined. This allows you to evaluate how different models (or even the same model across multiple conversations) handle various scenarios and situations.
For each simulated session, a report file is generated with the same structure as the report files generated during regular app sessions.
swift run PlainlyCLI simulate-session config.json output/Simulated sessions reach the model the same way the app does, through the Firebase chat function, so the simulator never holds an inference credential. Point it either at a deployed backend or at a local emulator — the Plainly-Firebase development guide covers configuring the local secrets and starting the emulators.
Session simulation is controlled via a JSON config file. Credentials are never stored in the config file — they are read from environment variables at runtime:
| Service | Required env var |
|---|---|
Firebase |
GOOGLE_CREDENTIALS_PLIST (path to GoogleService-Info.plist) |
Firebase-Emulator |
(none — connects to the local emulator suite) |
Additional optional environment variables:
| Env var | Default | Effect |
|---|---|---|
FIREBASE_REGION |
us-central1 |
Firebase Functions/Auth region |
FIREBASE_PROJECT_ID |
demo-project |
Project ID override for the emulator when GOOGLE_CREDENTIALS_PLIST is not set (emulator mode only) |
FIREBASE_AUTH_EMULATOR_HOST |
localhost:9099 |
Auth emulator address (host:port) |
FIREBASE_FUNCTIONS_EMULATOR_HOST |
localhost:5001 |
Functions emulator address (host:port) |
Each entry in the JSON config defines the parameters of one simulation:
numberOfRuns— how many times to repeat this sessionstudyId— the study whose prompts and context to usebundleName— name of an embedded synthetic patient, or a path to a FHIR bundle JSON file (resolved relative to the config file)model— the model identifier to requestuserQuestions— the questions the simulated patient asksservice(optional) —"Firebase"or"Firebase-Emulator"; if omitted, inferred from the environment (GOOGLE_CREDENTIALS_PLIST→ Firebase, otherwise Firebase-Emulator)name(optional) — human-readable label used as the output filename prefixcomment(optional) — free-form note describing the config, carried through to the reportcustomSystemPrompt(optional) — custom system prompt, replaces the study's default system promptcustomResourcePrompt(optional) — custom prompt controlling how individual FHIR resources are summarized
The example config below performs six simulated runs of the edu.stanford.plainly.gynStudy study with GPT-4o, three against a deployed backend and three against the local emulator:
[{
"numberOfRuns": 3,
"name": "gyn-gpt4o-firebase",
"studyId": "edu.stanford.plainly.gynStudy",
"bundleName": "Elena Kim",
"model": "gpt-4o",
"service": "Firebase",
"userQuestions": [
"Tell me about my recent diagnoses and how they affect my fertility.",
"How are my hormonal levels?"
]
}, {
"numberOfRuns": 3,
"name": "gyn-gpt4o-emulator",
"studyId": "edu.stanford.plainly.gynStudy",
"bundleName": "Elena Kim",
"model": "gpt-4o",
"service": "Firebase-Emulator",
"userQuestions": [
"Tell me about my recent diagnoses and how they affect my fertility.",
"How are my hormonal levels?"
]
}]Run against a deployed backend:
GOOGLE_CREDENTIALS_PLIST=~/GoogleService-Info.plist
swift run PlainlyCLI simulate-session config.json output/Or against the emulator suite, once it is running:
FIREBASE_PROJECT_ID=...
swift run PlainlyCLI simulate-session config.json output/Reports are saved to a timestamped subdirectory inside the output directory, named <index>-<name>-<run>.json (e.g. 00-gyn-gpt4o-firebase-1.json).
Contributions to this project are welcome. Please make sure to read the contribution guidelines and the contributor covenant code of conduct first. You can find a list of contributors in the CONTRIBUTORS.md file.
This project is licensed under the MIT License. See LICENSE.md for more information.
If you use this software, please cite it using the metadata in CITATION.cff, which GitHub surfaces through the Cite this repository button.
For more information, visit the Schmiedmayer Lab GitHub organization.




