-
Descubra novos recursos no framework App Intents
Aproveite ainda mais o App Intents com recursos avançados para torná-lo mais rápido, flexível e relevante. Descubra como ValueRepresentation e RelevantEntities facilitam a descoberta do seu conteúdo e permitem que ele seja compartilhado entre apps, como EntityCollection melhora o desempenho e como SyncableEntity ajuda a disponibilizar seu conteúdo em diferentes dispositivos. Explore tipos de parâmetros mais sofisticados, incluindo valores de união e intents de longa duração que lidam com cancelamento de maneira elegante.
Capítulos
- 0:00 - Introdução
- 2:40 - Compartilhe entidades entre apps com o ValueRepresentation
- 3:45 - Registre entidades relevantes usando o RelevantEntities
- 7:05 - Gerencie entidades de forma eficiente com EntityCollection
- 8:55 - Use entidades entre dispositivos com SyncableEntity
- 11:01 - Tipos de parâmetros mais ricos
- 12:38 - Parâmetros de valor de união
- 13:26 - Estenda a execução com LongRunningIntent
- 15:27 - Apontar para o processo correto usando ExecutionTargets
- 17:14 - Próximas etapas
Recursos
-
Buscar neste vídeo...
-
-
0:01 - Share structured entities with ValueRepresentation
struct LandmarkEntity: AppEntity, Transferable { var id: Int var landmark: Landmark // contains CLLocationCoordinate2D static var transferRepresentation: some TransferRepresentation { ValueRepresentation( exporting: { entity in PlaceDescriptor( representations: [.coordinate(entity.landmark.locationCoordinate)], commonName: entity.landmark.name ) } ) } } // If the entity already has a PlaceDescriptor property, use a key-path — much less code: struct LandmarkEntity: AppEntity, Transferable { var id: Int @Property var placeDescriptor: PlaceDescriptor static var transferRepresentation: some TransferRepresentation { ValueRepresentation(exporting: \.placeDescriptor) } } -
5:18 - Register relevant entities with RelevantEntities
// Suggest playlists for the workout session let playlistEntities = [dailyRun, runningMix] let workoutContext = AppEntityContext.audio(.workout(activityType: .running)) try await RelevantEntities.shared.updateEntities( playlistEntities, for: workoutContext ) // Clear all entities for a context try await RelevantEntities.shared.removeAllEntities(for: workoutContext) // Remove specific entities from a context try await RelevantEntities.shared.removeEntities(playlistEntities, from: workoutContext) // Or remove all entities across all contexts try await RelevantEntities.shared.removeAllEntities() -
7:15 - Handle large entity sets with EntityCollection
struct TagPhotosIntent: AppIntent { static let title: LocalizedStringResource = "Tag Travel Photos" @Parameter var photos: EntityCollection<PhotoEntity> // was: [PhotoEntity] @Parameter var tag: String func perform() async throws -> some IntentResult { modelData.tagPhotos(ids: photos.identifiers, tag: tag) // was: tagPhotos(photos, tag: tag) return .result() } } -
10:14 - Make entity IDs stable with SyncableEntity
// If your ID is already stable across devices (server UUID, CloudKit record ID): struct PhotoEntity: AppEntity, SyncableEntity { var id: Int // Already stable across devices — that's it } // If you use local IDs, pair a local and a stable ID: struct PhotoEntity: AppEntity, SyncableEntity { var id: SyncableEntityIdentifier<String, String> init(localID: String, stableID: String) { self.id = SyncableEntityIdentifier(local: localID, stable: stableID) } } -
11:58 - Accept multiple types with @UnionValue
@UnionValue enum TravelGalleryContent { case landmarkCollection(LandmarkCollectionEntity) case photoAlbum(PhotoAlbumEntity) static let typeDisplayRepresentation: TypeDisplayRepresentation = "Travel Gallery" static let caseDisplayRepresentations: [Cases: DisplayRepresentation] = [ .landmarkCollection: "Landmark Collection", .photoAlbum: "Photo Album" ] } -
13:41 - Run beyond 30 s with LongRunningIntent + CancellableIntent
struct UploadPhotoIntent: LongRunningIntent, CancellableIntent { static let title: LocalizedStringResource = "Upload Photo" @Parameter var photo: IntentFile func perform() async throws -> some IntentResult & ProvidesDialog { let result = try await performBackgroundTask { let chunks = calculateChunks(for: photo) progress.totalUnitCount = Int64(chunks) for chunk in 1...chunks { try Task.checkCancellation() try await uploadChunk(chunk) progress.completedUnitCount = Int64(chunk) } return "Upload complete!" } onCancel: { reason in cleanup(for: reason) } return .result(dialog: "\(result)") } } -
16:54 - Control which process runs your intent with ExecutionTargets
// Write operation — needs the main app struct UpdateFavoriteIntent: AppIntent { static var allowedExecutionTargets: ExecutionTargets { .main } } // Standalone download — runs in the extension struct DownloadPhotoIntent: AppIntent { static var allowedExecutionTargets: ExecutionTargets { .appIntentsExtension } } // Display-only — runs in the widget extension struct GetLandmarkStatusIntent: AppIntent { static var allowedExecutionTargets: ExecutionTargets { .widgetKitExtension } } // Works in either — lets the system choose struct TagPhotosIntent: AppIntent { static var allowedExecutionTargets: ExecutionTargets { [.main, .appIntentsExtension] } }
-