Turn dense medicine labels into plain, readable instruction cards — entirely on your iPhone.
MedsPlain is a privacy-first iOS app that scans a medicine label, extracts dosage text locally using Vision OCR, and uses Apple Foundation Models to produce a structured instruction card — dose, timing, warnings, confidence — without ever sending your data off-device.
No backend. No cloud AI. No account. No analytics.
Medicine labels are dense, full of jargon, and easy to misread. Critical information gets buried in small print.
| Before | After |
|---|---|
| "Adults and children 12 years and over: take 1 tablet every 4 to 6 hours while symptoms persist. Do not exceed 6 tablets in 24 hours unless directed by a doctor. Take with food or milk if stomach upset occurs." | Dose: 1 tablet (200 mg) · Frequency: Every 4–6 hours · Timing: With food or milk · Warning: Max 6 tablets/day |
MedsPlain reads the label so you don't have to squint.
Screenshots will be added here once the app is running on Apple Intelligence hardware.
| Home | Tutorial — Scan | Tutorial — Review | Tutorial — Result | Tutorial — Share | Saved Cards |
|---|---|---|---|---|---|
![]() |
![]() |
![]() |
![]() |
![]() |
![]() |
| Scan or import a label | Step 1 of 4 | Step 2 of 4 | Step 3 of 4 | Step 4 of 4 | Locally saved cards |
Captured on iPhone 17 Pro Max · iOS 26.4 · Apple Intelligence enabled
| Feature | Detail |
|---|---|
| Camera & Import | Scan live or import from the photo library |
| On-Device OCR | Vision VNRecognizeTextRequest — no cloud |
| User Review Step | Edit extracted text before AI analysis |
| On-Device AI | Apple Foundation Models with @Generable structured output |
| Plain Instruction Card | Dose · Frequency · Route · Timing · Warnings |
| Confidence Display | High / Medium / Low with reason and pharmacist prompt |
| Low-Confidence Safety | Conservative refusal UI when dosage is unclear |
| Local Persistence | SwiftData-backed saved cards with optional naming |
| Search & Share | Search saved cards; share via native share sheet |
| Privacy Manifest | PrivacyInfo.xcprivacy included for App Store compliance |
| Accessibility | VoiceOver labels, Dynamic Type, haptic feedback |
| First-Launch Tutorial | Step-by-step privacy intro with how-it-works walkthrough |
| Layer | Technology |
|---|---|
| UI | SwiftUI |
| Architecture | MVVM with protocol-oriented services |
| Concurrency | Swift async/await, @MainActor |
| On-Device AI | Apple Foundation Models — LanguageModelSession, @Generable |
| OCR | Vision — VNRecognizeTextRequest |
| Storage | SwiftData |
| Image Processing | Core Image via ImagePreprocessor |
| Testing | XCTest — unit tests for prompts, models, and VM cancellation |
| Minimum OS | iOS 26.0 · Xcode 26+ |
Views ──▶ ViewModels ──▶ Services ──▶ Models
flowchart TD
Photo[📷 Camera / Photo Library] --> OCR[Vision OCR\nVNRecognizeTextRequest]
OCR --> Review[User Reviews & Edits Text]
Review --> Availability{Foundation Models\nAvailability?}
Availability -- Available --> AI[On-Device Structured Generation\nLanguageModelSession + @Generable]
Availability -- Unavailable --> Banner[Availability Banner\nwith Reason]
AI --> Card[Plain Instruction Card\nDose · Timing · Warnings · Confidence]
Card --> Save[SwiftData — Local Save]
Card --> Share[Native Share Sheet]
| Component | Responsibility |
|---|---|
ScanViewModel |
Camera/import flow, AI availability state, recent cards |
OCRReviewViewModel |
Runs Vision OCR, exposes editable text |
AnalysisViewModel |
Orchestrates structured generation, cancellation, error states |
ResultViewModel |
Instruction card display and save/share actions |
FoundationModelsMedicationService |
LanguageModelSession lifecycle, @Generable mapping |
ModelAvailabilityService |
Maps SystemLanguageModel.availability to user-facing states |
SwiftDataStorageService |
CRUD for persisted SavedMedicationCard |
PromptBuilder |
Session instructions and per-request prompt construction |
ImagePreprocessor |
Contrast/sharpness normalization before OCR |
- Check
SystemLanguageModel.default.availability - Pre-warm a
LanguageModelSessionwith strict medical safety instructions - Build a structured prompt from the user-reviewed OCR text
- Generate typed
GeneratedMedicationAnalysisoutput via@Generable - Map to
MedicationAnalysismodel with confidence scoring - Show pharmacist guidance when
confidence.isLowor dose/frequency are empty
- Rephrase only what is supported by the user-reviewed label text
- Never invent dosage amounts, timing, routes, or warnings
- Never diagnose or recommend a medicine as appropriate
- Always preserve original label text as
sourceText - Surface
questionsForPharmacistwhen the label is ambiguous - Conservative refusal UI (
requiresPharmacistCheck) for low-confidence results
MedsPlain/
├── MedsPlain.xcodeproj/
├── MedsPlain/
│ ├── App/ # Entry point, router, environment
│ ├── Models/ # MedicationAnalysis, SavedMedicationCard, AppSettings
│ ├── Services/
│ │ ├── AI/ # FoundationModelsMedicationService, PromptBuilder
│ │ ├── OCR/ # VisionOCRService
│ │ ├── Imaging/ # ImagePreprocessor, ImageStore
│ │ └── Storage/ # SwiftDataStorageService
│ ├── ViewModels/ # ScanVM, OCRReviewVM, AnalysisVM, ResultVM, SavedCardsVM
│ ├── Views/
│ │ ├── Scan/ # ScanView, CameraPicker
│ │ ├── Analysis/ # AnalysisView
│ │ ├── Result/ # ResultView, InstructionCardView, ConfidenceView
│ │ ├── Saved/ # SavedCardsView, SavedCardRow
│ │ ├── Settings/ # SettingsView
│ │ └── Components/ # ShareSheet, reusable UI
│ ├── DesignSystem/ # MPButtonStyle, MPCardStyle, MPTypography, Haptics
│ ├── Utilities/ # Logger, ErrorState, DateFormatting, SampleData
│ └── Resources/
│ └── PrivacyInfo.xcprivacy
├── MedsPlainTests/ # PromptBuilder, AnalysisViewModel, MedicationAnalysis, Settings tests
├── docs/
│ ├── DEVELOPMENT.md
│ ├── APP_STORE.md
│ └── PRIVACY.md
├── screenshots/
└── LICENSE
- Xcode 26+ on macOS
- iOS 26+ on device or simulator
- Apple Intelligence-capable device for on-device AI (iPhone 15 Pro / iPhone 16 series or later)
- Apple Intelligence enabled under Settings → Apple Intelligence & Siri
git clone https://github.com/your-username/MedsPlain.git
cd MedsPlainopen MedsPlain.xcodeproj- Select the MedsPlain target → Signing & Capabilities
- Choose your development team
- Update the bundle identifier if needed
Select an Apple Intelligence-compatible simulator or device and press ⌘R.
On first launch, the app walks you through a privacy intro and tutorial. Scan or import a label, review extracted text, then tap Simplify Dosage.
Edit the scheme (⌘<) and use Foundation Models Availability to simulate:
| State | Simulates |
|---|---|
| Available | Normal on-device AI flow |
| Device not eligible | Older device banner |
| Apple Intelligence disabled | Settings prompt |
| Model not ready | Preparing state |
xcodebuild test \
-project MedsPlain.xcodeproj \
-scheme MedsPlain \
-destination 'platform=iOS Simulator,name=iPhone 16 Pro'Test coverage includes:
PromptBuilderTests— prompt content and instruction constraintsAnalysisViewModelTests— cancellation and error propagationMedicationAnalysisTests— model computed propertiesSettingsServiceTests— UserDefaults persistence
| Concern | Answer |
|---|---|
| Label photos | Stay on device, never uploaded |
| OCR text | Processed locally by Vision framework |
| AI analysis | Runs entirely via Apple Foundation Models on-device |
| Analytics / telemetry | None |
| Network requests | Zero for label processing |
| Account required | No |
Privacy manifest: MedsPlain/Resources/PrivacyInfo.xcprivacy
Full policy: docs/PRIVACY.md
- SwiftUI MVVM with protocol-oriented, injectable services
- On-device OCR via Vision framework
- On-device AI via Apple Foundation Models (
@Generable) - User review step before any AI generation
- Medical safety boundaries enforced in prompt and UI
- Conservative low-confidence and pharmacist-check states
- SwiftData local persistence with search and delete
- Share sheet export
- Privacy manifest for App Store
- Unit tests for prompts, models, and cancellation
- VoiceOver labels and Dynamic Type support
- App Store screenshots on physical Apple Intelligence device
- Physical device QA on iPhone 15 Pro / iPhone 16
MedsPlain is a label readability tool, not a medical advisor. It does not:
- Diagnose conditions or recommend whether a medicine is appropriate
- Adjust dosage based on age, weight, symptoms, or conditions
- Replace guidance from a pharmacist, clinician, or manufacturer
When the model is uncertain, the app surfaces a conservative state and directs you to check with your pharmacist.
- App Store screenshots and submission
- Physical device QA with Apple Intelligence
- Multi-language label support (locale-aware prompts already scaffolded)
- Medication interaction awareness (pharmacist Q&A prompts)
- Accessibility audit — full VoiceOver navigation pass
- iPad and Mac Catalyst layout
- Fork the repository
- Create a feature branch:
git checkout -b feature/your-feature - Follow the existing MVVM + protocol-service pattern
- Add unit tests for new business logic
- Verify
PromptBuildersafety constraints are preserved - Open a pull request with a clear description
Read docs/DEVELOPMENT.md for the implementation task breakdown and manual QA checklist.
MedsPlain interprets visible medicine label text. It is not a substitute for advice from a pharmacist, clinician, or the manufacturer's official instructions. When in doubt, ask your pharmacist.
MIT — see LICENSE.
Built with SwiftUI · Apple Foundation Models · Vision · SwiftData
No cloud. No account. Just your label, made readable.





