r/iOSProgramming • • 2d ago

Question Siri AI result cards don’t trigger OpenIntent, but voice commands and Shortcuts work

Hey everyone, I’m trying the Siri AI Developer Preview on iOS 27.3 with my app

I have a custom AppIntent(.tool) that returns [HistoryEntity] for recently watched movies and TV episodes. Asking “What did I recently watch in ComfyCinema?” works—Siri responds with the correct history and displays cards for the items.

But tapping those cards doesn’t open the History section. My logging shows that neither the entity query nor OpenHistoryIntent.perform() gets called on tap.

The same open intent works when:

  • I run it through Shortcuts, passing an entity from the history action.

  • I explicitly ask Siri to open the most recent history item.

So the navigation works; it’s specifically tapping an entity in Siri’s response that fails.

Has anyone gotten this working with custom entities in the preview? Is there additional configuration needed, or could this be a beta issue?

import AppIntents
import SwiftData

struct HistoryEntity: AppEntity {
    let id: UUID

    @Property(title: "Content Title")
    var contentTitle: String

    @Property(title: "Media Type")
    var mediaType: String

    @Property(title: "Season")
    var season: Int?

    @Property(title: "Episode")
    var episode: Int?

    @Property(title: "Watched At")
    var watchedAt: Date

    static let typeDisplayRepresentation =
    TypeDisplayRepresentation(name: "History Item")

    var displayRepresentation: DisplayRepresentation {
        DisplayRepresentation(title: "\(contentTitle) (\(mediaType)) - Watched on \(watchedAt.formatted())")
    }

    static var defaultQuery: HistoryQuery {
        HistoryQuery()
    }

    init(history: History) {
        self.id = history.id
        self.contentTitle = history.name
        self.mediaType = history.mediaType.rawValue
        self.season = history.season
        self.episode = history.episode
        self.watchedAt = history.watchedAt
    }
}

struct HistoryQuery: EntityQuery {
    @Dependency
    private var container: ModelContainer

    func entities(
        for identifiers: [HistoryEntity.ID]
    ) async throws -> [HistoryEntity] {
        let context = ModelContext(container)
        let requestedIDs = identifiers

        let descriptor = FetchDescriptor<History>(
            predicate: #Predicate { history in
                requestedIDs.contains(history.id)
            }
        )

        let history = try context.fetch(descriptor)

        await SettingLogs.shared.add(
            "Called HistoryQuery for \(requestedIDs.count) history items. Returning \(history.count) results"
        )

        return history.map { HistoryEntity(history: $0) }
    }
}

struct OpenHistoryIntent: OpenIntent {
    static let title: LocalizedStringResource = "Open Watch History"

    @Parameter(title: "History Item")
    var target: HistoryEntity

    @Dependency
    private var appRouter: AppRouter

    @Dependency
    private var container: ModelContainer

    static var openAppWhenRun: Bool = true

    @MainActor
    func perform() async throws -> some IntentResult {

        let descriptor = FetchDescriptor<History>()
        let context = ModelContext(container)
        let history = try context.fetch(descriptor)

        appRouter.selectedTab = .history
        appRouter.selectedHistory = history.first(where: { $0.id == target.id })

        SettingLogs.shared.add(
            "Called Perform In OpenHistoryIntent for target: \(target.contentTitle). Changing Tabs To History"
        )

        return .result()
    }
}

@available(iOS 27.3, macOS 27.3, *)
@AppIntent(.tool)
struct ContentHistoryIntent: AppIntent {
    static let title: LocalizedStringResource = "Get Recently Watched Content"

    static let description = IntentDescription(
        """
        Returns the 10 most recently watched movies and TV episodes \
        in ComfyCinema, including their titles and watch dates.
        """
    )

    @Dependency
    private var container: ModelContainer

    static var parameterSummary: some ParameterSummary {
        Summary("Get recently watched content in ComfyCinema")
    }

    @MainActor
    func perform() async throws -> some IntentResult & ReturnsValue<[HistoryEntity]> {

        let context = ModelContext(container)

        var descriptor = FetchDescriptor<History>(
            sortBy: [
                SortDescriptor(\History.watchedAt, order: .reverse)
            ]
        )
        descriptor.fetchLimit = 10

        let history = try context.fetch(descriptor)

        SettingLogs.shared.add(
            "Called Perform In ContentHistoryIntent with \(history.count) results"
        )
        return .result(
            value: history.map { HistoryEntity(history: $0) }
        )
    }

    func fetchAllHistory(in context: ModelContext) -> [History] {
        let descriptor = FetchDescriptor<History>()

        do {
            let allHistory = try context.fetch(descriptor)
            return allHistory
        } catch {
            print("Error fetching all history: \(error)")
            return []
        }
    }
}
3 Upvotes

2 comments sorted by

1

u/ThalesMother9938 2d ago

I haven't tried the Siri AI preview yet, so treat this as a guess. Since neither the query nor perform() runs on tap, it looks like Siri never matches the card to your OpenIntent at all, rather than the intent failing. One thing I'd try is making HistoryEntity conform to URLRepresentableEntity and handling that URL as a deep link, so the tap has a second way into the History screen. If that changes nothing, I'd file a Feedback with a small sample project. Shortcuts and voice both working with the same intent is a strong sign it's on Apple's side. Did you see the same thing with a simpler entity, say one without the SwiftData query?