Text Generation

On-device text intelligence.

Summarize, classify, transform, stream, and extract structured data with Primordial.

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.