New to Claude Skills? Learn how to install them →

affaan-m on GitHub

On-Device Foundation Models

Free

Integrate Apple's LLM for privacy-focused AI features.

by affaan-m239.3k stars on affaan-m/ecc
2 views
Updated Aug 10, 2026
Get this skill

Free · Opens the source repo

What On-Device Foundation Models does

The On-Device Foundation Models skill provides developers with a framework for integrating Apple's on-device language model into their applications. This skill is particularly useful for creating AI-powered features that operate without relying on cloud services, ensuring user data remains private and secure. By leveraging the FoundationModels framework, developers can implement functionalities such as text generation, structured output, and real-time data extraction directly on users' devices.

With this skill, developers can build applications that generate or summarize text, extract structured data from natural language inputs, and implement custom tool calls for specific domain-related tasks. The skill supports streaming responses for real-time UI updates, making it ideal for applications that require immediate feedback based on user interactions. The focus on on-device execution means that applications can function offline, providing a seamless experience for users regardless of their internet connectivity.

The skill also includes essential patterns for checking model availability and managing language model sessions. Developers can create single-turn or multi-turn sessions to maintain context in conversations, ensuring a more coherent interaction. The guided generation feature allows for defining structured types, enhancing the quality of generated data and ensuring it meets specific requirements. Additionally, the snapshot streaming capability enables developers to present partially generated results in real-time, enhancing user engagement and experience.

Overall, this skill is designed for developers looking to harness the power of on-device AI while prioritizing user privacy and data security. It is particularly suited for applications in domains such as personal assistants, content generation, and interactive user interfaces where real-time feedback is crucial.

When to use it

Use this skill when building AI features that require text generation, summarization, or data extraction on iOS devices.

When not to use it

Avoid this skill if your application needs extensive cloud-based processing or if you are targeting platforms other than iOS.

What you can build with it

Building a Personal Assistant

Develop a personal assistant app that generates responses to user queries using Apple's on-device language model.

Content Generation Tool

Create an app that summarizes articles or generates content based on user input without needing internet access.

Interactive Learning Application

Implement an educational app that extracts structured data from user inquiries, providing instant feedback and assistance.

How to install On-Device Foundation Models

View source

1. Install with the skills CLI

npx skills add affaan-m/ecc/foundation-models-on-device --agent claude-code

2. Or install it manually

Download the skill folder and drop it into ~/.claude/skills/ for all projects, or .claude/skills/ to scope it to one repo. Restart Claude Code so it picks up the new skill.

Anthropic's agentic coding CLI, and the reference implementation of Agent Skills. Drop a skill folder into ~/.claude/skills and Claude Code loads it automatically whenever a task matches the skill's description. Claude Code docs

Inside SKILL.md

Written by affaan-m

FoundationModels: On-Device LLM (iOS 26)

Patterns for integrating Apple's on-device language model into apps using the FoundationModels framework. Covers text generation, structured output with @Generable, custom tool calling, and snapshot streaming — all running on-device for privacy and offline support.

When to Activate

  • Building AI-powered features using Apple Intelligence on-device
  • Generating or summarizing text without cloud dependency
  • Extracting structured data from natural language input
  • Implementing custom tool calling for domain-specific AI actions
  • Streaming structured responses for real-time UI updates
  • Need privacy-preserving AI (no data leaves the device)

Core Pattern — Availability Check

Always check model availability before creating a session:

struct GenerativeView: View {
    private var model = SystemLanguageModel.default

    var body: some View {
        switch model.availability {
        case .available:
            ContentView()
        case .unavailable(.deviceNotEligible):
            Text("Device not eligible for Apple Intelligence")
        case .unavailable(.appleIntelligenceNotEnabled):
            Text("Please enable Apple Intelligence in Settings")
        case .unavailable(.modelNotReady):
            Text("Model is downloading or not ready")
        case .unavailable(let other):
            Text("Model unavailable: \(other)")
        }
    }
}

Core Pattern — Basic Session

// Single-turn: create a new session each time
let session = LanguageModelSession()
let response = try await session.respond(to: "What's a good month to visit Paris?")
print(response.content)

// Multi-turn: reuse session for conversation context
let session = LanguageModelSession(instructions: """
    You are a cooking assistant.
    Provide recipe suggestions based on ingredients.
    Keep suggestions brief and practical.
    """)

let first = try await session.respond(to: "I have chicken and rice")
let followUp = try await session.respond(to: "What about a vegetarian option?")

Key points for instructions:

  • Define the model's role ("You are a mentor")
  • Specify what to do ("Help extract calendar events")
  • Set style preferences ("Respond as briefly as possible")
  • Add safety measures ("Respond with 'I can't help with that' for dangerous requests")

Core Pattern — Guided Generation with @Generable

Generate structured Swift types instead of raw strings:

1. Define a Generable Type

@Generable(description: "Basic profile information about a cat")
struct CatProfile {
    var name: String

    @Guide(description: "The age of the cat", .range(0...20))
    var age: Int

    @Guide(description: "A one sentence profile about the cat's personality")
    var profile: String
}

2. Request Structured Output

let response = try await session.respond(
    to: "Generate a cute rescue cat",
    generating: CatProfile.self
)

// Access structured fields directly
print("Name: \(response.content.name)")
print("Age: \(response.content.age)")
print("Profile: \(response.content.profile)")

Supported @Guide Constraints

  • .range(0...20) — numeric range
  • .count(3) — array element count
  • description: — semantic guidance for generation

Core Pattern — Tool Calling

Let the model invoke custom code for domain-specific tasks:

1. Define a Tool

struct RecipeSearchTool: Tool {
    let name = "recipe_search"
    let description = "Search for recipes matching a given term and return a list of results."

    @Generable
    struct Arguments {
        var searchTerm: String
        var numberOfResults: Int
    }

    func call(arguments: Arguments) async throws -> ToolOutput {
        let recipes = await searchRecipes(
            term: arguments.searchTerm,
            limit: arguments.numberOfResults
        )
        return .string(recipes.map { "- \($0.name): \($0.description)" }.joined(separator: "\n"))
    }
}

2. Create Session with Tools

let session = LanguageModelSession(tools: [RecipeSearchTool()])
let response = try await session.respond(to: "Find me some pasta recipes")

3. Handle Tool Errors

do {
    let answer = try await session.respond(to: "Find a recipe for tomato soup.")
} catch let error as LanguageModelSession.ToolCallError {
    print(error.tool.name)
    if case .databaseIsEmpty = error.underlyingError as? RecipeSearchToolError {
        // Handle specific tool error
    }
}

Core Pattern — Snapshot Streaming

Stream structured responses for real-time UI with PartiallyGenerated types:

@Generable
struct TripIdeas {
    @Guide(description: "Ideas for upcoming trips")
    var ideas: [String]
}

let stream = session.streamResponse(
    to: "What are some exciting trip ideas?",
    generating: TripIdeas.self
)

for try await partial in stream {
    // partial: TripIdeas.PartiallyGenerated (all properties Optional)
    print(partial)
}

SwiftUI Integration

@State private var partialResult: TripIdeas.PartiallyGenerated?
@State private var errorMessage: String?

var body: some View {
    List {
        ForEach(partialResult?.ideas ?? [], id: \.self) { idea in
            Text(idea)
        }
    }
    .overlay {
        if let errorMessage { Text(errorMessage).foregroundStyle(.red) }
    }
    .task {
        do {
            let stream = session.streamResponse(to: prompt, generating: TripIdeas.self)
            for try await partial in stream {
                partialResult = partial
            }
        } catch {
            errorMessage = error.localizedDescription
        }
    }
}

Key Design Decisions

DecisionRationale
On-device executionPrivacy — no data leaves the device; works offline
4,096 token limitOn-device model constraint; chunk large data across sessions
Snapshot streaming (not deltas)Structured output friendly; each snapshot is a complete partial state
@Generable macroCompile-time safety for structured generation; auto-generates PartiallyGenerated type
Single request per sessionisResponding prevents concurrent requests; create multiple sessions if needed
response.content (not .output)Correct API — always access results via .content property

Best Practices

  • Always check model.availability before creating a session — handle all unavailability cases
  • Use instructions to guide model behavior — they take priority over prompts
  • Check isResponding before sending a new request — sessions handle one request at a time
  • Access response.content for results — not .output
  • Break large inputs into chunks — 4,096 token limit applies to instructions + prompt + output combined
  • Use @Generable for structured output — stronger guarantees than parsing raw strings
  • Use GenerationOptions(temperature:) to tune creativity (higher = more creative)
  • Monitor with Instruments — use Xcode Instruments to profile request performance

Anti-Patterns to Avoid

  • Creating sessions without checking model.availability first
  • Sending inputs exceeding the 4,096 token context window
  • Attempting concurrent requests on a single session
  • Using .output instead of .content to access response data
  • Parsing raw string responses when @Generable structured output would work
  • Building complex multi-step logic in a single prompt — break into multiple focused prompts
  • Assuming the model is always available — device eligibility and settings vary

When to Use

  • On-device text generation for privacy-sensitive apps
  • Structured data extraction from user input (forms, natural language commands)
  • AI-assisted features that must work offline
  • Streaming UI that progressively shows generated content
  • Domain-specific AI actions via tool calling (search, compute, lookup)

Frequently asked questions about On-Device Foundation Models

Similar skills