iOS 27 expands the App Intents framework with RelevantEntities for contextual content surfacing, EntityCollection for high-performance batch parameter handling, SyncableEntity for cross-device Siri continuity, native Duration/PersonNameComponents parameter types, and @UnionValue for multi-type parameters โ plus extended intent execution time limits.
โข Added RelevantEntities API to proactively hint which entities are contextually relevant, complementing Spotlight indexing and interaction donation
โข Added EntityCollection parameter type that passes only entity IDs to perform, avoiding costly full-resolution for batch operations
โข Added SyncableEntity protocol and SyncableEntityIdentifier to support stable cross-device entity references for Siri conversation continuity
โข Added native @Parameter support for Duration and PersonNameComponents, plus @UnionValue macro for multi-type parameters
โข RelevantEntities lets you proactively surface your content (e.g. playlists, landmarks) in the right system context without requiring a prior search or interaction donation
โข EntityCollection dramatically improves performance for intents that process large batches of entities by passing only IDs instead of fully resolved objects
โข SyncableEntity and cross-device Siri continuity mean apps with server or CloudKit IDs get seamless hand-off between devices with minimal code changes
Demonstrates how to use EntityCollection as an App Intent parameter type to efficiently tag thousands of photos by ID without resolving full entity objects, resulting in near-instant execution compared to the old array-based approach.
import AppIntents
// MARK: - Photo Entity
struct PhotoEntity: AppEntity {
static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "Photo")
static var defaultQuery = PhotoEntityQuery()
var id: String
var title: String
var keywords: [String]
var displayRepresentation: DisplayRepresentation {
DisplayRepresentation(title: "\(title)")
}
}
struct PhotoEntityQuery: EntityQuery {
func entities(for identifiers: [String]) async throws -> [PhotoEntity] {
// Fetch only the requested photos from your data layer
identifiers.map { id in
PhotoEntity(id: id, title: "Photo \(id)", keywords: [])
}
}
}
// MARK: - Efficient Batch Tagging Intent using EntityCollection
struct TagPhotosIntent: AppIntent {
static var title: LocalizedStringResource = "Tag Photos"
static var description = IntentDescription("Adds a keyword tag to multiple photos efficiently.")
// EntityCollection passes only IDs โ no full entity resolution
@Parameter(title: "Photos")
var photos: EntityCollection<PhotoEntity>
@Parameter(title: "Tag")
var tag: String
func perform() async throws -> some IntentResult {
// photos.entityIdentifiers contains just the IDs
let ids = photos.entityIdentifiers
await PhotoLibrary.shared.addTag(tag, toPhotoIDs: ids)
return .result()
}
}
// MARK: - Simulated Data Layer
actor PhotoLibrary {
static let shared = PhotoLibrary()
func addTag(_ tag: String, toPhotoIDs ids: [String]) {
// Efficiently update only the records that need tagging
print("Tagged \(ids.count) photos with '\(tag)'")
}
}
// MARK: - RelevantEntities: Surface a playlist in a workout context
import AppIntents
struct RunningPlaylistEntity: AppEntity {
static var typeDisplayRepresentation = TypeDisplayRepresentation(name: "Playlist")
static var defaultQuery = PlaylistEntityQuery()
var id: String
var name: String
var displayRepresentation: DisplayRepresentation { DisplayRepresentation(title: "\(name)") }
}
struct PlaylistEntityQuery: EntityQuery {
func entities(for identifiers: [String]) async throws -> [RunningPlaylistEntity] {
identifiers.map { RunningPlaylistEntity(id: $0, name: "Playlist \($0)") }
}
}
func registerRunningPlaylists() async throws {
let playlists = [
RunningPlaylistEntity(id: "pl-1", name: "High Tempo Run"),
RunningPlaylistEntity(id: "pl-2", name: "Endurance Mix")
]
let context = RelevantContext.workout
try await RelevantEntities.updateEntities(playlists, forContext: context)
}iOS 27 adds sectioned queries, codable model attributes, ResultsObserver for non-SwiftUI change observation, and HistoryObserver for reacting to persistent history changes in SwiftData.
USDKit is a new first-party Swift framework introduced in iOS/macOS 27 that brings native USD scene creation, composition, modification, and export capabilities to Apple platform apps, with deep RealityKit and Spatial Preview integration.
LiveCommunicationKit is the modern replacement for CXProvider that delivers rich, native conversation UIs integrated with the Lock Screen, Dynamic Island, Phone app Recents, and Siri. It provides a unified lifecycle model for audio and video conversations with a single delegate-driven action pipeline.
In-depth guide
iOS 26 โ iOS 27 Migration Guide โEntityCollection bypasses full entity resolution โ your perform method receives only IDs, so any property access on the entity must be done via your own data layer. @UnionValue requires the macro to be applied to a Swift enum with associated values; mismatched types will cause compile errors. RelevantEntities registrations persist until explicitly removed โ always clean up stale contexts to avoid surfacing irrelevant content.
Cross-device Siri continuity and SyncableEntity features require Apple Intelligence-capable devices signed into the same Apple ID. RelevantEntities requires the app to have appropriate intents defined.
The NowPlaying framework introduces a first-class Swift API for surfacing app media in system-wide now-playing surfaces โ Lock Screen, Control Center, Dynamic Island, StandBy, CarPlay, Apple Watch, and Apple TV โ via a declarative MediaSessionRepresentable protocol. It also supports remote media sessions (for controlling external speakers/TVs) and Media Sharing Extensions for routing media to third-party devices.