Sample CodeiOS 27.0+, iPadOS 27.0+, Mac Catalyst 27.0+, macOS 27.0+Reviewed 2026-07-21View on Apple Developer

Integrating your messaging app with Apple Intelligence

At a glance

Item Summary
Purpose Expose messaging entities and send, draft, edit, read-status, and unsend operations to system experiences.
Architecture One SwiftUI/SwiftData app with an actor-isolated data owner, a main-actor navigation owner, and thin App Intent adapters.
State strategy ModelManager owns stored conversation state; NavigationManager owns foreground UI state; entity structs carry system-facing snapshots.
Boundary App target only; there is no App Intents extension or widget target.

Project structure

UnicornChat/
├── AppIntents/                 # Schema intents, entities, queries, shortcuts
├── Managers/                   # Data, navigation, notifications, generation
├── Model/                      # SwiftData Contact/Conversation/Message
├── Extensions/                 # Model fetching and entity projections
├── Views/                      # Contact, conversation, and message features
└── UnicornChatApp.swift        # Composition root

The folders separate framework-facing contracts from stored models and UI. Larger ModelManager responsibilities are split into source extensions without creating additional owners.

Overall architecture

Reference code

UnicornChat/UnicornChatApp.swift:22 — the app creates one object graph and registers the same managers for App Intents.

init() {
    let model = ModelManager()
    let navigation = NavigationManager()
    AppDependencyManager.shared.add(dependency: navigation)
    AppDependencyManager.shared.add(dependency: model)
    NotificationManager.shared.configure(model: model, navigation: navigation)
    self.navigation = navigation
    self.model = model
}

Views and background intent invocations converge on the same state owners. The intent structs translate system schemas; they do not create stores or navigation state.

Ownership and state

Ownership evidence

UnicornChat/Managers/ModelManager.swift:13 — the model actor privately owns the automatic-response service.

@ModelActor
actor ModelManager {
    @MainActor var mainContext: ModelContext {
        modelContainer.mainContext
    }
    private let conversationManager = ConversationManager()
}
Owner Object or state Relationship Mutation authority
UnicornChatApp ModelManager, NavigationManager Creates, retains, and injects Wiring only
ModelManager SwiftData models and ConversationManager Actor-isolated lifecycle owner Conversation/contact/message persistence
NavigationManager Selection, composition, and edit state Main-actor observable owner Foreground presentation state
ConversationManager Per-recipient language-model sessions Private mutex-protected owner Session creation and response generation
NotificationManager.shared Optional model/navigation references Process singleton configured at launch Notification scheduling and tap routing
App Intent structs Injected managers Borrow for one invocation Parameter translation and orchestration only

Class and protocol design

Type Design Responsibility
ModelManager @ModelActor actor Serialize SwiftData access and coordinate indexing, donations, and generated replies.
ConversationManager final class: Sendable Use sendable snapshots and independent language-model sessions for concurrent recipients.
NavigationManager @MainActor @Observable final class Hold view-only state and foreground routes.
NotificationManager final class: NSObject, delegate extension Bridge UNUserNotificationCenterDelegate callbacks into model lookup and navigation.
MessageEntity IndexedEntity, Transferable extension Adapt stored messages to Spotlight, App Intents, and transfer contracts.
Intent structs AppIntent and message schema conformances Map system parameters to manager calls through @Dependency.

Protocol-oriented design is concentrated at system boundaries (AppIntent, EntityQuery, IndexedEntity, Transferable, and UNUserNotificationCenterDelegate) rather than an app-defined service-protocol layer.

Access control

Symbol Access Verified effect Likely rationale
ModelManager.conversationManager private let Only the persistence actor starts generated responses. Preserve one orchestration boundary.
ConversationManager.model, sessions, helpers private Callers can request responses but cannot manipulate sessions or prompts. Protect concurrency and prompt invariants.
NotificationManager initializer/references private Construction is limited to shared; retained dependencies change only through configure. Enforce singleton setup.
Managers, intents, entities internal default Cross-file code in the app target can collaborate; no external API is exported. This is an app, not a reusable library.
MessageTypingIndicator.body public within an internal type The member is explicit, but the containing view remains module-internal. Likely conformance-style explicitness, not a package boundary.
Architecture types no fileprivate or open No same-file-only collaboration layer or subclassable external API is defined. The sample favors type-private invariants plus target-internal composition.

Reference code

UnicornChat/Managers/NotificationManager.swift:18 — singleton creation and its mutable links are narrowly scoped.

nonisolated(unsafe) static let shared = NotificationManager()

private var model: ModelManager?
private var navigation: NavigationManager?

private override init() {}

The nonisolated(unsafe) exception is limited to the singleton reference; configuration and delegate routing are explicitly main-actor operations.

Logic ownership and placement

Logic Owner Placement rationale
Schema/container creation, CRUD, save, Spotlight, donation ModelManager and its extensions One actor owns stored-state transitions.
Automatic replies ConversationManager Foundation Models concurrency is isolated from SwiftData objects through snapshots.
Selection, compose, edit routes NavigationManager UI state stays outside persistence.
Notification metadata and delegate callbacks NotificationManager Keeps the Objective-C delegate bridge out of the data owner.
Entity lookup/projection Entity queries and ModelManager+Entities System values resolve through stable identifiers.
Intent parameter validation Individual intent perform() methods Unsupported schema combinations are rejected before domain mutation.

Design patterns

Pattern Source evidence Purpose or tradeoff
Composition root / dependency injection UnicornChat/UnicornChatApp.swift:22 Shares one model and navigation graph across UI and intents.
Actor repository UnicornChat/Managers/ModelManager.swift:13 Serializes SwiftData operations behind one owner.
Entity adapter UnicornChat/AppIntents/MessageEntity.swift:90 Converts actor-bound models into system-facing value snapshots.
Router UnicornChat/Managers/NavigationManager.swift:39 Centralizes foreground destinations.
Delegate adapter UnicornChat/Managers/NotificationManager.swift:138 Translates notification callbacks into app operations.
Per-key session sharding UnicornChat/Managers/ConversationManager.swift:59 Serializes dictionary access while allowing unrelated recipients to generate concurrently.

Naming conventions

  • Managers are named by owned capability: ModelManager, ConversationManager, NavigationManager, and NotificationManager.
  • Stored domain nouns are un-suffixed (Message, Conversation, Contact); system projections add Entity.
  • System commands use verb-noun Intent names such as SendMessageIntent and UnsendMessageIntent.
  • Methods distinguish commands (sendMessage, openConversation) from lookups (entities(for:), fetchRecentMessages).
  • Files generally match their primary type; extensions group fetch, entity conversion, and error concerns around ModelManager.

Architecture takeaways

  • Register the same long-lived managers for views and App Intents instead of constructing a second state graph.
  • Keep SwiftData mutation actor-isolated and cross that boundary with sendable snapshots for long-running generation work.
  • Treat intents and entities as adapters around domain owners, not as owners themselves.
  • Use weak/configured links for process services that route back into app state, and document narrow isolation exceptions.

Source map

Source Relevant symbols
UnicornChat/UnicornChatApp.swift:12 App entry and dependency registration
UnicornChat/Managers/ModelManager.swift:13 Actor repository and response orchestration
UnicornChat/Managers/ConversationManager.swift:21 Sendable response generator and session sharding
UnicornChat/Managers/NavigationManager.swift:12 Foreground state owner
UnicornChat/Managers/NotificationManager.swift:14 Singleton notification/delegate bridge
UnicornChat/AppIntents/MessageIntents.swift:30 Messaging schema intent adapters
UnicornChat/AppIntents/MessageEntity.swift:15 Indexed entity and query