All posts
case-studyhealthapp-intentssiri

Case study: 50 Siri intents from one biomarker schema

How Ambition Health used Axint to generate 50 App Intents for biomarker queries — from a TypeScript schema, without writing Swift.

Nima NejatFriday, April 10, 20268 min read

Ambition Health is a health intelligence platform I'm building. It measures biological age, tracks 50+ biomarkers (VO2 max, HRV, sleep quality, metabolic age), and gives personalized recommendations.

The iOS app needed Siri intents so users could ask things like "What's my biological age?" or "Show me my VO2 max trend." Fifty biomarkers means fifty intents. Writing those in Swift would take weeks — and every time we added a biomarker, we'd need another intent.

So we used Axint.

The schema

We already had a TypeScript biomarker definition:

typescript
const biomarkers = {
  vo2Max: {
    name: "VO2Max",
    title: "VO2 Max",
    description: "Maximal oxygen uptake in ml/kg/min",
    unit: "ml/kg/min",
    normalRange: { min: 35, max: 60 },
  },
  heartRateVariability: {
    name: "HRV",
    title: "Heart Rate Variability",
    description: "Variation in time between heartbeats",
    unit: "ms",
    normalRange: { min: 20, max: 200 },
  },
  // ... 48 more
};

We generated intents programmatically:

typescript
const intents = Object.entries(biomarkers).map(([key, bio]) =>
  defineIntent({
    name: `Query${bio.name}`,
    title: `Query ${bio.title}`,
    description: bio.description,
    params: {
      startDate: param.date("Start date"),
      endDate: param.date("End date"),
    },
    returns: {
      value: "Double",
      unit: "String",
      interpretation: "String",
    },
  })
);

One schema. Fifty intents. One axint compile call.

The generated Swift

For each biomarker:

swift
struct QueryVO2MaxIntent: AppIntent {
  static let title: LocalizedStringResource = "Query VO2 Max"

  @Parameter(title: "Start date") var startDate: Date
  @Parameter(title: "End date") var endDate: Date

  func perform() async throws -> some IntentResult<QueryVO2MaxResult> {
    let result = try await AmbitionHealthClient.shared
      .queryBiomarker(.vo2Max, from: startDate, to: endDate)
    return .result(value: result)
  }
}

We filled in the perform() body to call our API. The struct, parameter decorators, result type, and Info.plist entries were all generated. Zero Swift boilerplate written by hand.

Adding a new biomarker

When we added cortisol tracking:

1. Added it to the biomarker schema 2. Ran axint compile 3. Got 51 Swift files. Done.

No manual Swift editing. No Xcode project maintenance. The schema is the source of truth.

What we learned

Schema-driven codegen works at scale. Fifty similar intents is exactly the use case where manual Swift would be a maintenance nightmare.

The generated code is a starting point. We hand-edited three intents that needed special logic (biological age calculation involves multiple biomarkers, not a single query). Axint doesn't fight you on this — the output is plain Swift.

Version the schema like an API. We treat the biomarker definition as a contract. Changes to the schema trigger a recompile and a review of the generated diff.

The developer who set this up is a TypeScript developer. Never opened Xcode before Axint. Shipped 50 Siri intents in an afternoon.