Apple Intelligence Foundation Models for NativePHP Mobile#
Use Apple's on-device Foundation Models framework from a NativePHP Mobile app.
This plugin gives Laravel, Livewire, Inertia, Vue, React, and plain JavaScript apps a typed bridge to Apple Intelligence on iOS: availability diagnostics, explicit language model sessions, structured JSON responses, streaming snapshots, optional app-provided tool context, transcript helpers, cancellation, token/context metadata, PHP events, and a TypeScript client.
It is intentionally iOS-only. Android builds receive clean unsupported_platform responses.
License: proprietary. Read LICENSE.md before using, distributing, or reselling this package.
Requirements#
| Requirement | Version / Notes |
|---|---|
| PHP | ^8.2 |
| NativePHP Mobile | ^3 |
| iOS build target | 18.2+ |
| Foundation Models runtime | iOS 26.0+, Apple Intelligence-capable device, Apple Intelligence enabled, model assets ready |
| Android | Not supported by this plugin |
The plugin keeps the NativePHP iOS deployment floor at 18.2 so older projects can compile. Foundation Models calls are guarded with iOS availability checks and return unsupported_platform on unsupported OS versions.
Installation#
composer require weswecan/nativephp-mobile-apple-intelligence-foundation-modelsphp artisan native:plugin:register weswecan/nativephp-mobile-apple-intelligence-foundation-models
If this is the first NativePHP plugin in your app, publish the plugins provider first:
php artisan vendor:publish --tag=nativephp-plugins-provider
Your NativePHP plugins provider should include:
Weswecan\AppleFoundationModels\AppleFoundationModelsServiceProvider::class
JavaScript Setup#
Add the package import alias to your app's package.json:
{ "imports": { "#apple-foundation-models": { "types": "./vendor/weswecan/nativephp-mobile-apple-intelligence-foundation-models/resources/js/dist/index.d.ts", "default": "./vendor/weswecan/nativephp-mobile-apple-intelligence-foundation-models/resources/js/dist/index.js" } }}
The JavaScript bridge prefers Laravel's window.axios instance and falls back to XMLHttpRequest when Axios is not present.
Quick Start#
JavaScript#
import { appleFoundationModels } from '#apple-foundation-models'; const availability = await appleFoundationModels.availability(); if (!availability.available) { console.warn(availability.reason, availability.message); return;} const session = await appleFoundationModels.createSession({ id: 'release-assistant', instructions: 'Answer like a concise Laravel developer.',}); const response = await appleFoundationModels.respond(session.sessionId, 'Summarize the release.', { type: 'object', properties: { summary: { type: 'string' }, risk: { type: 'string', enum: ['low', 'medium', 'high'] }, }, required: ['summary', 'risk'],}); console.log(response);
PHP#
use Weswecan\AppleFoundationModels\Facades\AppleFoundationModels; $availability = AppleFoundationModels::availability(); if (! ($availability['available'] ?? false)) { return;} $session = AppleFoundationModels::createSession([ 'id' => 'release-assistant', 'instructions' => 'Answer like a concise Laravel developer.',]); AppleFoundationModels::respond('release-assistant', 'Extract action items.', [ 'type' => 'object', 'properties' => [ 'items' => [ 'type' => 'array', 'items' => [ 'type' => 'object', 'properties' => [ 'title' => ['type' => 'string'], 'priority' => ['type' => 'string', 'enum' => ['low', 'medium', 'high']], ], 'required' => ['title', 'priority'], ], ], ], 'required' => ['items'],]);
Developer Experience Helpers#
The low-level object-literal API remains supported. The JavaScript client also ships small helper functions that return the same schema and tool definition shapes while making app code easier to read.
Schema builders#
import { foundationSchema } from '#apple-foundation-models'; const responseSchema = foundationSchema.object({ summary: foundationSchema.string({ description: 'A concise generated summary.', }), risk: foundationSchema.enum(['low', 'medium', 'high'], { description: 'How risky this change looks.', }), nextActions: foundationSchema.array(foundationSchema.string()),}, ['summary', 'risk', 'nextActions']);
This is equivalent to writing the schema object by hand:
const responseSchema = { type: 'object', properties: { summary: { type: 'string', description: 'A concise generated summary.' }, risk: { type: 'string', enum: ['low', 'medium', 'high'], description: 'How risky this change looks.' }, nextActions: { type: 'array', items: { type: 'string' } }, }, required: ['summary', 'risk', 'nextActions'],};
Tool definition helpers#
defineFoundationModelTools() keeps tool definitions and JavaScript handlers together, then gives you the two maps expected by the existing API.
import { appleFoundationModels, defineFoundationModelTools, foundationSchema,} from '#apple-foundation-models'; const tools = defineFoundationModelTools({ currentUserPlan: { description: 'Returns the current user subscription plan.', timeout: 5, schema: foundationSchema.object({}), async handler() { return { plan: 'pro' }; }, },}); await appleFoundationModels.registerTools('support-agent', tools.definitions); const stopHandlingTools = appleFoundationModels.handleTools(tools.handlers); const response = await appleFoundationModels.respond( 'support-agent', 'Answer using the current plan if needed.', foundationSchema.object({ answer: foundationSchema.string(), }, ['answer']), { toolChoice: tools.names, },); stopHandlingTools();
Display helpers#
Structured and streamed output can arrive as objects, JSON strings, double-encoded JSON, fenced JSON, or snapshot-like values. Use the output helpers when rendering generated content in a Vue, React, or plain JavaScript UI.
import { normalizeFoundationModelsOutput, readableFoundationModelsText,} from '#apple-foundation-models'; const normalized = normalizeFoundationModelsOutput(response);const text = readableFoundationModelsText(normalized);
Availability#
Always call availability() before presenting AI features.
const status = await appleFoundationModels.availability('en_US');
Unavailable reasons are stable so your UI can explain what happened:
unsupported_platform: wrong platform or OS.unsupported_device: device cannot run Apple Intelligence.apple_intelligence_disabled: user must enable Apple Intelligence in Settings.assets_unavailable: model assets are not ready or still downloading.unsupported_locale: requested locale is not supported.unavailable: Apple returned an unclassified unavailable state.
Test on real Apple Intelligence-capable devices. Simulators and older iPhones are useful only for unsupported-state UI.
Sessions#
Sessions are explicit because instructions, transcripts, registered tools, and cancellation need stable IDs.
await appleFoundationModels.createSession({ id: 'support-agent', instructions: 'Help debug the current NativePHP app.', useCase: 'default',}); await appleFoundationModels.resetSession('support-agent');await appleFoundationModels.destroySession('support-agent');
Use content_tagging for Apple's specialized tagging use case:
await appleFoundationModels.createSession({ id: 'tagger', useCase: 'content_tagging',});
Structured Responses#
This plugin accepts JSON-like schemas from PHP or JavaScript. The current dynamic schema path instructs Foundation Models to return JSON, then validates the decoded output against the schema before dispatching completion events.
Use schemas for every structured result. Do not parse prose.
Supported schema shapes:
- Root
objectwithpropertiesand optionalrequired. - Nested
objectschemas. arraywithitems.stringwith optionalenum.integerandnumberwith optionalminimumandmaximum.boolean.
If the model returns non-JSON or JSON that does not match the schema, the plugin dispatches FoundationModelError with schema_invalid.
Streaming#
Streaming emits normalized snapshot-style partial responses, not guaranteed token-by-token deltas. FoundationModelStreamDelta.text contains display-ready generated content with Swift debug wrappers such as Snapshot(content: ..., rawContent: ...) removed.
const stream = await appleFoundationModels.stream('support-agent', 'Draft a changelog entry.', { type: 'object', properties: { note: { type: 'string' }, }, required: ['note'],}); for await (const chunk of stream) { console.log(chunk.sequence, chunk.text);}
App Tool Context#
Tools are application callbacks that can provide extra context before generation. They are not silent autonomous actions and they are not automatically chosen from the prompt.
For most JavaScript apps, prefer defineFoundationModelTools() from the Developer Experience Helpers section above. The lower-level API is still available when you want to manage definitions and handlers separately.
Register tools:
await appleFoundationModels.registerTools('support-agent', [ { name: 'currentUserPlan', description: 'Returns the current user subscription plan.', timeout: 5, schema: { type: 'object', properties: {}, }, },]);
Handle requests:
const stopHandlingTools = appleFoundationModels.handleTools({ async currentUserPlan() { return { plan: 'pro' }; },});
Explicitly request tool context during generation:
const response = await appleFoundationModels.respond( 'support-agent', 'Answer using the current plan if needed.', { type: 'object', properties: { answer: { type: 'string' }, }, required: ['answer'], }, { toolChoice: 'currentUserPlan', });
If toolChoice is omitted, no tools are invoked. Validate tool arguments, keep timeouts short, return the smallest useful result, and never perform irreversible side effects from a tool handler without explicit user confirmation.
Events#
Native code dispatches these PHP/JavaScript events:
| Event | Purpose |
|---|---|
FoundationModelResponseCompleted |
A non-streamed response completed and passed schema validation. |
FoundationModelStreamDelta |
A normalized streaming snapshot arrived. |
FoundationModelStreamCompleted |
A stream completed and passed schema validation. |
FoundationModelToolRequested |
The app must resolve explicit tool context. |
FoundationModelToolResolved |
A tool request was resolved or rejected. |
FoundationModelError |
Availability, generation, schema, cancellation, or tool error. |
JavaScript:
import { FoundationModelsEvent, on } from '#apple-foundation-models'; const stop = on(FoundationModelsEvent.Error, (payload) => { console.error(payload.code, payload.message);});
Livewire:
use Native\Mobile\Attributes\OnNative;use Weswecan\AppleFoundationModels\Events\FoundationModelResponseCompleted; #[OnNative(FoundationModelResponseCompleted::class)]public function handleFoundationModelResponse(string $requestId, string $sessionId, mixed $output): void{ // Persist or present the generated output.}
Cancellation#
Every generation returns a requestId. Use it to cancel active work:
const start = await appleFoundationModels.startRespond('support-agent', 'Draft a reply.', schema); await appleFoundationModels.cancel(start.requestId);
Destroying a session cancels active requests for that session and prevents late success events.
Token And Context Metadata#
tokenInfo() returns context metadata when Apple exposes it. Synchronous token counting is intentionally disabled in the native bridge so the main thread is not blocked.
const info = await appleFoundationModels.tokenInfo('support-agent', 'Prompt text', schema);
Expect tokenCount: null unless a future bridge-safe async token counting flow is added.
What This Plugin Does Not Do#
- It does not support Android Foundation Models.
- It does not call OpenAI, Anthropic, Gemini, or any server LLM.
- It does not provide Apple Writing Tools.
- It does not perform background AI work.
- It does not bypass Apple Intelligence availability, model asset, locale, or device restrictions.
- It does not guarantee arbitrary dynamic JSON schemas with native constrained sampling. Dynamic schemas are prompt-guided and validated after generation.
- It does not run tools automatically from a prompt. Tool context must be requested explicitly.
AI Usage Guidelines#
Use this plugin as a local assistant surface, not as a silent decision maker.
- Check
availability()first and show the exact reason when the model cannot run. - Ask for user intent before generating or rewriting user-visible content.
- Keep prompts scoped to the current action and avoid sending more personal data than needed.
- Prefer structured schemas over prose parsing.
- Show or confirm generated output before sending messages, changing records, charging money, deleting data, or taking actions that affect other people.
- Treat tool handlers as trusted app code. Validate arguments, authorize access, use short timeouts, and return only necessary data.
- Do not put secrets, credentials, API tokens, or private keys in prompts or tool results.
- Handle refusal, guardrails, cancellation, timeout, unavailable assets, unsupported locale, and unsupported platform as normal product states.
- Make AI-generated content visible to users when it affects recommendations, messages, summaries, or decisions.
- Test the exact device, OS, locale, and Apple Intelligence settings your app intends to support.
Troubleshooting#
| Symptom | Likely cause | Fix |
|---|---|---|
unsupported_platform |
Wrong OS, simulator, Android, or iOS below the Foundation Models runtime. | Test on a supported Apple Intelligence device. |
apple_intelligence_disabled |
Apple Intelligence is off. | Ask the user to enable Apple Intelligence in Settings. |
assets_unavailable |
Model assets are downloading or unavailable. | Show a retry state and ask the user to wait. |
unsupported_locale |
Requested locale is unavailable. | Use a supported locale or omit the locale. |
schema_invalid |
Returned JSON did not match the schema. | Tighten the prompt, simplify the schema, and handle the error in UI. |
| Tool request times out | No handler resolved the tool call in time. | Register handleTools() earlier and keep handlers fast. |
| Native function not found | Plugin was not rebuilt or registered. | Run NativePHP plugin validation and rebuild the native project. |
Local Release Checklist#
Before tagging a release:
composer installcomposer testvendor/bin/pint --dirty --format agentyarn installyarn typecheckyarn build
Then validate the plugin inside a NativePHP app:
php artisan native:plugin:validate
For iOS behavior, run a manual device smoke test on a real Apple Intelligence-capable device.
Support#
For licensing and support, contact [email protected].