Primordial uses on-device AI to generate, summarize, classify, transform, and extract structured information from text.
Create a Primordial Client
Import Primordial and create a PrimordialClient(). It selects Primordial’s default local generation configuration.
import Primordial
let primordial = PrimordialClient()
You can also use Apple Foundation Model or different AI model . See AI Model Setup for details.
Check Whether Generation Is Available
Text classification, summarization, and extraction or your custom tasks require
.generation capability. Check its status before presenting the feature or asking the user
to download anything.
let status = await primordial.ai.status(for: .generation)
print(status.isInstalled)
print(status.isAvailable)
print(status.downloadBytes)
print(status.requiredAvailableBytes)
isInstalled tells you whether the required AI model is already on the device.
isAvailable also checks whether the current device can use it. Primordial loads an downloaded model when
a collection operation
needs it.
When a download is required, use downloadBytes and requiredAvailableBytes to explain
the download and storage requirement in your own UI before continuing.
Activate Primordial
Activate the SDK before preparing a model or running a generation task.
try await primordial.activate(
.evaluationKey("pk_eval_your_key_here")
)
You can check primordial.isActivated when your UI needs to reflect activation state. Keep the
evaluation key out of logs and user-visible error messages.
Prepare Generation
makeAvailable checks storage,
downloads AI models, verifies it, and reports progress as an asynchronous stream.
for try await progress in primordial.ai.makeAvailable(.generation) {
if let fraction = progress.fractionCompleted {
print("Generation setup: \(Int(fraction * 100))%")
}
print(progress.phase)
print(progress.downloadedBytes as Any)
print(progress.totalBytes as Any)
}
Calling makeAvailable again reuses an existing valid download.
Primordial supports background model downloads on iOS when your app forwards background URL-session events. See Background downloads for the required app delegate code. If the user force-quits your app, the download is canceled.
Choose the Right Text API
Each API expresses a different application intent. Prefer the focused operation when it matches your
feature, and use task when you want to provide your own prompt.
| API | Use it when | Result |
|---|---|---|
summarize |
You need a concise summary or a bounded bullet list. | String |
streamSummarize |
Your UI should show a summary while it is generated. | Stream of PrimordialTextChunk |
classify |
The output must be exactly one category from your list. | String |
task |
You need a custom transformation or instruction. | PrimordialTaskResult |
streamTask |
You need fragments, optional citations, and a final result. | Stream of PrimordialTaskEvent |
extract |
You want to decode information from text into a Swift type. | Your Decodable type |
Structured task |
You need custom behavior and a typed result, optionally grounded in collections. | Typed PrimordialTaskResult |
Summarize Text
Once .generation is available, the quickest useful task is a summary. Primordial supplies the
task instruction and returns the generated text as a Swift String.
let article = """
Swift actors protect their isolated state from unsynchronized access.
Callers use await when crossing an actor boundary.
"""
let summary = try await primordial.summarize(article)
print(summary)
Use .bulletPoints(maximum:) to create a short, easy-to-scan list.
let concise = try await primordial.summarize(article)
let bullets = try await primordial.summarize(
article,
style: .bulletPoints(maximum: 3)
)
The maximum bullet count must be greater than zero, and the input must contain non-whitespace text.
Classify Text
Pass the complete set of values your application accepts. Primordial returns exactly one of those category strings or throws instead of handing your app an unexpected label.
let sentiment = try await primordial.classify(
"The setup was quick and the results are useful.",
into: ["positive", "neutral", "negative"]
)
switch sentiment {
case "positive": showPositiveState()
case "neutral": showNeutralState()
case "negative": showNegativeState()
default: break // Primordial does not return an unknown category.
}
Categories must be nonempty, unique, and free of empty values. Matching is exact, so choose stable values that your application can handle directly.
Define a Custom Task
Use task for transformations that do not have a dedicated API. Put your instructions
in prompt and place user provided text in input. Keeping these parameters separate
makes the intended behavior clear and keeps untrusted content out of the final prompt.
let result = try await primordial.task(
prompt: "Rewrite the input in clear language for a general audience.",
input: userText
)
print(result.output)
print(result.citations)
result.output is the generated text. An ungrounded task has no supporting collection sources,
so its citations array is empty.
Ground a Custom Task in App Data
A custom task can search one or more Primordial collections before generation. This changes the required
capability from .generation to .answering, because the workflow needs both semantic
search and text generation.
let notes = try primordial.collection("notes")
let result = try await primordial.task(
prompt: "Recommend the next action using only the saved notes.",
input: "What should I work on today?",
using: [notes],
where: .field("status", .equals(.keyword("open")))
)
print(result.output)
print(result.citations)
The collection must already contain searchable records and belong to the same client. Check and prepare
.answering before running the task. See Store & Search for
collection setup and Answers & Citations for source semantics.
Stream Generated Text
Streaming lets your UI begin showing text before the complete response is ready. For a summary,
append each nonempty chunk and use isFinal to recognize completion.
var summary = ""
for try await chunk in primordial.streamSummarize(article) {
summary += chunk.text
if chunk.isFinal {
renderFinalSummary(summary)
} else {
renderProvisionalSummary(summary)
}
}
A custom task uses events because it may also return citations. The completed event contains the final assembled output and its final citation set.
var visibleText = ""
for try await event in primordial.streamTask(
prompt: "Rewrite the input clearly.",
input: userText
) {
switch event {
case .fragment(let text):
visibleText += text
renderProvisionalText(visibleText)
case .citation(let citation):
showSource(citation)
case .completed(let result):
visibleText = result.output
renderFinalText(visibleText)
}
}
Streamed fragments are provisional. Commit application state from the completed value, not from a partial fragment. Canceling the consuming Swift task cancels the active generation operation.
Extract Structured Data
Structured output combines a Swift Decodable & Sendable type with a schema that describes the
exact generated shape. Primordial validates the complete output against the schema before decoding it.
struct Contact: Decodable, Sendable {
let name: String
let age: Int?
let email: String
}
let contactSchema = PrimordialOutputSchema(
Contact.self,
schema: .object(
[
"name": .string,
"age": .integer,
"email": .string
],
optional: ["age"]
)
)
let contact = try await primordial.extract(
"Minch is available at minch@example.com.",
as: contactSchema
)
print(contact.name)
print(contact.email)
If the first response does not match the schema, Primordial may make one bounded repair attempt. If the
repaired value is still invalid or cannot be decoded as your Swift type, the operation throws
PrimordialAIError.structuredOutputInvalid.
Use a custom prompt with typed output
Use the structured task overload when extraction alone does not describe the behavior you
need. The result keeps the typed output and any citations together.
let result = try await primordial.task(
prompt: "Normalize the contact details and lowercase the email address.",
input: "Minch — MINCH@EXAMPLE.COM",
as: contactSchema
)
let contact: Contact = result.output
Available schema values
| Schema | Expected value |
|---|---|
.string |
A Swift String. |
.oneOf([String]) |
Exactly one string from a nonempty, unique list. |
.integer |
A whole-number value. |
.number |
A finite numeric value. |
.boolean |
A Boolean value. |
.array(schema) |
An array whose elements all match the nested schema. |
.object(fields, optional:) |
An object with declared fields and an optional set of keys that may be omitted. |
Handle Generation Errors
Generation fails explicitly when resources are unavailable, input is invalid, a selected feature is not
supported, the context window is exceeded, or structured output cannot be validated. A canceled Swift
task remains CancellationError so your normal cancellation handling still works.
do {
let result = try await primordial.task(
prompt: "Rewrite the input clearly.",
input: userText
)
render(result.output)
} catch is CancellationError {
print("Generation canceled")
} catch let error as PrimordialAIError {
switch error {
case .capabilityUnavailable(let requirement):
print("Missing download: \(requirement.downloadBytes) bytes")
case .invalidQuery(let message),
.invalidConfiguration(let message):
print(message)
case .contextWindowExceeded:
print("The input is too long for the selected model")
case .structuredOutputInvalid:
print("The model did not return the required structure")
default:
print(error.localizedDescription)
}
}
Do not automatically start a model download from an error handler. Use the capability requirement to
explain what is missing, ask the user, and then call makeAvailable after they agree.