← 모든 글

iOS 26의 App Intents 2.0: Visual Intelligence, 인터랙티브 스니펫, 그리고 Deferred Properties

App Intents는 iOS 16에서 Apple의 구조화된 액션 API로 출시되어 Shortcuts, Siri, Spotlight를 지원했고, iOS 17에서는 App Intents 기반 위젯으로 확장되었으며, iOS 18에서는 Apple Intelligence의 액션 표면을 위한 계약이 되었습니다. iOS 26은 App Intents를 Visual Intelligence(타사 앱의 이미지 검색 결과), 인터랙티브 스니펫(시스템 컨텍스트에 구성되는 작은 팝업 UI 창), 엔티티 뷰 어노테이션(엔티티가 인라인으로 렌더링되는 방식), 그리고 @DeferredProperty(비동기로 계산되는 엔티티 속성)로 확장합니다1. 이러한 확장은 핵심 App Intents 모델을 변경하지 않고, 앱이 채택할 수 있는 새로운 참여 표면을 추가합니다.

이 글은 Apple의 공식 문서를 기준으로 iOS 26의 추가 사항을 살펴봅니다. 프레임은 “기존 App Intents 채택이 iOS 26 conformance를 추가함으로써 얻는 새로운 표면이 무엇인가”입니다. 이미 App Intents가 적용된 대부분의 앱은 기초 타입을 갖추고 있으며, iOS 26 작업은 그러한 타입을 새로운 컨텍스트로 확장하는 것에 관한 것이기 때문입니다.

TL;DR

  • IntentValueQuery는 Visual Intelligence 통합을 위한 새로운 프로토콜입니다. 이 쿼리는 SemanticContentDescriptor(사용자의 시각적 컨텍스트)를 받아 앱이 관련 있다고 판단하는 AppEntity 인스턴스 배열을 반환합니다2.
  • @DeferredProperty는 값이 비동기적으로 계산되는 엔티티 속성을 선언합니다. 시스템이 실제로 필요로 할 때 속성이 로드되므로, 표시 비용은 저렴하지만 계산 비용이 비싼 필드가 몇 개 있는 엔티티에서 미리 발생하는 비용을 피할 수 있습니다.
  • 인터랙티브 App Intents 스니펫을 사용하면 앱이 Spotlight, Visual Intelligence, Siri 응답 표면 같은 시스템 컨텍스트 내부에 작은 팝업 창(버튼, 텍스트, 컨트롤 포함)을 표시할 수 있습니다. 스니펫은 App Intent의 결과에 바인딩된 SwiftUI 뷰입니다.
  • 엔티티 뷰 어노테이션을 통해 앱은 AppEntity가 다양한 시스템 컨텍스트에서 어떻게 렌더링되어야 하는지 선언할 수 있습니다(Spotlight의 컴팩트한 행, Visual Intelligence의 히어로 카드, Live Activity 스타일 위젯).
  • 이 클러스터의 App Intents 글은 기초 모델을 다뤘으며, 이 글은 그것을 iOS 26의 새로운 참여 표면으로 확장합니다. 이 클러스터의 App Intents vs MCP Tools 글은 Apple Intelligence의 인텐트 표면과 범용 에이전트 MCP 도구 사이의 라우팅 질문을 다룹니다.

Visual Intelligence: IntentValueQuerySemanticContentDescriptor

iOS 26의 대표적인 App Intents 추가 사항은 Visual Intelligence 통합입니다2. 흐름은 다음과 같습니다:

  1. 사용자가 사진을 찍거나, 스크린샷을 캡처하거나, 카메라를 객체에 갖다 댑니다.
  2. 시스템의 Visual Intelligence 레이어가 시각적 + 의미적 컨텍스트를 추출합니다(이미지에 무엇이 있는지, 사용자가 그것에 대해 무엇을 원할 수 있는지).
  3. 시스템은 그 의미적 컨텍스트로 IntentValueQuery를 등록한 모든 앱에 쿼리를 보냅니다.
  4. 각 앱은 관련 있는 AppEntity 인스턴스를 반환하고, 시스템은 이를 집계하여 Visual Intelligence UI에 표시합니다.
  5. 사용자가 엔티티를 탭하면 적절한 컨텍스트로 원본 앱에 진입합니다.

개발자 표면은 SemanticContentDescriptor를 받는 values(for:) 메서드를 가진 IntentValueQuery를 준수하는 구조체입니다:

import AppIntents

struct ProductLookupQuery: IntentValueQuery {
    func values(for descriptor: SemanticContentDescriptor) async throws -> [Product] {
        // descriptor.labels: detected category and content labels
        // descriptor.pixelBuffer: the visual content as a CVReadOnlyPixelBuffer
        //                        (use VideoToolbox/CoreImage to convert if needed)
        let candidates = try await catalog.search(labels: descriptor.labels)
        return candidates.map(Product.init)
    }
}

SemanticContentDescriptor는 시스템이 채우는 두 개의 필드를 전달합니다: labels(“wine bottle”, “pinot noir”, “label text” 같이 감지된 카테고리와 콘텐츠 태그 배열)와 pixelBuffer(콘텐츠에 대해 자체 비전 모델을 실행하려는 앱을 위한 CVReadOnlyPixelBuffer 형태의 기본 이미지 데이터). 앱의 역할은 이 신호를 자체 데이터에 매핑하고 일치하는 엔티티를 반환하는 것입니다.

올바른 채택 패턴은 다음과 같습니다: 쇼핑 앱은 제품 카탈로그(시각적 컨텍스트 → 일치하는 제품)에 대해, 와인 앱은 와인 병 데이터베이스(라벨 이미지 → 와인 항목)에 대해, 레시피 앱은 레시피 라이브러리(재료 사진 → 일치하는 레시피)에 대해 쿼리를 구현합니다.

@DeferredProperty: 비동기 엔티티 값

기존 AppEntity 타입은 속성을 정적으로 선언합니다. 모든 속성은 엔티티가 반환되기 전에 계산되어야 합니다. 비용이 혼합된 속성(빠른 title/subtitle/image, 느린 server-fetched detailed-description)을 가진 엔티티의 경우, 전부 아니면 전무 식 계산은 낭비입니다.

@DeferredProperty(iOS 26+)는 속성을 비동기 계산으로 선언합니다3:

import AppIntents

struct Recipe: AppEntity {
    static var typeDisplayRepresentation = TypeDisplayRepresentation(...)
    static var defaultQuery = RecipeQuery()

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

    @Property(title: "Cuisine")
    var cuisine: String

    @DeferredProperty(title: "Detailed Instructions")
    var instructions: String {
        get async throws {
            try await loadInstructionsFromBackend(id: id)
        }
    }
}

deferred 속성의 getter는 비동기입니다. 시스템이 실제로 값을 필요로 할 때만 실행됩니다(예: 엔티티가 상세 표시를 위해 선택될 때). 쿼리에서 반환되어 선택 UI에서만 사용되는 엔티티의 경우, deferred 속성은 결코 계산되지 않습니다.

이 패턴은 빠르게 빌드되는 필드(id, title, summary)와 비싸게 빌드되는 필드(전체 본문, 계산된 메트릭, 서버에서 가져온 데이터)가 함께 있는 엔티티에 적합합니다. @DeferredProperty가 없으면, 개발자는 모든 것을 계산하거나(낭비), 저렴한 필드만 계산하고 별도의 “load detail” 인텐트를 추가해야 합니다(더 복잡함).

인터랙티브 스니펫과 SnippetIntent

iOS 26은 이전 릴리스에서 사용하던 기존 ShowsSnippetView 준수 AppIntent 패턴과 함께, 스니펫 형태 인터랙션을 위한 전용 SnippetIntent 프로토콜을 도입합니다4. SnippetIntent는 시스템이 전체 인텐트 재호출 없이 스니펫의 콘텐츠를 새로 고치기 위해 호출할 수 있는 정적 reload() 메서드를 추가합니다:

struct WeatherSnippet: SnippetIntent {
    static var title: LocalizedStringResource = "Weather Snippet"

    @Parameter(title: "City")
    var city: City

    func perform() async throws -> some IntentResult & ShowsSnippetView {
        let forecast = try await weatherService.forecast(for: city)
        return .result(view: ForecastSnippet(forecast: forecast))
    }

    static func reload() async throws {
        // Triggered by the system when it wants fresh snippet data
        // (e.g., after the data source signals an update)
    }
}

응답에 스니펫 뷰를 첨부하려는 비스니펫 전용 인텐트의 경우, 기존 AppIntent + ShowsSnippetView 패턴은 여전히 작동합니다:

struct WeatherForecastIntent: AppIntent {
    static var title: LocalizedStringResource = "Weather Forecast"

    @Parameter(title: "City")
    var city: City

    func perform() async throws -> some IntentResult & ProvidesDialog & ShowsSnippetView {
        let forecast = try await weatherService.forecast(for: city)

        return .result(
            dialog: "Here's the forecast for \(city.name).",
            view: ForecastSnippet(forecast: forecast)
        )
    }
}

struct ForecastSnippet: View {
    let forecast: Forecast
    var body: some View {
        VStack(alignment: .leading) {
            Text(forecast.headline).font(.headline)
            HStack {
                ForEach(forecast.days) { day in
                    DayCell(day: day)
                }
            }
            Button("Open in App") {
                // App-launching action wired via App Intents
            }
        }
        .padding()
    }
}

결과 타입의 ShowsSnippetView 준수는 시스템에 SwiftUI 뷰를 dialog와 함께 렌더링하라고 알립니다. 스니펫은 인터랙티브합니다: 내부의 버튼은 다른 App Intents를 트리거할 수 있고, 사용자는 스크롤할 수 있으며, 뷰는 시스템의 인터랙션 레이어에 참여합니다.

인터랙티브 스니펫이 가치 있는 사례: 일기 예보, 캘린더 이벤트, 교통 시간, 스포츠 점수, 패키지 추적. 시스템 표면에서 한 줄 dialog 응답 이상의 것을 사용자가 원하는 모든 곳입니다.

엔티티 뷰 어노테이션

엔티티 뷰 어노테이션을 통해 앱은 AppEntity가 다양한 시스템 컨텍스트에서 어떻게 렌더링되어야 하는지 선언할 수 있습니다5. 이 메커니즘은 DisplayRepresentation을 여러 변형으로 확장합니다:

struct Recipe: AppEntity {
    var displayRepresentation: DisplayRepresentation {
        DisplayRepresentation(
            title: "\(title)",
            subtitle: "\(cuisine) - \(time) min",
            image: .init(named: imageName)
        )
    }
}

기존 모델은 단일 DisplayRepresentation을 반환합니다. iOS 26은 엔티티가 렌더링되는 위치(컴팩트 리스트, 히어로 카드, Spotlight 결과, Visual Intelligence 패널)에 따라 시스템이 선택할 수 있도록 컨텍스트별 변형을 제공할 수 있게 합니다. 프레임워크는 컨텍스트마다 적절한 변형을 선택하고, 앱은 각각을 선언합니다.

이 패턴은 엔티티 렌더링이 빽빽하게 채워진 Spotlight 리스트와 단일 카드 형식의 Visual Intelligence 응답에서 다르게 나타나야 하는 앱을 지원합니다. 변형은 앱이 컨텍스트를 감지할 필요 없이 구성됩니다.

기존 App Intents와의 결합

iOS 26 추가 사항은 기존 App Intents 기본 요소와 결합됩니다:

  • 앱의 AppShortcutsProvider(Accessibility as platform에서 다룸)는 음성, 액션 버튼, Spotlight를 위한 단축어를 등록합니다.
  • AppShortcut은 사용자의 요청에서 해결되는 @Parameter 속성을 가진 AppIntent를 참조합니다.
  • AppIntent.perform() 메서드는 스니펫 뷰(ShowsSnippetView)나 dialog(ProvidesDialog)를 포함할 수 있는 결과 타입을 반환합니다.
  • 인텐트의 매개변수가 참조하는 AppEntity 타입은 이제 @DeferredProperty와 엔티티 뷰 어노테이션을 지원합니다.
  • 새로운 IntentValueQuery 타입은 동일한 엔티티가 Visual Intelligence에 표시되도록 합니다.

형태는 다음과 같습니다: 기존 모델은 보존되며, iOS 26 작업은 동일한 엔티티 표면에 새로운 타입(쿼리)과 새로운 어노테이션(deferred, 뷰 변형)을 추가하는 것입니다.

일반적인 실패

App Intents 2.0 채택 실패에서 나오는 세 가지 패턴:

범위가 지정되지 않은 결과를 반환하는 IntentValueQuery. 관련성과 무관하게 descriptor의 검색어와 일치하는 모든 제품을 반환하는 쿼리는 Visual Intelligence 경험을 희석시킵니다. 해결책: 시스템 UI에서 반환된 각 엔티티가 그 자리를 차지할 만하도록 쿼리 범위를 지정합니다(상위 N 관련성, 최신성 가중, 사용자 개인화).

빠른 값에 대한 @DeferredProperty. deferred 메커니즘은 진정으로 비싼 계산을 위한 것입니다. 빠른 인메모리 속성을 deferred로 표시하는 것은 이점 없이 비동기 오버헤드만 추가합니다. 해결책: deferred했을 때 실제로 효과를 얻는 속성(서버 fetch, 큰 계산, ML 모델 호출)에만 @DeferredProperty를 사용합니다.

전체 앱과 같은 인터랙티브 스니펫. 스니펫은 컴팩트한 UI 표면입니다. 미니 앱처럼 다루면 답답하거나 렌더링이 느린 스니펫이 됩니다. 해결책: 스니펫은 즉각적인 응답과 한두 가지 관련 액션에 집중하고, 복잡한 흐름은 “Open in App” 버튼을 사용하여 전체 앱으로 넘깁니다.

이 패턴이 iOS 26+ 앱에 의미하는 것

세 가지 핵심 사항.

  1. 시각적 콘텐츠가 있는 모든 앱에 IntentValueQuery를 추가하세요. 쇼핑, 레시피, 제품, 위치, 식별 가능한 객체. Visual Intelligence 통합은 새로운 발견 표면이며, 참여하지 않는 앱은 그곳에서 보이지 않습니다.

  2. 비싼 엔티티 필드에 @DeferredProperty를 사용하세요. 자세한 설명, 계산된 메트릭, 서버에서 가져온 데이터. 기본 비동기 패턴은 현대적인 Swift 코드와 일치하며, 빌드 비용이 저렴한 엔티티 반환을 빠르게 유지합니다.

  3. 고가치 쿼리 타입에 인터랙티브 스니펫을 채택하세요. 날씨, 캘린더, 교통, 스포츠, 패키지 추적. 한 줄 dialog로는 부족하지만 전체 앱 실행은 과한 경우, 스니펫 UI는 사용자를 시스템 응답 표면에 머물게 합니다.

전체 Apple Ecosystem 클러스터: 타입 기반 App Intents; MCP 서버; 라우팅 질문; Foundation Models; 런타임 vs 도구화 LLM 구분; 세 가지 표면; 단일 진실 공급원 패턴; 두 개의 MCP 서버; Apple 개발용 훅; Live Activities; watchOS 런타임; SwiftUI 내부 구조; RealityKit의 공간 정신 모델; SwiftData 스키마 규율; Liquid Glass 패턴; 멀티 플랫폼 출시; 플랫폼 매트릭스; Vision 프레임워크; Symbol Effects; Core ML 추론; Writing Tools API; Swift Testing; Privacy Manifest; 플랫폼으로서의 접근성; SF Pro 타이포그래피; visionOS 공간 패턴; Speech 프레임워크; SwiftData 마이그레이션; tvOS 포커스 엔진; @Observable 내부 구조; SwiftUI Layout 프로토콜; 커스텀 SF Symbols; AVFoundation HDR; watchOS 워크아웃 라이프사이클; 내가 쓰지 않기로 한 것들. 허브는 Apple Ecosystem 시리즈에 있습니다. iOS-with-AI-agents의 더 넓은 컨텍스트는 iOS Agent Development 가이드를 참조하세요.

FAQ

IntentValueQuery를 테스트하려면 Apple Intelligence가 활성화되어야 하나요?

전체 Visual Intelligence 테스트의 경우, 그렇습니다. Visual Intelligence는 Apple Intelligence 호환 하드웨어(iPhone 15 Pro 이상, M1 Mac 이상)와 iOS 26+ 그리고 Apple Intelligence 활성화가 필요합니다. 개발 단계에서는 SemanticContentDescriptor를 직접 구성하고 전체 시스템 스택 없이 쿼리의 values(for:) 메서드를 호출하여 단위 테스트에서 쿼리를 테스트할 수 있습니다.

@DeferredProperty가 throw할 수 있나요?

네. 비동기 getter 시그니처는 get async throws이며, 예외는 시스템으로 전파됩니다. 시스템은 deferred 속성의 값 없이 엔티티를 표시하거나(또는 일부 컨텍스트에서는 오류 상태를 표시) 실패를 처리합니다. 앱은 충돌하지 않고 의미 있는 오류 상태를 반환하며 우아하게 실패해야 합니다.

인터랙티브 스니펫 콘텐츠는 애니메이션을 지원하나요?

네, 표준 SwiftUI 애니메이션 기본 요소로 가능합니다. 스니펫의 뷰는 시스템의 응답 표면에서 실행되며, 이는 앱 내 SwiftUI와 동일한 애니메이션 인프라를 지원합니다. 플랫폼 규약에 맞는 애니메이션 어휘는 클러스터의 Symbol Effects 글을 참조하세요.

엔티티 뷰 어노테이션은 위젯과 어떻게 상호작용하나요?

위젯은 별도의 WidgetKit 표면입니다. 엔티티 뷰 어노테이션은 App Intents 컨텍스트(Spotlight, Visual Intelligence, Siri 응답) 내에서 적용됩니다. 동일한 데이터를 AppEntity와 위젯 모두로 노출하는 앱의 경우, 두 표면은 별도의 UI 선언이 필요합니다. 앱은 보통 기본 데이터 모델을 공유하고 표면별로 얇은 표시 뷰를 작성합니다.

이것과 MCP 도구의 관계는 무엇인가요?

App Intents는 Apple Intelligence의 인텐트 표면이고, MCP 도구는 일반 LLM를 위한 에이전트-서버 프로토콜입니다. iOS 26은 App Intents에 Visual Intelligence를 추가합니다(Apple의 표면). Apple이 아닌 에이전트(Claude, GPT 클래스)의 경우, 로컬이나 원격에서 실행되는 MCP 도구가 동일한 개념적 영역을 다룹니다. 클러스터의 App Intents vs MCP Tools 글이 라우팅 질문을 다룹니다.

IntentValueQueryEntityQuery와 결합할 수 있나요?

네. 둘은 서로 다른 표면을 담당합니다: EntityQuery는 사용자가 엔티티 이름을 입력하거나 말할 때(Spotlight, Siri)를 위한 것이고, IntentValueQuery는 사용자가 이미지 컨텍스트와 함께 Visual Intelligence에 있을 때를 위한 것입니다. 앱의 AppEntity는 두 쿼리 모두 등록할 수 있으며, 시스템은 컨텍스트에 맞는 쿼리를 선택합니다.

참고 자료


  1. Apple Developer Documentation: App Intents. AppIntent, AppEntity, 쿼리, 매개변수, iOS 26 추가 사항을 다루는 프레임워크 레퍼런스. 

  2. Apple Developer: Explore new advances in App Intents (WWDC 2025 세션 275). IntentValueQuery, SemanticContentDescriptor, Visual Intelligence 통합 소개. 

  3. Apple Developer Documentation: @DeferredProperty(). iOS 26의 App Entities를 위해 도입된 비동기 계산 엔티티 속성 매크로. WWDC 2025 세션 275 (Explore new advances in App Intents)에서 다룸. 

  4. Apple Developer Documentation: 시스템 컨텍스트(Spotlight, Visual Intelligence, Siri 응답 영역)에서 렌더링되는 SwiftUI 뷰와 결합된 ShowsSnippetView 결과 타입을 통한 인터랙티브 App Intents 스니펫. 

  5. Apple Developer Documentation: DisplayRepresentation와 엔티티 뷰 어노테이션. AppEntity가 다양한 시스템 컨텍스트에서 어떻게 렌더링되는지 선언하는 메커니즘. 

관련 게시물

App Intents vs MCP: The Routing Question

Two protocols, one app. App Intents expose your app to Apple Intelligence. MCP exposes the same domain to Claude, ChatGP…

16 분 소요

Three Surfaces: Human, Apple Intelligence, Agent

Every iOS app capability faces three surfaces: human, Apple Intelligence, agent. Each has different obligations, renderi…

17 분 소요

The Cleanup Layer Is the Real AI Agent Market

Charlie Labs pivoted from building agents to cleaning up after them. The AI agent market is moving from generation to pr…

15 분 소요