View in English

  • 메뉴 열기 메뉴 닫기
  • Apple Developer
검색
검색 닫기
  • Apple Developer
  • 뉴스
  • 둘러보기
  • 디자인
  • 개발
  • 배포
  • 지원
  • 계정
페이지에서만 검색

빠른 링크

5 빠른 링크

비디오

메뉴 열기 메뉴 닫기
  • 컬렉션
  • 주제
  • 전체 비디오
  • 소개

더 많은 비디오

  • 소개
  • 요약
  • 자막 전문
  • 코드
  • 앱 인텐트 알아보기

    앱 인텐트 프레임워크에 대해 알아보고 Apple의 개발자 플랫폼 내에서 점점 더 중요해지는 이 프레임워크의 역할에 대해 살펴보세요. 인텐트, 엔티티, 쿼리 등 핵심 개념을 차근차근 설명합니다. 이와 같은 요소가 한데 어우러지는 방법과 Spotlight 및 단축어 같은 소프트웨어 기능부터 동작 버튼 같은 하드웨어 기능까지 이러한 요소를 통해 Apple의 기기로 앱을 통합하는 방법에 대해서 학습할 수 있습니다. 향후 앱 인텐트가 Apple Intelligence와 통합하는 앱의 게이트웨이 역할을 하는 방법에 대해서도 설명합니다.

    챕터

    • 0:00 - 서론
    • 0:45 - 앱 인텐트 생태계
    • 2:47 - 프레임워크 살펴보기
    • 21:15 - 작동 방식

    리소스

    • Accelerating app interactions with App Intents
    • Adopting App Intents to support system experiences
    • App intent domains
    • App Intents
    • App Shortcuts
    • Building a workout app for iPhone and iPad
    • Creating your first app intent
    • Integrating actions with Siri and Apple Intelligence
    • Making actions and content discoverable and widely available
      • HD 비디오
      • SD 비디오

    관련 비디오

    WWDC25

    • 대화형 스니펫 설계하기
    • 앱 인텐트로 단축어 및 Spotlight 개발하기

    WWDC24

    • 시스템 경험을 위한 앱 인텐트 디자인하기
    • 앱 인텐트로 사용자에게 앱의 핵심 기능 제공하기
    • 앱 인텐트의 새로운 내용
    • 앱을 Siri로 가져오기
  • 비디오 검색…

    Swift Intelligence Frameworks 팀의 엔지니어링 매니저인 James입니다 이 세션에서는 App Intents에 대해 이야기하겠습니다 App Intents는 시스템 전체 및 모든 Apple 플랫폼에서 앱의 발견 가능성, 가시성, 기능을 확장해 주는 프레임워크입니다 먼저 App Intents가 Apple 개발자 생태계에서 왜 이전보다 더 중요한지에 대해 설명하겠습니다

    다음으로 이 프레임워크를 사용하여 앱의 동작과 엔티티를 시스템 전체에서 사용자에게 제공하는 방법을 보여드리고 앱 인텐트를 작성할 때 숙지해야 할 중요한 세부 사항을 공유하며 마무리하겠습니다

    앱 인텐트 사용 방법을 다루기 전에 사용해야 할 이유를 알려드릴게요 App Intents는 단순히 기능을 빌드하기 위해 앱으로 가져오는 프레임워크가 아닙니다 App Intents는 앱의 기능을 시스템 전체로 확장해 주는 생태계입니다

    App Intents 덕분에 사용자에게 Spotlight에서 맞춤형 결과를 동작 버튼을 위한 컨텍스트 인식 경험을 위젯에서 구성과 상호작용 기능을 제어 센터에서 쉽게 접근 가능한 관리 기능을 Apple Pencil Pro를 위한 맞춤형 동작을 제공할 수 있죠

    올해 새롭게 추가된 기능으로 이제 Spotlight는 Mac 내 모든 위치에서 앱의 동작을 실행할 수 있습니다

    App Intents 프레임워크는 앱을 열지 않은 사용자에게도 풍부한 경험을 제공할 수 있도록 합니다 모든 것은 메모 열기, 운동 시작이나 쇼핑 목록에 항목 추가 등 앱이 실행할 수 있는 동작으로 시작합니다 동작은 앱을 위한 동사와 같으며 예상하셨듯이 개발자는 간단히 인텐트라고도 하는 앱 인텐트를 만들어 동작을 설명하죠 인텐트를 만들면 시스템이 동작을 올바르게 실행할 수 있도록 추가 정보를 제공하게 됩니다 인텐트는 매개변수를 받고 값을 반환할 수 있습니다 이 입력과 출력은 기본 Swift 타입이거나 앱에서 정의된 타입일 수 있죠 앱 인텐트로 두 가지 유형의 값을 만들 수 있습니다 상수 값 집합을 가진 타입에는 AppEnum을 동적 타입에는 AppEntity를 사용하세요 AppEnum과 AppEntity는 앱을 위한 명사와 같죠 앱 단축어는 앱 인텐트를 강조해 더 쉽게 접근하고 발견할 수 있도록 합니다 앱 단축어는 Spotlight 검색이나 Siri 사용, 동작 버튼 구성과 같은 상황에서 표시됩니다 앱 단축어는 앱의 문장과 같으며 인텐트 및 해당 인텐트가 실행되는데 필요한 매개변수로 구성됩니다 직접 보여드리는 것이 가장 효과적이겠죠 처음으로 인텐트를 만들 때 무엇이 필요한지 확인해 보죠 저는 가족과 함께 여행하는 것을 좋아하며 전 세계의 유명한 랜드마크를 탐색할 수 있는 앱을 개발 중입니다 이 앱에는 몇 가지 섹션이 있습니다 랜드마크 목록을 스크롤하며 탐색하거나 지도에서 랜드마크를 확인하거나 제가 만든 컬렉션을 볼 수 있습니다 많은 사용자가 랜드마크 그리드를 스크롤하는 것을 좋아합니다 사용자가 더 편리하게 그리드로 이동하도록 하는 앱 인텐트를 빌드하겠습니다 어떻게 해야 할까요? 먼저 AppIntent 프로토콜을 채택하는 새 struct를 정의하여 시작하겠습니다 앱 인텐트에는 최소한 title과 perform 메서드가 있어야 합니다

    title은 고유하고 현지화된 문자열로 인텐트의 이름으로 표시됩니다 perform 메서드는 인텐트의 로직을 포함합니다 Landmarks 뷰를 열기 위해 shared navigator를 사용할게요 탐색이 main 스레드에서 수행되어야 하므로 perform 메서드에 @MainActor를 적용합니다

    perform 메서드는 IntentResult를 반환합니다 IntentResult는 Siri가 말할 수 있는 대화 표시 가능한 뷰 스니펫 등을 포함할 수 있습니다 기본적으로 인텐트를 실행해도 앱이 포그라운드로 전환되지 않으므로 대화상자와 스니펫을 표시하여 동작 결과를 보여 주는 것이 좋습니다

    이 인텐트는 앱 내에서 화면을 탐색하도록 설계되었으므로 실행될 때 앱을 열도록 구성하겠습니다 새 supportedModes 속성을 foreground로 설정하여 인텐트가 실행되기 전에 앱을 열도록 할게요 아주 간단하게 첫 인텐트를 빌드했습니다 앱을 설치한 후 인텐트는 단축어에서 찾을 수 있습니다 새 단축어를 만들고 탐색 인텐트를 추가할 수 있죠 단축어를 실행하면 앱이 포그라운드로 이동하고 Landmarks 뷰가 바로 표시되죠 제 앱에는 컬렉션을 보기 위한 섹션과 지도에 랜드마크를 표시하는 섹션도 있습니다 인텐트로 이러한 앱 섹션으로 이동할 수 있다면 편리하겠네요 제 앱은 섹션을 모델링하기 위해 간단한 Swift enum을 사용합니다 타입이 프레임워크와 호환되도록 AppEnum 프로토콜을 채택할게요 AppEnum을 사용하려면 몇 가지 요건만 충족하면 됩니다 문자열로부터 인스턴스화가 가능해야 하므로 String 원본 값을 추가할게요 TypeDisplayRepresentation은 타입을 전체적으로 설명하고 caseDisplayRepresentation은 enum의 각 case를 설명하죠

    정보가 컴파일 시점에 사용되므로 이러한 표현은 상수 값을 가져야 합니다

    인텐트에 새 변수를 추가하여 NavigationOption을 홀딩하겠습니다 @Parameter 속성을 추가하여 인텐트 매개변수로 만들게요 인텐트 매개변수는 인텐트의 입력으로 사용됩니다 필수 또는 선택 사항으로 지정할 수 있습니다 perform이 호출되기 전에 런타임에서 값이 있는지 확인할 수 있도록 필수로 지정했습니다

    해결된 navigationOption을 사용하도록 perform 메서드를 업데이트한 다음 새 동작에 맞게 title도 바꿀게요

    단축어로 돌아가면 NavigationOption이 편집 가능한 매개변수로 표시되죠 인텐트를 실행하면 섹션을 선택하라고 단축어가 요청합니다 Map을 선택하면 해당 뷰가 바로 열립니다 App Intents 프레임워크의 타입은 높은 수준으로 맞춤화할 수 있죠 덕분에 기본 요소를 빠르게 빌드한 다음 경험을 조정할 수 있습니다 인텐트가 더 유용해지도록 정보를 추가해 보겠습니다

    기본적으로 단축어는 각 매개변수를 행으로 표시합니다 행을 탭하면 해당 타입의 값 목록이 표시됩니다 이대로도 좋지만 조금 더 조정해서 경험을 개선할게요

    AppEnum은 각 case에 대해 title만 요구하지만 아이콘과 같은 추가 정보로 구성할 수 있습니다

    아이콘을 추가하려면 DisplayRepresentation 이니셜라이저가 필요합니다 이제 각 case에 대해 기호를 추가할 수 있죠 이렇게 하면 단축어의 선택기에서 이미지가 표시됩니다 인텐트는 ParameterSummary라는 유동적인 문장 형식으로 구성할 수 있습니다 ParameterSummary는 동작과 관련 매개변수를 사용자가 읽기 편한 방식으로 설명합니다

    매개변수가 문자열에 보간된 형태로 요약을 제공할게요 이렇게 하면 단축어가 선택 가능한 매개변수를 인라인으로 표시하여 동작에 관한 더 유용한 설명을 제공합니다

    사실 이 문구도 문장처럼 이해가 쉽지는 않네요 매개변수에 맞춤화된 title을 추가하여 해결할 수 있습니다

    값을 요청할 때 표시할 맞춤화된 대화상자도 추가하겠습니다 올해 새롭게 추가된 기능으로 인텐트에 모든 필수 매개변수를 포함해 ParameterSummary를 구현하면 사용자가 Mac의 Spotlight에서 해당 동작을 실행할 수 있습니다 Spotlight의 신규 기능에 대해 자세히 알아보려면 이 세션을 시청해 보세요

    앱의 동작을 인텐트로 모델링하면 사용자가 강력한 단축어와 자동화를 만들 수 있습니다 그러나 앱에서 매우 중요한 인텐트는 앱이 설치된 순간부터 제공되어야 합니다 앱 단축어를 채택하여 제공할 수 있습니다

    앱 단축어는 시스템 전체에 앱 인텐트를 자동으로 노출하는 타입입니다 앱 단축어는 Spotlight 검색에서 눈에 잘 띄게 표시됩니다 사용자는 동작을 실행하는 문구를 말하여 Siri를 통해 앱 단축어를 실행할 수 있죠 동작 버튼이나 Apple Pencil 스퀴즈를 통해 실행되도록 구성할 수 있죠 앱 단축어는 사용자가 설정하지 않아도 단축어 앱에 표시되며 특히 단 몇 줄의 코드만으로 빌드할 수 있어서 유용합니다 앱 단축어를 빌드하는 방법을 알아보죠

    앱은 AppShortcutsProvider를 통해 앱 단축어를 제공합니다 앱에서는 모든 앱 단축어를 포함하는 단일 Provider를 정의해야 합니다 앱 단축어는 intent의 인스턴스와 phrase 목록 title 및 이미지로 구성됩니다 앱 단축어를 Siri에서 말하거나 입력하여 실행할 수 있습니다 각 phrase에는 applicationName 플레이스홀더가 있어야 합니다 phrase는 최대 1개의 intent 매개변수를 포함할 수 있습니다 제공되는 경우 해당 타입의 각 값에 대해 앱 단축어가 생성됩니다

    이 간단한 구조만으로 앱 단축어를 만들 수 있습니다 앱의 AppShortcut은 단축어의 새 섹션에서 표시됩니다 phrase는 생성되는 앱 단축어에 영향을 미칩니다 매개변수 없이 phrase를 제공하면 title과 이미지 이름으로 앱 단축어가 생성됩니다 AppEnum을 포함한 phrase를 제공했으므로 case마다 앱 단축어가 생성됩니다

    이제 Siri나 Spotlight를 통해 인텐트를 실행할 수 있습니다 앱 단축어를 빌드하면 사용자가 인텐트를 쉽게 찾을 수 있습니다 WWDC23의 이 세션을 시청하여 앱 단축어 빌드 방법을 자세히 알아보세요

    제 앱에서 랜드마크는 상당한 비중을 차지합니다 인텐트를 통해 랜드마크에 대해 동작을 실행하면 매우 편하겠죠 항상 표시되는 탐색 옵션과 달리 랜드마크는 동적이므로 AppEnum을 사용할 수 없습니다 대신 AppEntity를 만들어 랜드마크를 모델링하겠습니다

    앱에 이미 Landmark 타입이 있습니다 이 타입이 AppEntity를 따르도록 할 수 있지만 이번에는 새로운 LandmarkEntity struct를 만들게요 이 타입은 앱 인텐트와 기본 데이터 모델을 연결해 줍니다

    AppEntity는 식별 가능해야 하므로 ID를 추가하겠습니다 이 식별자는 영구적이어야 하며 이 ID를 통해 엔티티의 인스턴스를 조회할 수 있어야 합니다 나중에 다시 설명하겠습니다 인텐트가 매개변수를 포함하는 것처럼 엔티티는 @Property 속성으로 표시된 속성을 포함할 수 있습니다 엔티티는 단축어에서 노출되며 찾기 및 필터링 동작에서 사용될 수 있습니다 이 값을 데이터 모델에서 직접 설정할 수도 있지만 올해부터는 엔티티 속성에 Getter를 추가할 수 있습니다 새로 추가된 @ComputedProperty 속성을 사용해서요 타입 간에 값을 복사하는 대신 데이터 모델을 따를 수 있죠 AppEnum처럼 AppEntity도 타입 및 타입 인스턴스에 대한 Representation이 필요합니다

    AppEntity에는 쿼리라는 정보가 추가로 필요하죠

    AppEnum이 알려진 값을 갖는 반면 AppEntity는 동적입니다 제 앱에 있는 랜드마크의 수는 달라질 수 있습니다 쿼리는 시스템이 엔티티에 대해 추론하는 방법입니다 이는 다양한 질문을 답하며 진행됩니다 첫 번째 유형의 질문은 “어떤 엔티티가 존재하나요?”입니다 쿼리는 이 질문에 답변하고 일치하는 엔티티의 컬렉션을 반환합니다

    쿼리는 이 질문의 다양한 형태를 지원합니다 예를 들어 EntityStringQuery는 “이 문자열과 일치하는 엔티티가 있어?”라고 질문합니다 EntityPropertyQuery는 “이 주에 있는 모든 랜드마크는 무엇인가?”라고 물을 수 있죠

    모든 쿼리는 매우 구체적인 한 질문에 답해야 합니다 “이 ID에 해당하는 엔티티는 무엇인가?” 이는 시스템이 엔티티를 고유하게 참조하고 필요할 때만 해결할 수 있도록 합니다 쿼리를 제공하려면 EntityQuery 프로토콜을 준수하는 타입을 생성합니다 쿼리는 entities(for:) 메서드를 통해 “이 ID를 가진 엔티티는 무엇인가?”라는 질문에 답변합니다 식별자 배열을 입력으로 받고 엔티티 인스턴스 배열을 반환하죠 “어떤 엔티티가 존재해?”라는 질문은 나중에 다룰게요 인스턴스를 가져오기 위해 쿼리는 종종 로컬 데이터베이스나 다른 종속성에 접근해야 합니다 @Dependency 속성을 사용하여 쿼리에 종속성을 삽입할 수 있습니다 공유된 AppDependencyManager를 사용하여 종속성을 등록해야 합니다 가능하면 종속성은 앱의 라이프사이클 초반에 등록하는 것이 좋습니다 랜드마크에 대한 AppEntity를 만들었으므로 이를 인텐트를 통해 활용할 수 있습니다 여행 중에 가장 가까운 랜드마크를 확인할 수 있으면 좋겠네요 이를 위한 앱 인텐트를 만들겠습니다 기본적인 ClosestLandmarkIntent로 시작할게요 데이터 모델에서 가장 가까운 랜드마크를 가져와야 합니다 인텐트에서도 종속성이 지원되므로 종속성을 추가할 수 있습니다 perform 메서드에 LandmarkEntity의 ReturnsValue를 추가하겠습니다 인텐트 매개변수로 사용된 타입은 인텐트에서 반환될 수도 있습니다 반환된 값은 여러 단계의 단축어에서처럼 다른 인텐트의 입력으로 사용할 수 있습니다 대화상자와 뷰 스니펫도 반환되도록 설정하겠습니다 이렇게 하면 인텐트가 결과물을 표시하거나 음성으로 안내하게 됩니다 마지막으로 perform 메서드를 구현하겠습니다 가장 가까운 랜드마크를 찾은 후 엔티티, 대화상자, 뷰를 포함한 결과를 반환하겠습니다 대화상자와 뷰를 모두 제공하면 인텐트가 어떤 방식으로 호출되더라도 고객은 가장 가까운 랜드마크를 찾을 수 있게 됩니다 이 인텐트에 대해 앱 단축어를 만들면 인텐트의 실행이 더 수월해질 것입니다 고객이 휴대폰을 주머니에 넣은 상태에서도 Siri나 Spotlight를 사용하여 이 인텐트에 접근할 수 있죠 인텐트, 엔티티, 쿼리는 앱 인텐트의 기본 구성 요소입니다 각 프로토콜에 있는 하위 프로토콜과 구성을 사용하면 더 많은 기능을 제공할 수 있습니다 이제 AppEntity를 조정하여 추가 경험을 제공하는 방법을 살펴보겠습니다 가장 가까운 랜드마크의 사진을 쉽게 확인하는 기능을 만들게요 단축어 앱을 사용하여 이를 구현할 수 있죠 먼저 단축어를 만들고 Find Closest Landmark 인텐트를 추가하겠습니다 결과를 확인하기 위해 Show Content 동작을 추가할게요 이 동작은 인텐트 결과를 가져와 렌더링합니다 기본적으로 이 동작은 LandmarkEntity의 displayRepresentation을 표시하지만 렌더링할 다른 엔티티 속성을 선택할 수 있죠

    LandmarkEntity는 이미지를 직접 보유하지 않지만 이미지의 경로를 포함합니다 Transferable 프로토콜을 사용하여 엔티티의 imageRepresentation을 선언할 수 있습니다 Transferable은 타입의 다양한 데이터 표현을 선언적으로 설명하는 방법입니다 이러한 representation은 앱 간에 데이터를 공유하는 데 사용될 수 있죠 이미지 데이터를 타입의 TransferRepresentation 일부로 제공할게요 단축어로 돌아가면 엔티티의 imageRepresentation을 표시하도록 선택할 수 있습니다 단축어를 실행하면 랜드마크의 이미지가 표시됩니다 imageRepresentation 선언 시 누릴 수 있는 이점은 더 있습니다 사진을 촬영하는 모든 동작의 입력으로 이 이미지 값을 사용할 수 있습니다 다른 앱에 있는 사진도 활용할 수 있죠 Transferable에 대해 더 알아보려면 이 세션을 확인하세요

    시스템에서 엔티티를 더 쉽게 찾을 수 있도록 한 가지만 더 변경하겠습니다 Spotlight는 여러 앱에서 작동하는 강력한 시맨틱 검색을 제공합니다 엔티티를 Spotlight에 제공하면 시스템이 콘텐츠를 이해하는 범위가 확장됩니다 Spotlight가 엔티티를 인덱싱하도록 IndexedEntity 프로토콜을 채택할게요 IndexedEntity는 Core Spotlight 속성 집합을 포함하는 AppEntity입니다

    올해부터는 속성에 Spotlight indexingKey를 속성에 바로 추가할 수 있습니다 속성을 주석 처리하면 Spotlight이 고객에게 관련성 높은 정보를 더 표시하죠 인덱싱된 엔티티를 제공하면 프레임워크가 검색 가능한 항목과 속성 집합을 자동으로 생성합니다 제공된 엔티티는 Spotlight에서 확인할 수 있습니다 기본적으로 엔티티를 누르면 앱이 포그라운드로 이동합니다 랜드마크 세부 정보 뷰가 바로 열리도록 하여 이 경험을 더 개선해 보겠습니다 먼저 OpenIntent 프로토콜을 준수하는 인텐트를 만들겠습니다 이 프로토콜을 채택한 인텐트는 실행 전에 앱을 자동으로 엽니다 그러므로 SupportedMode를 추가하지 않아도 되죠 OpenIntent에는 target 매개변수가 있어야 합니다 Spotlight에서 엔티티를 누르면 해당 엔티티와 일치하는 OpenIntent가 실행됩니다

    navigator를 호출하는 대신 내비게이션을 위해 특별히 설계된 새로운 TargetContentProvidingIntent 프로토콜을 채택할 수 있습니다 이 인텐트에는 perform 메서드가 필요하지 않습니다 대신 뷰에 onAppIntentExecution 한정자를 추가할 수 있습니다 클로저에서 인텐트의 매개변수를 사용하여 SwiftUI 탐색을 수행할 수 있습니다

    이제 Spotlight에서 랜드마크를 탭하면 해당 랜드마크의 세부 정보 뷰로 바로 이동하죠 이 섹션을 마무리하기 전에 쿼리와 쿼리가 엔티티를 제공하는 방법을 더 설명하겠습니다 실제로 작동하는 모습을 보기 위해 랜드마크를 여는 새 앱 단축어를 만들게요 provider에 새 AppShortcut과 두 phrase를 추가했습니다 landmark 매개변수가 있는 phrase와 해당 매개변수가 없는 phrase죠 그러나 앱 단축어를 실행해도 선택 가능한 랜드마크가 표시되지 않네요 예상하셨듯이 쿼리를 사용하여 랜드마크를 제공할 수 있습니다 EntityQuery에 선택적인 suggestedEntities 메서드를 도입하여 고객이 좋아하는 랜드마크를 반환하겠습니다 인텐트를 다시 실행하면 추천된 엔티티의 목록이 표시됩니다 추천된 엔티티는 다른 용도로도 활용할 수 있습니다 이 인텐트에 매개변수화된 phrase를 추가한 것을 기억하시나요? provider에서 updateAppShortcutParameters 메서드를 호출하여 추천된 각 엔티티에 대해 앱 단축어를 생성할 수 있죠 이제 Siri와 단축어를 사용하여 내가 좋아하는 랜드마크로 직접 이동할 수 있습니다 쿼리는 엔티티에 대한 다양한 질문에 답할 수 있습니다 엔티티가 모두 메모리에 들어갈 경우 모든 엔티티를 반환하기 위해 EnumerableEntityQuery를 사용할 수 있습니다 앱 인텐트는 이 쿼리에서 더 복잡한 쿼리를 파생시킬 수 있습니다 EntityPropertyQuery는 predicate 집합을 기반으로 정렬된 엔티티 목록을 반환하는 기능을 추가합니다 EntityStringQuery를 구현하여 문자열에서 엔티티를 검색하는 기능을 추가하겠습니다 이름이나 설명이 문자열과 일치하는 랜드마크의 목록을 반환하도록 할게요 이제 고객은 랜드마크가 필요한 인텐트를 구성할 때 모든 랜드마크를 검색할 수 있습니다 App Intents 프레임워크에는 제가 설명하지 않은 수많은 기능이 있습니다 App Intents 관련 문서에서 이 프레임워크로 사용자를 위한 훌륭한 기능을 제공하는 방법을 모두 살펴보세요 App Intents를 지원하는 아키텍처에 대한 정보를 공유하며 세션을 마무리할게요 앱 인텐트로 앱을 빌드하면 코드가 Source of Truth가 됩니다 앱 인텐트에는 설정이나 구성 파일이 필요하지 않습니다 Swift 소스 코드는 빌드 시점에 읽혀 앱 인텐트의 representation을 생성하게 됩니다 이 representation은 앱이나 프레임워크 내부에 저장됩니다 앱이 설치된 후 시스템은 이 데이터를 사용하여 앱을 실행하지 않고도 앱의 기능을 이해합니다 인텐트를 예로 들어 이 과정이 어떻게 진행되는지 살펴볼게요 인텐트의 이름은 해당 동작의 고유한 식별자가 됩니다 인텐트의 title은 사용자가 인텐트를 구분할 수 있도록 하며 perform 메서드의 return 시그니처는 인텐트의 결과를 렌더링하는 방법을 설명합니다 이 프로세스는 실행 시점이 아닌 빌드 시점에 진행되므로 특정 값은 상수 값으로 제공되어야 합니다 예를 들어 인텐트의 title은 상수여야 합니다 함수나 계산된 속성을 호출하면 오류가 발생합니다 이 처리는 앱의 각 타깃에 대해 개별적으로 진행됩니다 타깃 간에 앱 인텐트 타입을 제대로 공유하려면 몇 가지 추가 사항에 유의해야 합니다 작년에 Apple은 앱과 앱 인텐트 확장이 프레임워크에 정의된 앱 인텐트 타입을 참조할 수 있는 기능을 도입했습니다 올해부터는 Swift 패키지와 정적 라이브러리에 앱 인텐트를 추가할 수 있습니다 여러 타깃에서 앱 인텐트 타입을 사용할 때는 각 타깃에 대한 추가 정보를 런타임에 제공해야 합니다 이렇게 해야 타입이 올바르게 인덱싱 및 검증됩니다 그 방법을 보여드리겠습니다 제 앱에는 모든 앱 인텐트 코드를 포함하는 target 하나만 있습니다 일부 인텐트를 호스팅할 새 AppIntentsExtension을 추가하고자 합니다 두 타깃 모두 랜드마크에 액세스해야 합니다 따라서 Swift 패키지를 만들고 LandmarkEntity를 거기로 옮길게요 타깃 간에 타입을 공유하려면 각 타깃을 AppIntentsPackage로 등록해야 합니다 먼저 엔티티와 동일한 타깃에 AppIntentsPackage를 만듭니다 앱 타깃에 다른 AppIntentsPackage를 추가합니다 포함된 패키지 목록을 제공할 수 있으므로 방금 생성한 패키지를 포함합니다 마지막으로 확장에 대해서도 동일한 절차를 진행합니다 이렇게 하면 앱 인텐트 런타임에서 패키지에 정의된 모든 타입에 적절히 접근할 수 있습니다 정적 라이브러리에 컴파일되지 않은 코드를 참조할 때는 AppIntentsPackage를 사용하세요

    이제 이 세션을 마무리하겠습니다 App Intents를 처음 접하셨다면 앱에 첫 앱 단축어를 추가하며 시작하세요 그런 다음 프레임워크를 둘러보며 고객에게 가장 유용한 경험을 제공하는 데 도움이 될 만한 기능을 찾아보세요 App Intents를 다루는 다른 세션도 확인해 보시기 바랍니다 시청해 주셔서 감사합니다

    • 3:23 - Navigate Intent

      struct NavigateIntent: AppIntent {
          static let title: LocalizedStringResource = "Navigate to Landmarks"
      
          static let supportedModes: IntentModes = .foreground
      
          @MainActor
          func perform() async throws -> some IntentResult {
              Navigator.shared.navigate(to: .landmarks)
              return .result()
          }
      }
    • 5:02 - Navigation Option App Enum

      enum NavigationOption: String, AppEnum {
          case landmarks
          case map
          case collections
      
          static let typeDisplayRepresentation: TypeDisplayRepresentation = "Navigation Option"
      
          static let caseDisplayRepresentations: [NavigationOption: DisplayRepresentation] = [
              .landmarks: "Landmarks",
              .map: "Map",
              .collections: "Collections"
          ]
      }
    • 5:38 - Navigate Intent with Parameter

      struct NavigateIntent: AppIntent {
          static let title: LocalizedStringResource = "Navigate to Section"
      
          static let supportedModes: IntentModes = .foreground
        
          @Parameter var navigationOption: NavigationOption
      
          @MainActor
          func perform() async throws -> some IntentResult {
              Navigator.shared.navigate(to: navigationOption)
              return .result()
          }
      }
    • 6:57 - Case Display Representations with Images

      static let caseDisplayRepresentations = [
          NavigationOption.landmarks: DisplayRepresentation(
              title: "Landmarks",
              image: .init(systemName: "building.columns")
          ),
          NavigationOption.map: DisplayRepresentation(
              title: "Map",
              image: .init(systemName: "map")
          ),
          NavigationOption.collections: DisplayRepresentation(
              title: "Collections",
              image: .init(systemName: "book.closed")
          )
      ]
    • 7:28 - Navigation Option With Parameter Summary

      struct NavigateIntent: AppIntent {
          static let title: LocalizedStringResource = "Navigate to Section"
      
          static let supportedModes: IntentModes = .foreground
        
          static var parameterSummary: some ParameterSummary {
              Summary("Navigate to \(\.$navigationOption)")
          }
        
          @Parameter(
              title: "Section",
              requestValueDialog: "Which section?"
          )
          var navigationOption: NavigationOption
      
          @MainActor
          func perform() async throws -> some IntentResult {
              Navigator.shared.navigate(to: navigationOption)
              return .result()
          }
      }
    • 9:22 - App Shortcuts Provider and Navigation Intent App Shortcut

      struct TravelTrackingAppShortcuts: AppShortcutsProvider {
          static var appShortcuts: [AppShortcut] {
              AppShortcut(
                  intent: NavigateIntent(),
                  phrases: [
                      "Navigate in \(.applicationName)",
                      "Navigate to \(\.$navigationOption) in \(.applicationName)"a
                  ],                
                  shortTitle: "Navigate",
                  systemImageName: "arrowshape.forward"
              )
          }
      }
    • 11:02 - Landmark Entity

      struct LandmarkEntity: AppEntity {
          var id: Int { landmark.id }
      
          @ComputedProperty
          var name: String { landmark.name }
      
          @ComputedProperty
          var description: String { landmark.description }
        
          let landmark: Landmark
        
          static let typeDisplayRepresentation = TypeDisplayRepresentation(name: "Landmark")
      
          var displayRepresentation: DisplayRepresentation {
              DisplayRepresentation(title: "\(name)")
          }
        
          static let defaultQuery = LandmarkEntityQuery()
      }
    • 13:19 - Landmark Entity Query

      struct LandmarkEntityQuery: EntityQuery {
          @Dependency var modelData: ModelData
        
          func entities(for identifiers: [LandmarkEntity.ID]) async throws -> [LandmarkEntity] {
              modelData
                  .landmarks(for: identifiers)
                  .map(LandmarkEntity.init)
          }
      }
    • 13:50 - App Dependency Manager

      @main
      struct LandmarksApp: App {    
          init() {
              AppDependencyManager.shared.add { ModelData() }
          }
      }
    • 14:18 - Closest Landmark Intent

      struct ClosestLandmarkIntent: AppIntent {
          static let title: LocalizedStringResource = "Find Closest Landmark"
      
          @Dependency var modelData: ModelData
      
          @MainActor
          func perform() async throws 
              -> some ReturnsValue<LandmarkEntity> & ProvidesDialog & ShowsSnippetView {
              
              let landmark = try await modelData.findClosestLandmark()
      
              return .result(
                  value: landmark,
                  dialog: "The closest landmark to you is \(landmark.name)",
                  view: ClosestLandmarkView(landmark: landmark)
              )
          }
      }
    • 15:18 - Closest Landmark App Shortcut

      AppShortcut(
          intent: ClosestLandmarkIntent(),
          phrases: [
              "Find closest landmark in \(.applicationName)"
          ],
          shortTitle: "Closest landmark",
          systemImageName: "location"
      )
    • 16:33 - Transferable

      extension LandmarkEntity: Transferable {
          static var transferRepresentation: some TransferRepresentation {
              DataRepresentation(exportedContentType: .image) {
                  return try $0.imageRepresentationData
              }
          }
      }
    • 17:31 - Indexed Entity

      struct LandmarkEntity: IndexedEntity {
          // ...
          
          @Property(
              indexingKey: \.displayName
          )
          var name: String
      
          @Property(
              indexingKey: \.contentDescription
          )
          var description: String
      }
    • 18:17 - Open Landmark Intent

      struct OpenLandmarkIntent: OpenIntent, TargetContentProvidingIntent {
          static let title: LocalizedStringResource = "Open Landmark"
      
          @Parameter(title: "Landmark", requestValueDialog: "Which landmark?")
          var target: LandmarkEntity
      }
      
      struct LandmarksNavigationStack: View {
          @State var path: [Landmark] = []
      
          var body: some View {
              NavigationStack(path: $path) {}
              .onAppIntentExecution(OpenLandmarkIntent.self) { intent in
                  path.append(intent.target.landmark)
              }
          }
      }
    • 19:24 - Open Landmark App Shortcut

      AppShortcut(
          intent: OpenLandmarkIntent(),
          phrases: [
              "Open \(\.$target) in \(.applicationName)",
              "Open landmark in \(.applicationName)"
          ],
          shortTitle: "Open",
          systemImageName: "building.columns"
      )
    • 19:39 - Suggested Entities

      struct LandmarkEntityQuery: EntityQuery {
          // ...
      
          func suggestedEntities() async throws -> [LandmarkEntity] {
              modelData
                  .favoriteLandmarks()
                  .map(LandmarkEntity.init)
          }
      }
    • 20:06 - Update App Shortcut Parameters

      TravelTrackingAppShortcuts.updateAppShortcutParameters()
    • 20:25 - EnumerableEntityQuery

      extension LandmarkEntityQuery: EnumerableEntityQuery {
          func allEntities() async throws -> [LandmarkEntity] { 
              // ...
          }
      }
    • 20:36 - EntityPropertyQuery

      extension LandmarkEntityQuery: EntityPropertyQuery {
          static var properties = QueryProperties {
              // ...
          }
      
          static var sortingOptions = SortingOptions {
              // ...
          }
      
          func entities(
              matching comparators: [Predicate<LandmarkEntity>],
              mode: ComparatorMode,
              sortedBy: [Sort<LandmarkEntity>],
              limit: Int?
          ) async throws -> [LandmarkEntity] {
              // ...
          }
      }
    • 20:44 - EntityStringQuery

      extension LandmarkEntityQuery: EntityStringQuery {
          func entities(matching: String) async throws -> [LandmarkEntity] {
              modelData
                  .landmarks
                  .filter { $0.name.contains(matching) || $0.description.contains(matching) }
                  .map(LandmarkEntity.init)
          }
      }
    • 23:10 - App Intents Package

      // TravelTrackingKit
      public struct TravelTrackingKitPackage: AppIntentsPackage {}
      public structaLandmarkEntity: AppEntity {}
      
      // TravelTracking
      struct TravelTrackingPackage: AppIntentsPackage {
          static var includedPackages: [any AppIntentsPackage.Type] {
              [TravelTrackingKitPackage.self]
          }
      }
      struct OpenLandmarkIntent: OpenIntent {}
      
      // TravelTrackingAppIntentsExtension
      struct TravelTrackingExtensionPackage: AppIntentsPackage {
          static var includedPackages: [any AppIntentsPackage.Type] {
              [TravelTrackingKitPackage.self]
          }
      }
      struct FavoriteLandmarkIntent: AppIntent {}
    • 0:00 - 서론
    • 모든 Apple 플랫폼에서 앱의 발견 가능성과 기능을 개선하는 데 유용한 프레임워크인 앱 인텐트에 대해 알아보세요. 이 세션에서 다루는 주제에는 프레임워크의 중요성과 구현 방법, 앱 인텐트를 작성하기 위한 모범 사례가 포함됩니다.

    • 0:45 - 앱 인텐트 생태계
    • 앱 인텐트는 Spotlight, 동작 버튼, 위젯, 제어 센터, Apple Pencil Pro 등 시스템 전반에서 앱의 기능을 확장시켜 주는 생태계입니다. 사용자는 앱을 실행하지 않아도 기기 어디서나 앱 작업을 실행할 수 있습니다. 앱 인텐트는 인텐트라고도 하며, 매개변수를 받고 값을 반환합니다. 상수 값을 가진 유형에는 AppEnum을, 동적 유형에는 AppEntity를 사용합니다. 인텐트와 매개변수로 구축된 앱 단축어는 Spotlight, Siri, 동작 버튼에서 접근성과 발견 가능성을 개선합니다.

    • 2:47 - 프레임워크 살펴보기
    • 예시 앱은 전 세계의 유명한 랜드마크를 표시합니다. 사용자 경험을 개선하기 위해 이 앱에는 앱 인텐트가 구현되어 있습니다. 사용자는 Siri, 단축어, Spotlight에서 작업을 바로 실행할 수 있습니다. 이 절차에서는 앱 인텐트 프로토콜을 채택하는 struct를 정의하고 title, perform 메서드, IntentResult를 지정합니다. 인텐트에 매개변수를 추가하면 사용자가 랜드마크 그리드나 지도 뷰 등 앱의 특정 섹션을 선택할 수 있습니다. 사용자가 앱을 더 쉽게 발견하고 편리하게 이용할 수 있도록 시스템 전체에서 인텐트를 자동으로 노출시키는 앱 단축어를 만듭니다. 또한 앱 엔티티를 사용하여 랜드마크와 같은 동적 데이터를 모델링하면 사용자가 인텐트를 통해 특정 랜드마크를 대상으로 작업을 실행할 수 있습니다. 앱 인텐트 프레임워크에서 앱 엔티티는 랜드마크와 같은 동적 데이터를 나타냅니다. 쿼리는 시스템이 이러한 엔티티에 대해 추론할 수 있도록 하는 필수 구성 요소입니다. 쿼리는 모든 엔티티를 가져오거나, 특정 문자열이나 속성과 일치하거나, ID로 엔티티를 고유하게 참조하는 것을 포함한 다양한 질문에 응답합니다. EntityStringQuery, EntityPropertyQuery와 같은 다양한 유형으로 쿼리를 맞춤화할 수 있습니다. 이러한 쿼리는 로컬 데이터베이스나 다른 리소스에 종속될 수 있습니다. ‘@Dependency’ 속성을 사용하여 쿼리에 종속성을 삽입할 수 있습니다. 종속성은 앱의 라이프 사이클 초기에 일찍 등록하는 것이 좋습니다. 앱 인텐트를 사용하면 Siri, Spotlight 또는 단축어를 사용하여 실행할 수 있는 맞춤형 작업을 만들 수 있습니다. 인텐트에서 엔티티 유형을 반환하면 이러한 작업을 여러 단계의 단축어로 연결할 수 있습니다. 엔티티에 Transferable를 적용하면 엔티티가 앱 간에 공유 가능한 상태가 되어 사용자 경험을 개선할 수 있습니다. IndexedEntity 프로토콜을 채택하면 Spotlight에서 시맨틱 검색이 활성화됩니다. 또한 Spotlight에서 엔티티를 탭할 때 앱 내 특정 뷰로 직접 이동하는 OpenIntent를 만들 수 있습니다.

    • 21:15 - 작동 방식
    • 앱 인텐트는 빌드 시점에 Swift 소스 코드를 사용하여 앱 인텐트 표현을 생성하며, 이 표현은 앱 또는 프레임워크 내에 저장됩니다. 이를 통해 시스템은 앱을 실행하지 않고도 앱의 기능을 이해할 수 있습니다. 인텐트의 이름은 인텐트의 고유 식별자로 사용됩니다. title은 인텐트를 구분하는 데 도움이 되며 ‘perform’ 메서드의 return 시그니처는 결과를 렌더링하는 방법을 정의합니다. 처리가 빌드 시점에 실행되므로 특정 인텐트 속성에 대한 상수 값을 제공해야 합니다. 앱과 확장 프로그램처럼 타깃 간에 앱 인텐트 유형을 공유하려면 Swift 패키지나 정적 라이브러리를 사용하세요. 앱 인텐트 런타임에서 공유 유형의 적절한 인덱싱과 검증을 보장하려면 각 타깃을 앱 인텐트 패키지로 등록해야 합니다.

Developer Footer

  • 비디오
  • WWDC25
  • 앱 인텐트 알아보기
  • 메뉴 열기 메뉴 닫기
    • iOS
    • iPadOS
    • macOS
    • tvOS
    • visionOS
    • watchOS
    메뉴 열기 메뉴 닫기
    • Swift
    • SwiftUI
    • Swift Playground
    • TestFlight
    • Xcode
    • Xcode Cloud
    • SF Symbols
    메뉴 열기 메뉴 닫기
    • 손쉬운 사용
    • 액세서리
    • 앱 확장 프로그램
    • App Store
    • 오디오 및 비디오(영문)
    • 증강 현실
    • 디자인
    • 배포
    • 교육
    • 서체(영문)
    • 게임
    • 건강 및 피트니스
    • 앱 내 구입
    • 현지화
    • 지도 및 위치
    • 머신 러닝 및 AI
    • 오픈 소스(영문)
    • 보안
    • Safari 및 웹(영문)
    메뉴 열기 메뉴 닫기
    • 문서(영문)
    • 튜토리얼
    • 다운로드(영문)
    • 포럼(영문)
    • 비디오
    메뉴 열기 메뉴 닫기
    • 지원 문서
    • 문의하기
    • 버그 보고
    • 시스템 상태(영문)
    메뉴 열기 메뉴 닫기
    • Apple Developer
    • App Store Connect
    • 인증서, 식별자 및 프로파일(영문)
    • 피드백 지원
    메뉴 열기 메뉴 닫기
    • Apple Developer Program
    • Apple Developer Enterprise Program
    • App Store Small Business Program
    • MFi Program(영문)
    • News Partner Program(영문)
    • Video Partner Program(영문)
    • Security Bounty Program(영문)
    • Security Research Device Program(영문)
    메뉴 열기 메뉴 닫기
    • Apple과의 만남
    • Apple Developer Center
    • App Store 어워드(영문)
    • Apple 디자인 어워드
    • Apple Developer Academy(영문)
    • WWDC
    Apple Developer 앱 받기
    Copyright © 2025 Apple Inc. 모든 권리 보유.
    약관 개인정보 처리방침 계약 및 지침