Apple Foundation Models · SwiftUI
Foundation Models tool calling Swift, done right
Foundation Models tool calling Swift developers reach for when they want Apple's on-device model to invoke their own functions — but the tricky part is the loop: registering Tool conformances, decoding @Generable arguments, running them locally, and feeding results back into the transcript. ShipThatApp ships an OnDeviceAgent that does exactly that, with the Swift 6 concurrency and availability edge cases already solved.
A real tool-calling loop
An OnDeviceAgent drives the tool-calling loop over the LanguageModelSession transcript — model asks, tool runs on-device, result is fed back — so you extend behavior instead of wiring the plumbing.
Typed tool arguments
Tools declare a @Generable argument type, so the model hands you decoded, typed inputs instead of a JSON string you have to parse and validate by hand.
Example tools to extend
Weather, calculator, calendar, and reminders tools ship as working examples with a SwiftUI agent view — copy the pattern for your own domain tools.
What Foundation Models tool calling Swift skips for you
The on-device agent code that AI generators compile but get subtly wrong, already implemented and device-tested:
Services/AI/
LocalLLMService.swift // sessions, streaming, availability, errors
Agent/OnDeviceAgent.swift // tool-calling loop over the transcript
Tools/ // Weather, Calculator, Calendar, Reminders
// each: Tool + @Generable Arguments
Views/AI/
Agent/ // multi-tool agent UIFAQ
How does tool calling work with Apple's Foundation Models?
With Foundation Models, you conform a type to the framework's Tool protocol, give it a name, a natural-language description, and a @Generable arguments type. You register the tools on a LanguageModelSession; when the on-device model decides a tool is needed, it emits a structured call with typed arguments, your tool runs on-device, and the result is fed back into the transcript so the model can continue. ShipThatApp wraps this whole loop in an OnDeviceAgent so you don't wire it by hand.
What tool-calling code does the kit actually include?
An OnDeviceAgent that drives the tool-calling loop over the session transcript, a set of example Tool conformances (weather, calculator, calendar, reminders) with @Generable argument types, and a SwiftUI agent view that shows the model's final answer along with the tool calls it made. It's the exact glue that AI code generators get subtly wrong — Swift 6 actor isolation, availability handling, and re-feeding tool output into the transcript — implemented and device-tested.
Why not just have ChatGPT or Cursor generate the tool-calling code?
The SwiftUI scaffolding is the easy 80% and AI generates it fine. The hard 20% is the Foundation Models glue: correct Swift 6 actor isolation for partial streaming (a non-Sendable PartiallyGenerated crossing an isolation boundary is a common crash), modeling model availability, and looping tool results back into the transcript without breaking the session. That's where generated code compiles but fails on device. ShipThatApp ships the known-correct version.
Do on-device tool calls send data to a server?
The model itself runs entirely on-device — no API keys and no per-token cost, and your prompt is never shipped to an LLM provider. What a tool does is up to the tool: a pure-local tool (calculator, reminders, calendar) touches nothing external, while a tool that needs live data — the example weather tool calls Open-Meteo — makes its own network request, so you decide per tool what leaves the device. When you need a bigger model, the kit also includes a secure cloud AI stack (ChatGPT, DALL·E, Vision) behind an HMAC-signed proxy, and the app degrades gracefully where the on-device model isn't available.