• Overview

    • 이전에는 Siri에 추가 버튼을 누르거나, 단축어 앱을 통해서 intent를 추가해야 했다.
    • 이제는 App Shortcuts를 사용해서 유저가 설정하지 않아도 단축어가 자동으로 나오게 바뀌었다.
      • 앱이 설치되는 즉시 사용 가능
      • 사용자 설정 단축어와 동일하게 단축어 앱, Spotlight, Siri에서 뜬다.
  • Implement App Shortcuts

    • App Shortcut은 App Intent 프레임워크로 구현한다.

      • Swift-Only
    • 별도의 메타데이터 파일이 아닌, Swift 코드로 정의한다.

      • 코드 생성 단계가 제거된다.
      • 소스 편집기와 메타데이터 편집기를 왔다갔다 하지 않아도 된다.
      • 코드 리뷰와 머지 충돌 해결이 쉽다.
    • AppShortCutsProvider를 통해서 Intent를 단축어로 변환하는데 필요한 구문과 데이터를 제공한다.

      • 사용자가 설정하는게 아니기 때문에, 트리거 구문에 앱 이름을 포함해야 한다.
    • Intent 구현

      • title과 perform 메소드는 필수로 제공해야 한다.
        • 결과 값 타입에 따라 다양한 interaction을 제공할 수 있다.
        • locaization이 필요한 경우 자동으로 적용될 수 있도록 해야 한다.
      // StartMeditationIntent creates a meditation session.
      
      import AppIntents
      
      struct StartMeditationIntent: AppIntent {
          static let title: LocalizedStringResource = "Start Meditation Session"
      
          func perform() async throws -> some IntentResult & ProvidesDialog {
              await MeditationService.startDefaultSession()
              return .result(dialog: "Okay, starting a meditation session.")
          }
      }
      
    • shortcut으로 구현하기

      • App이 가질 수 있는 App ShortCut은 최대 10개
      • .applicationName 토큰을 쓰면 기본 앱 이름 뿐 아니라 설정해놓은 앱 이름 동의어도 제대로 인식할 수 있다.
      • 구문도 localization이 필요하고, 구문을 여러 개 제공할 수도 있다.
      // An AppShortcut turns an Intent into a full fledged shortcut
      // AppShortcuts are returned from a struct that implements the AppShortcuts
      // protocol
      
      import AppIntents
      
      struct MeditationShortcuts: AppShortcutsProvider {
          static var appShortcuts: [AppShortcut] {
              AppShortcut(
                  intent: StartMeditationIntent(),
                  phrases: [
                      "Start a \\(.applicationName)",
                      "Begin \\(.applicationName)",
                      "Meditate with \\(.applicationName)",
                      "Start a session with \\(.applicationName)"
                  ]
              )
          }
      }
      
    • 이렇게 구현된 App Shortcut은 Siri와 Spotlight등에서 쉽게 찾을 수 있다.

      • 이때 Spotlight에서는 첫번째 App Shortcut만 뜬다.
      • Intent가 앱 실행을 트리거 하는 경우에는 Spotlight에 표시되지 않는다.
    • Shortcut실행 결과로 커스텀 뷰 띄우기

      • SwiftUI 뷰를 사용한다

      • 인터렉션이나 애니매이션을 포함할 수 없다.

        • 위젯과 유사한 제한
      • 총 3구간에서 커스텀 뷰를 적용할 수 있다.

        • value confirmation
        • intent confirmation
        • after run intent
      • 예시(after run intent)

        // Custom views give your intent more personality
        // and can convey more information
        
        func perform() async throws -> some ProvidesDialog & ShowsSnippetView {
            await MeditationService.startDefaultSession()
        
            return .result(
                dialog: "Okay, starting a meditation session.",
                view: MeditationSnippetView()
            )
        }
        
  • Add parameters

    • entity 구현

      • AppIntent 프레임워크에 커스텀 타입을 알려주고, 어떻게 표현되어야 하는지를 지정하는 방법
      • ID, typeDisplayName, displayRepresentation, 기본 query를 제공해야 한다.
      // An entity is a type that can be used as a parameter
      // for an AppIntent.
      
      import AppIntents
      
      struct MeditationSession: AppEntity {
          let id: UUID
          let name: LocalizedStringResource
      
          static var typeDisplayName: LocalizedStringResource = "Meditation Session"
          var displayRepresentation: AppIntents.DisplayRepresentation {
              DisplayRepresentation(title: name)
          }
      
          static var defaultQuery = MeditationSessionQuery()
      }
      
      // Queries allow the App Intents framework to
      // look up your entities by their identifier
      
      struct MeditationSessionQuery: EntityQuery {
          func entities(for identifiers: [UUID]) async throws -> [MeditationSession] {
              return identifiers.compactMap { SessionManager.session(for: $0) }
          }
      }
      
    • 정의한 Entity를 Intent의 매개 변수로 쓰기

      // Adding a parameter to an intent allows you to prompt the user
      // to provide a value for the parameter
      
      struct StartMeditationIntent: AppIntent {
      
          @Parameter(title: "Session Type")
          var sessionType: SessionType?
      
          // ...
      
      }
      
    • 매개변수를 사용자에게 요구하기

      • 어떤 것을 원하는지 후속으로 질문할 수 있게 한다.

        • Siri에서는 역질문을 한다.
        • spotlight와 단축어 앱에서는 프롬프트를 제공할 것이다.
          • Disambiguation: 고정 목록에서 선택.

            스크린샷 2022-08-11 오후 4.12.51.png

          • Value Prompt: 정수, 문자열 등의 열린 값을 요청하는 데 좋다.

            스크린샷 2022-08-11 오후 4.14.18.png

          • Confimation: 실제 실행 전 한번 더 확인 시키는 용도

            스크린샷 2022-08-11 오후 4.17.00.png

      • 프롬프트가 너무 많으면 속도가 느려지고 유저가 답답할 수 있으니 적절하게 사용해야 한다.

      • 코드로 구현(Disambiguation)

        • dialog를 띄워서 사용자의 요청을 제대로 이해했다는 것을 알려주는 게 좋다.
        // Prompting for values can be done by calling methods
        // on the property's wrapper type.
        
        func perform() async throws -> some ProvidesDialog {
            let sessionToRun = self.session ?? try await $session.requestDisambiguation(
                   among: SessionManager.allSessions,
                   dialog: IntentDialog("What session would you like?")
               )
            }
            await MeditationService.start(session: sessionToRun)
            return .result(
               dialog: "Okay, starting a \\(sessionToRun.name) meditation session."
            )
        }
        
    • 매번 묻는 건 귀찮으니 몇가지 매개변수를 기본으로 제공해주면 좋겠다.

      • 이 때 매개변수는 열린 값이 될 수 없다.
      • 앱이 실행되는 동안에 미리 지정된다.
      • 구현하기 위해서는
        • Query에서 suggestedResults() 메소드를 구현해야 한다.

          • 옵셔널이지만, 이게 없으면, Paramterized된 단축어를 이용할 수 없다.
          // Queries can provide suggested values for your Entity
          // that serve as parameters for App Shortcuts
          
          struct MeditationSessionQuery: EntityQuery {
              func entities(for identifiers: [UUID]) async throws -> [MeditationSession] {
                  return identifiers.compactMap { SessionManager.session(for: $0) }
              }
          
              func suggestedEntities() async throws -> [MeditationSession] {
                  return SessionManager.allSessions
              }
          }
          
        • 사용 가능한 목록이 변경되면, AppIntent 프레임워크에 알려야 한다

          // Your app must notify App Intents when your values change
          // This is typically best done in your app’s model layer
          
          class SessionModel {
              @Published
              var sessions: [MeditationSession] = []
              private var cancellable: AnyCancellable?
          
              init() {
                  self.cancellable = $sessions.sink { _ in
                      MeditationShortcuts.updateAppShortcutParameters()
                  }
              }
          
              // ...
          
          }
          
        • Parameter를 받을 수 있는 새로운 구문을 추가해야 한다.

          // Phrases can also contain a single parameter reference
          
          import AppIntents
          
          struct MeditationShortcuts: AppShortcutsProvider {
              static var appShortcuts: [AppShortcut] {
                  AppShortcut(
                      intent: StartMeditationIntent(),
                      phrases: [
                          "Start a \\(.applicationName)",
                          "Begin \\(.applicationName)",
                          "Meditate with \\(.applicationName)",
                          "Start a \\(\\.$session) session with \\(.applicationName)",
                          "Begin a \\(\\.$session) session with \\(.applicationName)",
                          "Meditate on \\(\\.$session) with \\(.applicationName)"
                      ]
                  )
              }
          }
          
  • Add discoverability

    • 멋진 구문을 골라라.
      • 짧고 기억에 남을수록 좋다.
      • 앱 이름을 명사나 동사로 그대로 사용하는 경우도 고려하라.
      • 앱 이름 동의어를 활용해보라.
        • iOS 11부터 지원한다.
    • Siri Tip
      • Add to Siri 를 대체하는 기능
        • 이미 추가 되어 있으니 따로 추가하는 기능이 필요 없어졌다.
      • UIKit과 SwiftUI모두에서 사용 가능
      • 맥락에 맞게 배치하는 게 좋다.
      • 닫기를 지원하고, 이 때 커스텀 클로저를 실행할 수 있다.
      • 단축어 앱을 실행할 수 있는 버튼도 추가된다.(ShortcutsLink)
    • App Shortcuts는 앱이 처음 실행되기 전부터 사용할 수 있다.
    • Parameterized Phrase는 앱이 실행되고, AppIntent 프레임워크에 노티가 가야 수집된다.
      • 그래서 매개변수가 없는 구문이 한개도 없다면, 앱 실행전까지는 아무것도 보이지 않을 거다.
    • “여기서 무엇을 할 수 있나요?(What can I do here?)” 지원 추가
      • 자동으로 App Shortcuts 구문을 수집하고 제시해줄거다.
      • “What can I do with \(.appName)” 같은 구문도 가능하다.
      • 이를 위해 추가구현할 것은 없다.
    • App Shortcut의 구문 표시 순서는 코드에 나온 순서대로