AppEntity
, and a verb, your AppIntent
. You’ll look at those first.
SessionEntity.swift
.
AppIntents
and Foundation
, and have the SessionEntity
struct adopt the AppEntity
and IndexedEntity
protocols:
import AppIntents
import Foundation
struct SessionEntity: AppEntity, IndexedEntity {
IndexedEntity
protocol will help you donate your objects to the App Intents system so that you can perform activities such as search. However, the AppEntity
protocol is the star here.
SessionEntity
needs to adopt the protocol. First, it needs a defaultQuery
property:
static var defaultQuery = SessionEntityQuery()
SessionEntity
structures. You’ll define this struct shortly.
Next, there must be a unique and persistent identifier:
var id: Session.ID
@Property
property wrapper. Here, you’ll define the session name, description, and session length, all of which will be exposed:
@Property(title: "Session Name")
var name: String
/// A description of the session
@Property(title: "Session Description")
var sessionDescription: String
/// The session length
@Property(title: "Session Length")
var sessionLength: String
displayRepresentation
property, which you’ll learn about shortly, can use it:
var imageName: String
typeDisplayRepresentation
is a localized name representing this entity as a concept people are familiar with when they use your app. It can be localized and vary based on plural rules defined in the .stringsdict
file.
static var typeDisplayRepresentation: TypeDisplayRepresentation {
TypeDisplayRepresentation(
name: LocalizedStringResource("Session", table: "AppIntents"),
numericFormat: LocalizedStringResource("\(placeholder: .int) sessions", table: "AppIntents")
)
}
displayRepresentation
, can provide information on how to display the entity to people. Here, you’ll use the session name, description, and image to build the DisplayRepresentation
:
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(
title: "\(name)",
subtitle: "\(sessionDescription)",
image: DisplayRepresentation.Image(named: imageName))
}
init
method for this struct. Where appropriate, map properties of the Session
to the SessionEntity
. Remember that since you’re making your own custom AppEntity
class, you can choose not to bring properties from the main Session
struct into this entity.
init(session: Session) {
self.id = session.id
self.imageName = session.featuredImage
self.name = session.name
self.sessionDescription = session.sessionDescription
self.sessionLength = session.sessionLength
}
SessionEntityQuery.swift
.
AppIntents
and Foundation
frameworks. The SessionEntityQuery
struct should adopt the EntityQuery
protocol:
import AppIntents
import Foundation
struct SessionEntityQuery: EntityQuery {
sessionManager
property as a dependency. You’ll register this dependency later in the demo.
@Dependency var sessionManager: SessionDataManager
entities(for identifiers:)
function:
func entities(for identifiers: [SessionEntity.ID]) async throws -> [SessionEntity] {
return sessionManager.sessions(with: identifiers)
.map { SessionEntity(session: $0) }
}
map
to convert those to SessionEntity
objects.
entities(matching string:)
:
func entities(matching string: String) async throws -> [SessionEntity] {
return sessionManager
.sessions { session in
session.name.localizedCaseInsensitiveContains(string)
}
.map { SessionEntity(session: $0) }
}
SessionEntity
objects.
With the entity and its query object in place, it’s time to add an intent so you can actually do something with the entity.
OpenSessionIntent.swift
, start by importing the AppIntents
framework and creating the struct:
import AppIntents
struct OpenSessionIntent: AppIntent, OpenIntent {
AppIntent
and OpenIntent
. Take a look at what these two protocols need.
AppIntent
needs first:
static let title: LocalizedStringResource = "Open Session"
func perform() async throws -> some IntentResult {
await NavigationModel.shared.navigate(toSession: target)
return .result()
}
static var parameterSummary: some ParameterSummary {
Summary("Open \(\.$target)")
}
AppIntent
requires a title
property, a LocalizedStringResource
that provides a title for the intent. This is the intent name in places such as a shortcut.
perform()
method, which returns an IntentResult
describing the result of this intent running. Sometimes, like in this example, the return value can be an empty .result()
. Before that return, the perform
method asks the navigation model to navigate to the specified session.
parameterSummary
was also included. This property defines the summary of this intent in relation to how its parameters are populated. In this case, this is a more entity-specific title, using the target as a parameter in the string.
OpenIntent
has a special parameter that says the app will open when the intent runs and also requires the target of the intent. Here, the target is a SessionEntity
.
static let title: LocalizedStringResource = "Open Session"
@Parameter(title: "Session")
var target: SessionEntity
func perform() async throws -> some IntentResult {
await NavigationModel.shared.navigate(toSession: target)
return .result()
}
static var parameterSummary: some ParameterSummary {
Summary("Open \(\.$target)")
}
With the entity and intent defined, you need to do a few more things to try this out.
AppMain.swift
file, add import AppIntents
to the top of the file. Then, near the bottom of the init
method, add:
AppDependencyManager.shared.add(dependency: sessionDataManager)
AppDependencyManager.shared.add(dependency: navigationModel)
Task {
try await CSSearchableIndex
.default()
.indexAppEntities(sessionDataManager.sessions.map(SessionEntity.init(session:)))
}
sessionDataManager
and navigationModel
objects as dependencies for entities and intents you may use. Second, the Task
block indexes or donates all of the sessions to the system. This means we’ll be able to search for them shortly.
NavigationModel
add the navigate(to:)
function:
func navigate(toSession: SessionEntity) {
self.selectedCollection = SessionDataManager.shared.completeSessionCollection
self.selectedSession = SessionDataManager.shared.session(with: toSession.id)
}
Look at this in the simulator. Launch the app in your favorite iOS 18 simulator. Let the initial screen with the sessions sit for a bit. In the background task you defined earlier, the app donates the session objects to the system.
Then, tap the home button and drag down to reveal the search dialog. Type in Session 1 and press enter
OpenSession
intent and the update to the navigation model you made.