Siri can take parameters through App Intents. But without AppEntity, it can't search your app's data — users have to type exact values instead of picking from a list. Entity queries fix this. They're also the most boilerplate-heavy part of App Intents.
A single entity requires an AppEntity struct with displayRepresentation, a typeDisplayRepresentation, an EntityQuery with entities(for:), suggestedEntities(), and optionally allEntities() for Spotlight indexing. About 30 lines of mechanical code per entity. Twenty entities = 600 lines.
Axint generates all of it from a TypeScript definition.
const noteEntity = defineEntity({
name: "Note",
typeDisplayName: "Note",
fields: {
id: "UUID",
title: "String",
content: "String",
},
displayRepresentation: {
stringLiteral: "title",
},
query: {
suggestedEntities: { returns: "Note[]" },
entitiesByIdentifier: {
params: { identifiers: "UUID[]" },
returns: "Note[]",
},
searchableEntities: {
params: { searchTerm: "String" },
returns: "Note[]",
},
},
});Compiles to a complete AppEntity + EntityQuery:
struct Note: AppEntity {
var id: UUID
var title: String
var content: String
static let defaultQuery = NoteQuery()
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(stringLiteral: title)
}
static var typeDisplayRepresentation: TypeDisplayRepresentation {
TypeDisplayRepresentation(name: "Note")
}
}
struct NoteQuery: EntityQuery {
func entities(for identifiers: [UUID]) async throws -> [Note] {
fatalError("Implement entities(for:)")
}
func suggestedEntities() async throws -> [Note] {
fatalError("Implement suggestedEntities()")
}
}The fatalError() stubs force you to implement the data-fetching logic. Axint handles the structure; you provide the database calls.
Using entities in intents
Once you have an entity, reference it in an intent:
const searchNotes = defineIntent({
name: "SearchNotes",
title: "Search Notes",
params: {
note: param.entity(noteEntity, "Note to search"),
},
});Siri will now show a list of notes (from suggestedEntities()) when the user triggers SearchNotes. No manual entity wiring.
Property-based filtering
For filtered queries, Axint generates PropertyBasedEntityQuery conformances:
const noteEntity = defineEntity({
name: "Note",
fields: { id: "UUID", title: "String", tags: "String[]" },
query: {
entitiesByProperty: {
property: "tags",
params: { tag: "String" },
returns: "Note[]",
},
},
});Generates:
extension NoteQuery: PropertyBasedEntityQuery {
nonisolated static var properties = QueryProperties {
Property(\[Note.self\], where: \.tags, matches: \.tag) { notes, tag in
notes.filter { $0.tags.contains(tag) }
}
}
}Siri can now ask: "Search notes tagged 'urgent'."
Spotlight indexing
Implement allEntities() and Spotlight will periodically index your data:
func allEntities() async throws -> [Note] {
return try await database.fetchAllNotes()
}Users find your content from the home screen. The SPM plugin compiles entity definitions alongside your intent files, so everything stays in sync.
Current limitations
v0.3.8 doesn't support sorting criteria, compound predicates, or pagination hints. These are coming in v0.4. For now, implement complex filtering in your Swift stubs.