Test iOS AI with the Evaluations Framework
- Author
- Ehsan Azish · 3NSOFTS
- Updated
- Read time
- 14 min read
- Level
- Intermediate to Senior
- Platform
- Xcode 27, Swift Testing, an AI-powered feature, representative test samples
Implementation Notes
- ~/ What broke: A production edge case that generic tutorials skip.
- ~/ What to do: Ship the production fix with clear state, errors, and fallback behavior.
A unit test can prove that a parser returns the same value for the same input. A language model may produce different wording on two valid runs. Testing only for exact text either creates brittle failures or ignores the quality problems that matter.
Apple’s Evaluations framework in Xcode 27 adds a Swift-native way to run an intelligence-powered feature against a dataset, score each response, aggregate the metrics, and inspect failures. It works with models exposed through Foundation Models, including on-device, Private Cloud Compute, and other providers.
The goal is not to make probabilistic output deterministic. It is to define acceptable behavior and measure whether a change improves or degrades it.
Start with a feature contract
Suppose an app turns a note into three to eight short tags. Before writing an evaluator, define observable requirements:
- return between three and eight tags;
- use no empty or duplicate tags;
- stay relevant to the source note;
- avoid sensitive categories the product does not support;
- finish within the UI’s latency budget.
The first two are quantitative. Swift code can score them exactly. Relevance is qualitative and may need reviewed expected values or a model judge. Latency is operational and should remain a separate measurement rather than being hidden in a quality score.
This contract also exposes product ambiguity. If two reviewers cannot agree on what “good” means, a model judge will not fix the specification.
Build a small, reviewed dataset first
Apple recommends starting with a focused set of roughly 20 to 30 samples. Include ordinary inputs, boundaries, and failures:
struct TaggingSample: Codable, Sendable {
let note: String
let expectedTopics: Set<String>
let shouldRefuse: Bool
}
Useful categories include short notes, long notes, mixed languages, misspellings, empty content, instructions embedded in user text, and subjects that should not produce a result. Label each sample with its category so aggregate results can reveal where the feature fails.
Do not begin with thousands of synthetic examples. A small human-reviewed set establishes the behavior you actually want. Synthetic generation can expand coverage after the seed data is trustworthy.
Define the subject behind a stable boundary
The evaluator should call the same feature service used by the app, with model selection and prompt configuration supplied explicitly.
struct TaggingConfiguration: Sendable {
let instructions: String
let maximumTags: Int
}
actor BookTaggingService {
func tags(
for note: String,
configuration: TaggingConfiguration
) async throws -> [String] {
// Call LanguageModelSession and decode guided output here.
[]
}
}
Record the configuration with every evaluation run: app commit, OS and model availability, prompt version, tool versions, locale, and model profile. A score without its configuration cannot explain a regression later.
If the production service has availability and cancellation behavior, test those paths with ordinary Swift tests. The Evaluations framework measures model-driven quality; it does not replace deterministic tests for state transitions, persistence, permissions, or writes.
Add code-based evaluators first
Anything measurable in Swift should use a deterministic evaluator. For tag generation, score count, uniqueness, blocked terms, and known expected topics independently.
Conceptually, the evaluation has four parts:
- A subject that runs the feature.
ModelSamplevalues that contain inputs and expected data.Evaluatorimplementations that return metrics for each sample.- Aggregate metrics and Swift Testing expectations for release thresholds.
Apple’s APIs provide the concrete Evaluation, Evaluator, Metric, and sample types. Keep each metric narrow. A single “quality passed” Boolean cannot tell the team whether a change hurt relevance, format compliance, tool use, or safety.
For example, a range metric should report the produced count and pass state. A topic metric should report the overlap with expected topics. Aggregate each across the dataset and set an explicit threshold such as at least 95% format compliance.
Use a model judge only for qualitative criteria
Relevance, tone, completeness, and usefulness are difficult to encode with string rules. ModelJudgeEvaluator can ask a second model to score those dimensions from a written scoring guide.
The judge should be at least as capable as the model it evaluates. Apple’s WWDC26 example uses a more capable Private Cloud Compute model to judge an on-device feature. That introduces a data boundary: do not send private production records to a remote judge. Evaluation datasets should be approved test data.
Use an even-numbered scale such as one through four to avoid a comfortable neutral middle. Define every level:
- 1: unrelated or misleading;
- 2: partly relevant but includes a material error;
- 3: relevant with a minor omission;
- 4: directly relevant and complete for the task.
Review the judge’s rationale, not only its score. If reviewers and the judge disagree repeatedly, measure that drift and refine the guide or examples. A judge is another probabilistic system, not an objective oracle.
Evaluate tool use as a trajectory
An agent can return a plausible final answer after calling the wrong tool, skipping required confirmation, or reading too much data. Final-text evaluation will miss that failure.
For tool-calling features, record and evaluate the trajectory:
- which tools were called;
- their order;
- the arguments supplied;
- whether required tools were omitted;
- whether forbidden tools were invoked;
- whether a write occurred before confirmation.
Apple’s advanced Evaluations material includes tool-call evaluators and trajectory expectations. Use them to state the boundary explicitly: a record lookup may run automatically, while deletion or sending requires a review step.
Pair this with the native agent action-boundary pattern. Quality and authorization are separate gates; a good answer cannot excuse an unsafe action sequence.
Expand coverage with synthetic samples carefully
After the seed set produces useful failures, use SampleGenerator to create variations across length, language, ambiguity, user intent, and tool conditions. Validate generated samples before treating them as truth.
Synthetic examples can amplify a mistaken seed label or produce unrealistic inputs. Keep their provenance, review a stratified sample, and retain a human-authored holdout set that generation never sees. Otherwise the feature, generator, and judge can converge on the same blind spot.
Compare changes instead of chasing one score
Run a control and an experimental configuration against the same dataset. Compare per-dimension distributions and inspect samples that changed direction.
A prompt revision that raises average relevance from 3.1 to 3.3 but doubles refusals may be a regression for the product. A tool addition that improves correctness but adds two seconds at p95 may fail the interaction budget. Keep quality, safety, latency, and cost visible as separate measures.
Use the Xcode evaluation report to inspect the full request, generated response, metric values, and rationales for failing samples. Export machine-readable results when CI needs to compare baselines over time.
Put the suite beside release gates
An evaluation belongs in the release workflow when it is stable enough to detect a meaningful regression. Recommended gates include:
- deterministic unit and integration tests pass;
- model availability and fallback behavior pass on target devices;
- evaluation thresholds pass for each critical category;
- no required tool-call trajectory regresses;
- latency and memory stay within device budgets;
- a human reviews changed failure clusters before release.
Do not run an expensive remote judge on every keystroke. A small deterministic suite can run frequently; the complete judge-backed suite can run for release candidates or scheduled quality checks.
Production checklist
- Write an observable feature contract before metrics.
- Start with 20 to 30 reviewed samples across real categories.
- Store prompt, model, OS, locale, and tool configuration with each run.
- Use deterministic evaluators for properties Swift can measure.
- Use model judges for narrow qualitative dimensions.
- Keep private user data out of remote evaluation models.
- Score tool trajectories as well as final answers.
- Validate synthetic samples and protect a human-authored holdout set.
- Compare control and experimental runs on the same data.
- Keep quality, safety, latency, memory, and cost as separate release gates.
Apple references
- Evaluations framework
- Meet the Evaluations framework
- Create robust evaluations for agentic apps
- Improve your prompts by hill-climbing with Evaluations
Evaluation evidence is strongest when it accompanies device profiling and an explicit fallback path. Use the Foundation Models availability guide for the runtime gate, then profile the selected model with Apple’s Foundation Models Instruments template on the devices the app supports.