axiom-foundation-models-ref by charleswiltgen/axiom
npx skills add https://github.com/charleswiltgen/axiom --skill axiom-foundation-models-refFoundation Models 框架通过 Swift API 提供对苹果设备端大型语言模型(30 亿参数,2 位量化)的访问。本参考涵盖了每个 API、所有 WWDC 2025 代码示例以及全面的实现模式。
30 亿参数模型,2 位量化,4096 个令牌上下文(输入 + 输出合计)。针对设备端摘要、提取、分类和生成进行了优化。不适用于世界知识、复杂推理、数学或翻译。完全在设备上运行 —— 无需网络、无成本、数据不离开设备。
在以下情况下使用本参考:
相关技能 :
axiom-foundation-models — 包含反模式、压力场景、决策树的学科技能axiom-foundation-models-diag — 用于故障排除的诊断技能LanguageModelSession 是与模型交互的核心类。它维护对话历史(记录)、处理多轮交互并管理模型状态。
基本创建 :
广告位招租
在这里展示您的产品或服务
触达数万 AI 开发者,精准高效
import FoundationModels
let session = LanguageModelSession()
使用自定义指令 :
let session = LanguageModelSession(instructions: """
你是一家像素艺术咖啡馆里友好的咖啡师。
简洁地回答玩家的问题。
"""
)
使用工具 :
let session = LanguageModelSession(
tools: [GetWeatherTool()],
instructions: "帮助用户获取天气预报。"
)
使用特定模型/用例 :
let session = LanguageModelSession(
model: SystemLanguageModel(useCase: .contentTagging)
)
指令 :
提示 :
respond(to:) 都会将提示添加到记录中安全考虑 :
基本文本生成 :
func respond(userInput: String) async throws -> String {
let session = LanguageModelSession(instructions: """
你是一个充满像素世界中的友好咖啡师。
回答玩家的问题。
"""
)
let response = try await session.respond(to: userInput)
return response.content
}
返回类型 : Response<String>,包含 .content 属性
使用 @Generable 的结构化输出 :
@Generable
struct SearchSuggestions {
@Guide(description: "建议搜索词列表", .count(4))
var searchTerms: [String]
}
let prompt = """
为一个关于参观著名地标的应用生成建议搜索词列表。
"""
let response = try await session.respond(
to: prompt,
generating: SearchSuggestions.self
)
print(response.content) // SearchSuggestions 实例
返回类型 : Response<SearchSuggestions>,包含 .content 属性
有关 GenerationOptions 的详细信息,请参阅采样与生成选项,包括 sampling:、temperature: 和 includeSchemaInPrompt:。
let session = LanguageModelSession()
// 第一轮
let firstHaiku = try await session.respond(to: "写一首关于钓鱼的俳句")
print(firstHaiku.content)
// 静水泛微光,
// 晨雾中抛线——
// 每抛皆希望。
// 第二轮 - 模型记得上下文
let secondHaiku = try await session.respond(to: "再写一首关于高尔夫的")
print(secondHaiku.content)
// 晨露静无声,
// 球童轻声指引——
// 耐心之路行。
print(session.transcript) // 显示完整历史
工作原理 :
respond() 调用都会向记录添加条目let transcript = session.transcript
for entry in transcript.entries {
print("条目: \(entry.content)")
}
使用场景 :
根据 session.isResponding 控制 UI,防止并发请求:
Button("开始!") {
Task { haiku = try await session.respond(to: prompt).content }
}
.disabled(session.isResponding)
@Generable 使模型能够使用 Swift 类型生成结构化输出。该宏在编译时生成模式,并使用约束解码来保证结构正确性。
在结构体上 :
@Generable
struct Person {
let name: String
let age: Int
}
let response = try await session.respond(
to: "生成一个人物",
generating: Person.self
)
let person = response.content // 类型安全的 Person 实例
在枚举上 :
@Generable
struct NPC {
let name: String
let encounter: Encounter
@Generable
enum Encounter {
case orderCoffee(String)
case wantToTalkToManager(complaint: String)
}
}
基本类型 :
StringInt、Float、Double、DecimalBool集合类型 :
[ElementType](数组)复合类型 :
@Generable
struct Itinerary {
var destination: String
var days: Int
var budget: Float
var rating: Double
var requiresVisa: Bool
var activities: [String]
var emergencyContact: Person
var relatedItineraries: [Itinerary] // 递归!
}
@Guide 约束生成的属性。支持 description:(自然语言)、.range()(数值边界)、.count() / .maximumCount()(数组长度)和 Regex(模式匹配)。
@Generable
struct NPC {
@Guide(description: "全名")
let name: String
@Guide(.range(1...10))
let level: Int
@Guide(.count(3))
let attributes: [String]
}
工作原理 :
@Generable 宏在编译时生成模式来自 WWDC 286 : "约束解码防止结构错误。模型被阻止生成无效的字段名或错误的类型。"
优势 :
属性按声明顺序生成 :
@Generable
struct Itinerary {
var name: String // 首先生成
var days: [DayPlan] // 其次生成
var summary: String // 最后生成
}
为何重要 :
Foundation Models 使用快照流式传输(非增量流式传输)。框架不传输原始增量,而是传输 PartiallyGenerated 类型,其可选属性会逐步填充。
@Generable 宏自动创建一个 PartiallyGenerated 嵌套类型:
@Generable
struct Itinerary {
var name: String
var days: [DayPlan]
}
// 编译器生成:
extension Itinerary {
struct PartiallyGenerated {
var name: String? // 所有属性都是可选的!
var days: [DayPlan]?
}
}
@Generable
struct Itinerary {
var name: String
var days: [Day]
}
let stream = session.streamResponse(
to: "制定一个为期 3 天的富士山行程。",
generating: Itinerary.self
)
for try await partial in stream {
print(partial) // 逐步更新的 Itinerary.PartiallyGenerated
}
返回类型 : AsyncSequence<Itinerary.PartiallyGenerated>
struct ItineraryView: View {
let session: LanguageModelSession
let dayCount: Int
let landmarkName: String
@State
private var itinerary: Itinerary.PartiallyGenerated?
var body: some View {
VStack {
if let name = itinerary?.name {
Text(name).font(.title)
}
if let days = itinerary?.days {
ForEach(days, id: \.self) { day in
DayView(day: day)
}
}
Button("开始") {
Task {
do {
let prompt = """
生成一个为期 \(dayCount) 天的 \(landmarkName) 行程。
"""
let stream = session.streamResponse(
to: prompt,
generating: Itinerary.self
)
for try await partial in stream {
self.itinerary = partial
}
} catch {
print(error)
}
}
}
}
}
}
1. 使用 SwiftUI 动画 :
if let name = itinerary?.name {
Text(name)
.transition(.opacity)
}
2. 数组的视图标识 :
// ✅ 良好 - 稳定的标识
ForEach(days, id: \.id) { day in
DayView(day: day)
}
// ❌ 不佳 - 标识会改变
ForEach(days.indices, id: \.self) { index in
DayView(day: days[index])
}
3. 属性顺序优化 :
// ✅ 良好 - 标题先出现以便流式传输
@Generable
struct Article {
var title: String // 立即显示
var summary: String // 其次显示
var fullText: String // 最后显示
}
工具让模型能够自主执行你的自定义代码以获取外部数据或执行操作。工具可与 MapKit、WeatherKit、Contacts、EventKit 或任何自定义 API 集成。
protocol Tool {
var name: String { get }
var description: String { get }
associatedtype Arguments: Generable
func call(arguments: Arguments) async throws -> ToolOutput
}
import FoundationModels
import WeatherKit
import CoreLocation
struct GetWeatherTool: Tool {
let name = "getWeather"
let description = "获取城市的最新天气信息"
@Generable
struct Arguments {
@Guide(description: "要获取天气的城市")
var city: String
}
func call(arguments: Arguments) async throws -> ToolOutput {
let places = try await CLGeocoder().geocodeAddressString(arguments.city)
let weather = try await WeatherService.shared.weather(for: places.first!.location!)
let temperature = weather.currentWeather.temperature.value
let content = GeneratedContent(properties: ["temperature": temperature])
let output = ToolOutput(content)
// 或者,如果你的工具输出是自然语言:
// let output = ToolOutput("\(arguments.city) 的温度是 \(temperature) 度。")
return output
}
}
let session = LanguageModelSession(
tools: [GetWeatherTool()],
instructions: "帮助用户获取天气预报。"
)
let response = try await session.respond(
to: "库比蒂诺的温度是多少?"
)
print(response.content)
// 库比蒂诺的温度是 71˚F!
工作原理 :
getWeather(city: "Tokyo")call() 方法来自 WWDC 301 : "模型自主决定何时以及多久调用工具。每个请求可以调用多个工具,甚至可以并行调用。"
使用 class 代替 struct 以在工具调用之间维护状态。工具实例在会话生命周期内持续存在,支持跟踪先前返回结果等模式:
class FindContactTool: Tool {
let name = "findContact"
let description = "从指定的年龄代际中查找联系人。"
var pickedContacts = Set<String>()
@Generable
struct Arguments {
let generation: Generation
@Generable
enum Generation { case babyBoomers, genX, millennial, genZ }
}
func call(arguments: Arguments) async throws -> ToolOutput {
// 获取,过滤掉已选中的,返回新联系人
pickedContacts.insert(pickedContact.givenName)
return ToolOutput(pickedContact.givenName)
}
}
两种形式 :
return ToolOutput("温度是 71°F")
let content = GeneratedContent(properties: ["temperature": 71])
return ToolOutput(content)
应该 :
getWeather、findContactget、find、fetch、create不应该 :
gtWthr来自 WWDC 301 : "工具名称和描述会逐字放入提示中。更长的字符串意味着更多令牌,从而增加延迟。"
let session = LanguageModelSession(
tools: [
GetWeatherTool(),
FindRestaurantTool(),
FindHotelTool()
],
instructions: "规划旅行行程。"
)
// 模型自主决定调用哪些工具以及何时调用
关键事实 :
来自 WWDC 301 : "当工具并行调用时,你的 call 方法可能会并发执行。访问数据时请记住这一点。"
DynamicGenerationSchema 允许在运行时而非编译时创建模式。适用于用户定义的结构、关卡创建器或动态表单。
使用 DynamicGenerationSchema.Property 构建属性,组合成模式,然后使用 GenerationSchema 验证:
// 在运行时构建模式
let questionProp = DynamicGenerationSchema.Property(
name: "question", schema: DynamicGenerationSchema(type: String.self)
)
let answersProp = DynamicGenerationSchema.Property(
name: "answers", schema: DynamicGenerationSchema(
arrayOf: DynamicGenerationSchema(referenceTo: "Answer")
)
)
let riddleSchema = DynamicGenerationSchema(name: "Riddle", properties: [questionProp, answersProp])
let answerSchema = DynamicGenerationSchema(name: "Answer", properties: [/* text, isCorrect */])
// 验证并使用
let schema = try GenerationSchema(root: riddleSchema, dependencies: [answerSchema])
let response = try await session.respond(to: "生成一个谜语", schema: schema)
let question = try response.content.value(String.self, forProperty: "question")
使用 @Generable 当 :
使用动态模式当 :
来自 WWDC 301 : "编译时 @Generable 提供类型安全。动态模式提供运行时灵活性。两者都使用相同的约束解码保证。"
贪婪采样(确定性) — 用于测试和演示。仅在同一模型版本内具有确定性:
let response = try await session.respond(
to: prompt,
options: GenerationOptions(sampling: .greedy)
)
温度 — 控制方差。0.1-0.5 专注,1.0 默认,1.5-2.0 创意:
let response = try await session.respond(
to: prompt,
options: GenerationOptions(temperature: 0.5)
)
专门适配器用于 :
标签生成
实体提取
主题检测
@Generable struct Result { let topics: [String] }
let session = LanguageModelSession( model: SystemLanguageModel(useCase: .contentTagging) )
let response = try await session.respond( to: articleText, generating: Result.self )
使用自定义指令 :
@Generable
struct Top3ActionEmotionResult {
@Guide(.maximumCount(3))
let actions: [String]
@Guide(.maximumCount(3))
let emotions: [String]
}
let session = LanguageModelSession(
model: SystemLanguageModel(useCase: .contentTagging),
instructions: "标记给定输入文本中 3 个最重要的动作和情感。"
)
let response = try await session.respond(
to: text,
generating: Top3ActionEmotionResult.self
)
捕获 LanguageModelSession.GenerationError 情况:
.exceededContextWindowSize — 超出上下文限制(4096 个令牌)。压缩记录或创建新会话。.guardrailViolation — 触发内容策略。显示优雅的消息。.unsupportedLanguageOrLocale — 不支持的语言。检查 supportedLanguages。var session = LanguageModelSession()
do {
let response = try await session.respond(to: prompt)
print(response.content)
} catch LanguageModelSession.GenerationError.exceededContextWindowSize {
// 新会话,无历史记录
session = LanguageModelSession()
}
do {
let response = try await session.respond(to: prompt)
} catch LanguageModelSession.GenerationError.exceededContextWindowSize {
// 带有部分历史记录的新会话
session = newSession(previousSession: session)
}
private func newSession(previousSession: LanguageModelSession) -> LanguageModelSession {
let allEntries = previousSession.transcript.entries
var condensedEntries = [Transcript.Entry]()
if let firstEntry = allEntries.first {
condensedEntries.append(firstEntry) // 指令
if allEntries.count > 1, let lastEntry = allEntries.last {
condensedEntries.append(lastEntry) // 最近的上下文
}
}
let condensedTranscript = Transcript(entries: condensedEntries)
// 注意:记录包含指令
return LanguageModelSession(transcript: condensedTranscript)
}
当 Foundation Models 不可用时(旧设备、用户选择退出、不支持的区域),提供优雅降级:
func summarize(_ text: String) async throws -> String {
let model = SystemLanguageModel.default
switch model.availability {
case .available:
let session = LanguageModelSession()
let response = try await session.respond(to: "总结:\(text)")
return response.content
case .unavailable:
// 后备方案:用省略号截断,或调用服务器 API
return String(text.prefix(200)) + "..."
}
}
架构模式 : 将 Foundation Models 包装在协议后面,以便可以交换实现:
protocol TextSummarizer {
func summarize(_ text: String) async throws -> String
}
struct OnDeviceSummarizer: TextSummarizer { /* Foundation Models */ }
struct ServerSummarizer: TextSummarizer { /* 服务器 API 后备 */ }
struct TruncationSummarizer: TextSummarizer { /* 简单截断 */ }
嵌套的 @Generable 类型必须各自独立地符合 @Generable:
// ✅ 两种类型都标记为 @Generable
@Generable struct Itinerary {
var days: [DayPlan]
}
@Generable struct DayPlan {
var activities: [String]
}
// ❌ 会失败 — 嵌套类型不是 @Generable
@Generable struct Itinerary {
var days: [DayPlan] // DayPlan 也必须是 @Generable
}
struct DayPlan { var activities: [String] }
常见问题 : 非 Generable 类型的数组可以编译,但会在运行时失败。检查图中的所有类型。
struct AvailabilityExample: View {
private let model = SystemLanguageModel.default
var body: some View {
switch model.availability {
case .available:
Text("模型可用").foregroundStyle(.green)
case .unavailable(let reason):
Text("模型不可用").foregroundStyle(.red)
Text("原因:\(reason)")
}
}
}
let supportedLanguages = SystemLanguageModel.default.supportedLanguages
guard supportedLanguages.contains(Locale.current.language) else {
// 显示消息
return
}
设备要求 :
区域要求 :
用户要求 :
访问 : Instruments 应用 → Foundation Models 模板
指标 :
来自 WWDC 286 : "新的 Instruments 性能分析模板让你能够观察优化领域并量化改进。"
问题 : 第一个请求需要 1-2 秒加载模型
解决方案 : 在用户交互之前创建会话
class ViewModel: ObservableObject {
private var session: LanguageModelSession?
init() {
// 在初始化时预热
Task {
self.session = LanguageModelSession(instructions: "...")
}
}
func generate(prompt: String) async throws -> String {
let response = try await session!.respond(to: prompt)
return response.content
}
}
来自 WWDC 259 : "在用户交互之前预热会话可以减少初始延迟。"
节省时间 : 首次生成减少 1-2 秒
问题 : 大型 @Generable 模式增加令牌数量
解决方案 : 为后续请求跳过模式插入
// 第一个请求 - 插入模式
let first = try await session.respond(
to: "生成第一个人物",
generating: Person.self
)
// 后续请求 - 跳过模式
let second = try await session.respond(
to: "生成另一个人物",
generating: Person.self,
options: GenerationOptions(includeSchemaInPrompt: false)
)
来自 WWDC 259 : "将 includeSchemaInPrompt 设置为 false 可减少后续请求的令牌数量和延迟。"
节省时间 : 每个请求减少 10-20%
在 @Generable 结构体中首先声明重要属性。使用流式传输时,当标题在完整文本之前出现时,感知延迟从 2.5 秒降至 0.2 秒。有关示例,请参阅流式传输最佳实践。
LanguageModelFeedbackAttachment 允许你通过 Feedback Assistant 向苹果报告模型质量问题。使用 input、output、sentiment(.positive/.negative)、issues(类别 + 解释)和 desiredOutputExamples 创建。编码为 JSON 并附加到 Feedback Assistant 报告中。
Xcode Playgrounds 无需重建整个应用即可快速迭代提示。
import FoundationModels
import Playgrounds
#Playground {
let session = LanguageModelSession()
let response = try await session.respond(
to: "去日本旅行取什么名字好?只回复标题"
)
}
Playgrounds 还可以访问应用中定义的类型(如 @Generable 结构体)。
LanguageModelSession — 主要接口:respond(to:) → Response<String>、respond(to:generating:) → Response<T>、streamResponse(to:generating:) → AsyncSequence<T.PartiallyGenerated>。属性:transcript、isResponding。SystemLanguageModel — default.availability(.available/.unavailable(reason))、default.supportedLanguages、init(useCase:)GenerationOptions — sampling(.greedy/.random)、temperature、includeSchemaInPrompt@Generable — 启用结构化输出和约束解码的宏@Guide — 属性约束:description:、.range()、.count()、.maximumCount()、RegexTool 协议 — name、description、Arguments: Generable、call(arguments:) → ToolOutputDynamicGenerationSchema — 运行时模式定义,输出为 GeneratedContentGenerationError — .exceededContextWindowSize、.guardrailViolation、.unsupportedLanguageOrLocale使用 @Generable 和 respond(to:generating:) 代替提示 JSON 并手动解析。有关完整的迁移模式,请参阅 axiom-foundation-models 场景 2。
WWDC : 286, 259, 301
技能 : axiom-foundation-models, axiom-foundation-models-diag
最后更新 : 2025-12-03 版本 : 1.0.0 技能类型 : 参考 内容 : 包含所有 WWDC 2025 代码示例
每周安装量
107
仓库
GitHub 星标数
606
首次出现
2026 年 1 月 21 日
安全审计
安装于
opencode92
codex86
claude-code85
gemini-cli83
cursor82
github-copilot79
The Foundation Models framework provides access to Apple's on-device Large Language Model (3 billion parameters, 2-bit quantized) with a Swift API. This reference covers every API, all WWDC 2025 code examples, and comprehensive implementation patterns.
3B parameter model, 2-bit quantized, 4096 token context (input + output combined). Optimized for on-device summarization, extraction, classification, and generation. NOT suited for world knowledge, complex reasoning, math, or translation. Runs entirely on-device — no network, no cost, no data leaves device.
Use this reference when:
Related Skills :
axiom-foundation-models — Discipline skill with anti-patterns, pressure scenarios, decision treesaxiom-foundation-models-diag — Diagnostic skill for troubleshooting issuesLanguageModelSession is the core class for interacting with the model. It maintains conversation history (transcript), handles multi-turn interactions, and manages model state.
Basic Creation :
import FoundationModels
let session = LanguageModelSession()
With Custom Instructions :
let session = LanguageModelSession(instructions: """
You are a friendly barista in a pixel art coffee shop.
Respond to the player's question concisely.
"""
)
With Tools :
let session = LanguageModelSession(
tools: [GetWeatherTool()],
instructions: "Help user with weather forecasts."
)
With Specific Model/Use Case :
let session = LanguageModelSession(
model: SystemLanguageModel(useCase: .contentTagging)
)
Instructions :
Prompts :
respond(to:) adds prompt to transcriptSecurity Consideration :
Basic Text Generation :
func respond(userInput: String) async throws -> String {
let session = LanguageModelSession(instructions: """
You are a friendly barista in a world full of pixels.
Respond to the player's question.
"""
)
let response = try await session.respond(to: userInput)
return response.content
}
Return Type : Response<String> with .content property
Structured Output with @Generable :
@Generable
struct SearchSuggestions {
@Guide(description: "A list of suggested search terms", .count(4))
var searchTerms: [String]
}
let prompt = """
Generate a list of suggested search terms for an app about visiting famous landmarks.
"""
let response = try await session.respond(
to: prompt,
generating: SearchSuggestions.self
)
print(response.content) // SearchSuggestions instance
Return Type : Response<SearchSuggestions> with .content property
See Sampling & Generation Options for GenerationOptions including sampling:, temperature:, and includeSchemaInPrompt:.
let session = LanguageModelSession()
// First turn
let firstHaiku = try await session.respond(to: "Write a haiku about fishing")
print(firstHaiku.content)
// Silent waters gleam,
// Casting lines in morning mist—
// Hope in every cast.
// Second turn - model remembers context
let secondHaiku = try await session.respond(to: "Do another one about golf")
print(secondHaiku.content)
// Silent morning dew,
// Caddies guide with gentle words—
// Paths of patience tread.
print(session.transcript) // Shows full history
How it works :
respond() call adds entry to transcriptlet transcript = session.transcript
for entry in transcript.entries {
print("Entry: \(entry.content)")
}
Use cases :
Gate UI on session.isResponding to prevent concurrent requests:
Button("Go!") {
Task { haiku = try await session.respond(to: prompt).content }
}
.disabled(session.isResponding)
@Generable enables structured output from the model using Swift types. The macro generates a schema at compile-time and uses constrained decoding to guarantee structural correctness.
On Structs :
@Generable
struct Person {
let name: String
let age: Int
}
let response = try await session.respond(
to: "Generate a person",
generating: Person.self
)
let person = response.content // Type-safe Person instance
On Enums :
@Generable
struct NPC {
let name: String
let encounter: Encounter
@Generable
enum Encounter {
case orderCoffee(String)
case wantToTalkToManager(complaint: String)
}
}
Primitives :
StringInt, Float, Double, DecimalBoolCollections :
[ElementType] (arrays)Composed Types :
@Generable
struct Itinerary {
var destination: String
var days: Int
var budget: Float
var rating: Double
var requiresVisa: Bool
var activities: [String]
var emergencyContact: Person
var relatedItineraries: [Itinerary] // Recursive!
}
@Guide constrains generated properties. Supports description: (natural language), .range() (numeric bounds), .count() / .maximumCount() (array length), and Regex (pattern matching).
@Generable
struct NPC {
@Guide(description: "A full name")
let name: String
@Guide(.range(1...10))
let level: Int
@Guide(.count(3))
let attributes: [String]
}
How it works :
@Generable macro generates schema at compile-timeFrom WWDC 286 : "Constrained decoding prevents structural mistakes. Model is prevented from generating invalid field names or wrong types."
Benefits :
Properties generated in order declared :
@Generable
struct Itinerary {
var name: String // Generated FIRST
var days: [DayPlan] // Generated SECOND
var summary: String // Generated LAST
}
Why it matters :
Foundation Models uses snapshot streaming (not delta streaming). Instead of raw deltas, the framework streams PartiallyGenerated types with optional properties that fill in progressively.
The @Generable macro automatically creates a PartiallyGenerated nested type:
@Generable
struct Itinerary {
var name: String
var days: [DayPlan]
}
// Compiler generates:
extension Itinerary {
struct PartiallyGenerated {
var name: String? // All properties optional!
var days: [DayPlan]?
}
}
@Generable
struct Itinerary {
var name: String
var days: [Day]
}
let stream = session.streamResponse(
to: "Craft a 3-day itinerary to Mt. Fuji.",
generating: Itinerary.self
)
for try await partial in stream {
print(partial) // Incrementally updated Itinerary.PartiallyGenerated
}
Return Type : AsyncSequence<Itinerary.PartiallyGenerated>
struct ItineraryView: View {
let session: LanguageModelSession
let dayCount: Int
let landmarkName: String
@State
private var itinerary: Itinerary.PartiallyGenerated?
var body: some View {
VStack {
if let name = itinerary?.name {
Text(name).font(.title)
}
if let days = itinerary?.days {
ForEach(days, id: \.self) { day in
DayView(day: day)
}
}
Button("Start") {
Task {
do {
let prompt = """
Generate a \(dayCount) itinerary \
to \(landmarkName).
"""
let stream = session.streamResponse(
to: prompt,
generating: Itinerary.self
)
for try await partial in stream {
self.itinerary = partial
}
} catch {
print(error)
}
}
}
}
}
}
1. Use SwiftUI animations :
if let name = itinerary?.name {
Text(name)
.transition(.opacity)
}
2. View identity for arrays :
// ✅ GOOD - Stable identity
ForEach(days, id: \.id) { day in
DayView(day: day)
}
// ❌ BAD - Identity changes
ForEach(days.indices, id: \.self) { index in
DayView(day: days[index])
}
3. Property order optimization :
// ✅ GOOD - Title first for streaming
@Generable
struct Article {
var title: String // Shows immediately
var summary: String // Shows second
var fullText: String // Shows last
}
Tools let the model autonomously execute your custom code to fetch external data or perform actions. Tools integrate with MapKit, WeatherKit, Contacts, EventKit, or any custom API.
protocol Tool {
var name: String { get }
var description: String { get }
associatedtype Arguments: Generable
func call(arguments: Arguments) async throws -> ToolOutput
}
import FoundationModels
import WeatherKit
import CoreLocation
struct GetWeatherTool: Tool {
let name = "getWeather"
let description = "Retrieve the latest weather information for a city"
@Generable
struct Arguments {
@Guide(description: "The city to fetch the weather for")
var city: String
}
func call(arguments: Arguments) async throws -> ToolOutput {
let places = try await CLGeocoder().geocodeAddressString(arguments.city)
let weather = try await WeatherService.shared.weather(for: places.first!.location!)
let temperature = weather.currentWeather.temperature.value
let content = GeneratedContent(properties: ["temperature": temperature])
let output = ToolOutput(content)
// Or if your tool's output is natural language:
// let output = ToolOutput("\(arguments.city)'s temperature is \(temperature) degrees.")
return output
}
}
let session = LanguageModelSession(
tools: [GetWeatherTool()],
instructions: "Help the user with weather forecasts."
)
let response = try await session.respond(
to: "What is the temperature in Cupertino?"
)
print(response.content)
// It's 71˚F in Cupertino!
How it works :
getWeather(city: "Tokyo")call() methodFrom WWDC 301 : "Model autonomously decides when and how often to call tools. Can call multiple tools per request, even in parallel."
Use class instead of struct to maintain state across tool calls. The tool instance persists for the session lifetime, enabling patterns like tracking previously returned results:
class FindContactTool: Tool {
let name = "findContact"
let description = "Finds a contact from a specified age generation."
var pickedContacts = Set<String>()
@Generable
struct Arguments {
let generation: Generation
@Generable
enum Generation { case babyBoomers, genX, millennial, genZ }
}
func call(arguments: Arguments) async throws -> ToolOutput {
// Fetch, filter out already-picked, return new contact
pickedContacts.insert(pickedContact.givenName)
return ToolOutput(pickedContact.givenName)
}
}
Two forms :
return ToolOutput("Temperature is 71°F")
let content = GeneratedContent(properties: ["temperature": 71])
return ToolOutput(content)
DO :
getWeather, findContactget, find, fetch, createDON'T :
gtWthrFrom WWDC 301 : "Tool name and description put verbatim in prompt. Longer strings mean more tokens, which increases latency."
let session = LanguageModelSession(
tools: [
GetWeatherTool(),
FindRestaurantTool(),
FindHotelTool()
],
instructions: "Plan travel itineraries."
)
// Model autonomously decides which tools to call and when
Key facts :
From WWDC 301 : "When tools called in parallel, your call method may execute concurrently. Keep this in mind when accessing data."
DynamicGenerationSchema enables creating schemas at runtime instead of compile-time. Useful for user-defined structures, level creators, or dynamic forms.
Build properties with DynamicGenerationSchema.Property, compose into schemas, then validate with GenerationSchema:
// Build schema at runtime
let questionProp = DynamicGenerationSchema.Property(
name: "question", schema: DynamicGenerationSchema(type: String.self)
)
let answersProp = DynamicGenerationSchema.Property(
name: "answers", schema: DynamicGenerationSchema(
arrayOf: DynamicGenerationSchema(referenceTo: "Answer")
)
)
let riddleSchema = DynamicGenerationSchema(name: "Riddle", properties: [questionProp, answersProp])
let answerSchema = DynamicGenerationSchema(name: "Answer", properties: [/* text, isCorrect */])
// Validate and use
let schema = try GenerationSchema(root: riddleSchema, dependencies: [answerSchema])
let response = try await session.respond(to: "Generate a riddle", schema: schema)
let question = try response.content.value(String.self, forProperty: "question")
Use @Generable when :
Use Dynamic Schemas when :
From WWDC 301 : "Compile-time @Generable gives type safety. Dynamic schemas give runtime flexibility. Both use same constrained decoding guarantees."
Greedy (deterministic) — use for tests and demos. Only deterministic within same model version:
let response = try await session.respond(
to: prompt,
options: GenerationOptions(sampling: .greedy)
)
Temperature — controls variance. 0.1-0.5 focused, 1.0 default, 1.5-2.0 creative:
let response = try await session.respond(
to: prompt,
options: GenerationOptions(temperature: 0.5)
)
Specialized adapter for :
Tag generation
Entity extraction
Topic detection
@Generable struct Result { let topics: [String] }
let session = LanguageModelSession( model: SystemLanguageModel(useCase: .contentTagging) )
let response = try await session.respond( to: articleText, generating: Result.self )
With custom instructions :
@Generable
struct Top3ActionEmotionResult {
@Guide(.maximumCount(3))
let actions: [String]
@Guide(.maximumCount(3))
let emotions: [String]
}
let session = LanguageModelSession(
model: SystemLanguageModel(useCase: .contentTagging),
instructions: "Tag the 3 most important actions and emotions in the given input text."
)
let response = try await session.respond(
to: text,
generating: Top3ActionEmotionResult.self
)
Catch LanguageModelSession.GenerationError cases:
.exceededContextWindowSize — Context limit (4096 tokens) exceeded. Condense transcript or create new session..guardrailViolation — Content policy triggered. Show graceful message..unsupportedLanguageOrLocale — Language not supported. Check supportedLanguages.var session = LanguageModelSession()
do {
let response = try await session.respond(to: prompt)
print(response.content)
} catch LanguageModelSession.GenerationError.exceededContextWindowSize {
// New session, no history
session = LanguageModelSession()
}
do {
let response = try await session.respond(to: prompt)
} catch LanguageModelSession.GenerationError.exceededContextWindowSize {
// New session with some history
session = newSession(previousSession: session)
}
private func newSession(previousSession: LanguageModelSession) -> LanguageModelSession {
let allEntries = previousSession.transcript.entries
var condensedEntries = [Transcript.Entry]()
if let firstEntry = allEntries.first {
condensedEntries.append(firstEntry) // Instructions
if allEntries.count > 1, let lastEntry = allEntries.last {
condensedEntries.append(lastEntry) // Recent context
}
}
let condensedTranscript = Transcript(entries: condensedEntries)
// Note: transcript includes instructions
return LanguageModelSession(transcript: condensedTranscript)
}
When Foundation Models is unavailable (older device, user opted out, unsupported region), provide graceful degradation:
func summarize(_ text: String) async throws -> String {
let model = SystemLanguageModel.default
switch model.availability {
case .available:
let session = LanguageModelSession()
let response = try await session.respond(to: "Summarize: \(text)")
return response.content
case .unavailable:
// Fallback: truncate with ellipsis, or call server API
return String(text.prefix(200)) + "..."
}
}
Architecture pattern : Wrap Foundation Models behind a protocol so you can swap implementations:
protocol TextSummarizer {
func summarize(_ text: String) async throws -> String
}
struct OnDeviceSummarizer: TextSummarizer { /* Foundation Models */ }
struct ServerSummarizer: TextSummarizer { /* Server API fallback */ }
struct TruncationSummarizer: TextSummarizer { /* Simple truncation */ }
Nested @Generable types must each independently conform to @Generable:
// ✅ Both types marked @Generable
@Generable struct Itinerary {
var days: [DayPlan]
}
@Generable struct DayPlan {
var activities: [String]
}
// ❌ Will fail — nested type not @Generable
@Generable struct Itinerary {
var days: [DayPlan] // DayPlan must also be @Generable
}
struct DayPlan { var activities: [String] }
Common issue : Arrays of non-Generable types compile but fail at runtime. Check all types in the graph.
struct AvailabilityExample: View {
private let model = SystemLanguageModel.default
var body: some View {
switch model.availability {
case .available:
Text("Model is available").foregroundStyle(.green)
case .unavailable(let reason):
Text("Model is unavailable").foregroundStyle(.red)
Text("Reason: \(reason)")
}
}
}
let supportedLanguages = SystemLanguageModel.default.supportedLanguages
guard supportedLanguages.contains(Locale.current.language) else {
// Show message
return
}
Device Requirements :
Region Requirements :
User Requirements :
Access : Instruments app → Foundation Models template
Metrics :
From WWDC 286 : "New Instruments profiling template lets you observe areas of optimization and quantify improvements."
Problem : First request takes 1-2s to load model
Solution : Create session before user interaction
class ViewModel: ObservableObject {
private var session: LanguageModelSession?
init() {
// Prewarm on init
Task {
self.session = LanguageModelSession(instructions: "...")
}
}
func generate(prompt: String) async throws -> String {
let response = try await session!.respond(to: prompt)
return response.content
}
}
From WWDC 259 : "Prewarming session before user interaction reduces initial latency."
Time saved : 1-2 seconds off first generation
Problem : Large @Generable schemas increase token count
Solution : Skip schema insertion for subsequent requests
// First request - schema inserted
let first = try await session.respond(
to: "Generate first person",
generating: Person.self
)
// Subsequent requests - skip schema
let second = try await session.respond(
to: "Generate another person",
generating: Person.self,
options: GenerationOptions(includeSchemaInPrompt: false)
)
From WWDC 259 : "Setting includeSchemaInPrompt to false decreases token count and latency for subsequent requests."
Time saved : 10-20% per request
Declare important properties first in @Generable structs. With streaming, perceived latency drops from 2.5s to 0.2s when title appears before full text. See Streaming Best Practices for examples.
LanguageModelFeedbackAttachment lets you report model quality issues to Apple via Feedback Assistant. Create with input, output, sentiment (.positive/.negative), issues (category + explanation), and desiredOutputExamples. Encode as JSON and attach to a Feedback Assistant report.
Xcode Playgrounds enable rapid iteration on prompts without rebuilding entire app.
import FoundationModels
import Playgrounds
#Playground {
let session = LanguageModelSession()
let response = try await session.respond(
to: "What's a good name for a trip to Japan? Respond only with a title"
)
}
Playgrounds can also access types defined in your app (like @Generable structs).
LanguageModelSession — Main interface: respond(to:) → Response<String>, respond(to:generating:) → Response<T>, streamResponse(to:generating:) → AsyncSequence<T.PartiallyGenerated>. Properties: transcript, isResponding.SystemLanguageModel — (/), , Use @Generable with respond(to:generating:) instead of prompting for JSON and parsing manually. See axiom-foundation-models Scenario 2 for the complete migration pattern.
WWDC : 286, 259, 301
Skills : axiom-foundation-models, axiom-foundation-models-diag
Last Updated : 2025-12-03 Version : 1.0.0 Skill Type : Reference Content : All WWDC 2025 code examples included
Weekly Installs
107
Repository
GitHub Stars
606
First Seen
Jan 21, 2026
Security Audits
Gen Agent Trust HubPassSocketPassSnykPass
Installed on
opencode92
codex86
claude-code85
gemini-cli83
cursor82
github-copilot79
AI 代码实施计划编写技能 | 自动化开发任务分解与 TDD 流程规划工具
50,900 周安装
default.availability.available.unavailable(reason)default.supportedLanguagesinit(useCase:)GenerationOptions — sampling (.greedy/.random), temperature, includeSchemaInPrompt@Generable — Macro enabling structured output with constrained decoding@Guide — Property constraints: description:, .range(), .count(), .maximumCount(), RegexTool protocol — name, description, Arguments: Generable, call(arguments:) → ToolOutputDynamicGenerationSchema — Runtime schema definition with GeneratedContent outputGenerationError — .exceededContextWindowSize, .guardrailViolation, .unsupportedLanguageOrLocale