Add configuration and intelligence to your widgets

영상 링크 → 링크

Meet WidgetKit 정리 → 링크

함께 보면 좋은 문서 → Making a Configurable Widget

Intro

이 영상에선 위젯을 Configurable하게 만드는 방법과 Configurable한 위젯이 어떻게 시스템을 더욱 영리한 방식으로 작동하는데 도움을 줄 수 있는지에 대해 알아볼 것이다.

다음은 영상에서 살펴볼 예제 앱이다.

카드별 결제 내역을 보여주는 앱으로 최근 결제 내역, 납부일 및 납부해야 할 총 금액을 나타내는 두 가지 위젯을 제공한다.

이번 영상에서 다룰 주제의 목차는 다음과 같다.

  1. The Basics
  2. Types of data entry
  3. Configuration experience
  4. System intelligence

제일 먼저 위젯에 설정을 추가하는 방법을 살펴볼 것이다. 그 다음으로 사용자에게 입력하도록 요청할 수 있는 정보의 유형에 대해 설명하고, 앱의 데이터로 인터페이스를 채울 수 있는 방법을 살펴볼 것 이다.

이후엔 어떻게 위젯의 제목, 설명을 지정하고 배경색을 설정할 수 있는지에 대해 살펴볼 것이다. 마지막으론 스마트 스택에서 시스템이 여러분이 개발한 위젯이 노출되어야 할 시간을 보다 더 정확하게 예측할 수 있도록 설정을 추가하는 방법에 대해 살펴볼 예정이다.

The Basics

위젯이 설정 가능하다면 위젯 뒷면에 사용자에게 입력을 요청할 수 있는 옵션들을 지정할 수 있고, 시스템은 이를 사용자에게 보여줄 것이다. 이런 옵션, 즉 사용자가 정보를 입력할 수 있는 요소들을 파라미터라고 한다.

예제 앱에선 Card 파리미터와 Category 파라미터를 지정하여 사용자가 원하는 카드의 원하는 카테고리의 최근 결제 내역을 위젯에 보여줄 수 있다.

이런 파라미터를 정의하기 위해 SiriShortcut에서 사용하고 있는 Intent를 사용한다. Intent에 추가한 파라미터는 위젯에 하나의 행으로 사용자에게 보여진다.

Xcode에서 Intent Definition 파일을 통해 Intent를 선언할 수 있다. 이 파일에는 Intent와 더불어 그들의 파라미터 등도 포함된다. 이 파일을 통해 시스템은 Intent의 정보를 읽을 수 있다.

Intent를 정의하면 Xcode는 정의한 Intent와 이에 포함되는 파라미터를 프로퍼티로 갖는 클래스를 생성한다.

이 클래스의 인스턴스는 런타임 중 위젯 익스텐션에 전달되며 이를 통해 위젯은 사용자가 무엇을 설정했고 사용자에게 무엇을 보여주어야 하는지를 알 수 있다.

Types of data entry

위젯 설정에선 다양한 데이터 타입을 지원한다.

String 타입의 파라미터를 지정하면 설정 화면은 텍스트 필드를 보여줄 것이고, Boolean 타입의 파라미터는 스위치를 보여줄 것이다.

그리고 숫자는 Int 타입의 파라미터는 스테퍼를, Decimal 타입의 파라미터는 슬라이더를 지원한다.

또한 설정 화면은 연락처 선택과 Location 타입의 파라미터를 위한 위치 선택 UI도 제공한다.

설정 화면은 열거형도 지원하는데, 정적 열거형과 동적 열거형을 통해 이를 활용할 수 있다. 여기서 동적 열거형이란 사용자마다 차이가 있을 수 있는 앱의 사용자 데이터를 의미한다. (사용자가 추가한 카드(Card)는 앱을 사용하는 사용자마다 달라질 수 있다.) 동적 옵션은 아래에서 더 자세히 살펴보도록 하자.

설정 화면은 위에서 언급한 것들 말고도 다수의 타입을 각각의 고유한 UI로 지원한다.

  • Date components
  • Duration
  • URL
  • Measurement
  • Currency amount
  • Payment method

또한 파라미터는 다수의 값을 가질 수 있으며, iOS 14에서 Intent는 고정된 크기의 배열을 지원한다. 이를 통해 정의된 갯수만큼의 아이템만 배열에 들어갈 수 있게 지정할 수 있다.

그리고 위젯의 크기에 따라 이 갯수를 별도로 지정해줄 수 있다.

영상의 06:43 ~ 09:39 구간에선 기본적으로 Intent Definition 파일에 Intent와 그의 파라미터를 입력하는 방법을 보여주고 있다.

Dynamic Options

많은 경우, 위에서 언급한 사용자가 등록한 카드와 같이 위젯 설정에서 보여주고 싶은 데이터는 사용자마다 달라질 수 있다. 그리고 이런 유형의 데이터는 Intent Definition 파일에서 지정해줄 수 없다.

대신 Intent Definition 파일에서 Dynamic Options 체크박스를 체크하면 동적 옵션을 활성화할 수 있다.

이렇게 동적 옵션을 활성화하면 위젯 설정 화면에서 원하는 값을 직접 입력할 수 있는 대신 사용자가 선택할 수 있는 값을 검색하기 위해 앱에 요청해야 한다는 사실을 시스템에 알리게 된다. 이렇게 동적 옵션을 활성화하면 두 가지가 발생한다.

첫째, 위젯의 뒷면(설정 화면)의 동적 옵션이 활성화된 파라미터의 UI가 옵션의 목록을 보여주는 모달을 여는 버튼 모영으로 변경된다.

둘째, 여러분의 앱에서 구현해야 할 두 가지 메서드가 생성되는데, 하나는 선택 가능한 옵션을 제공하는 메서드, 나머지 하나는 기본 값을 제공하는 메서드이다.

이 메서드들은 Xcode가 생성해주는 Intent Handler 프로토콜의 일부이다. 여러분은 앱이나 Intent 익스텐션에서 이 프로토콜을 채택하는 클래스를 생성해야 한다. 이 클래스는 사용자가 위젯을 설정할 때 선택 가능한 옵션을 제공하기 위해 시스템에 의해 사용된다.

예제 앱에선 아래와 같이 선택 가능한 카드의 목록과 기본 선택 카드를 위의 메서드 구현을 통해 제공하고 있다.

선택 가능한 옵션을 제공하는 메서드를 사용하면 단순 리스트 형태로 옵션을 제공하거나 그룹화된 리스트 형태로 옵션을 제공할 수 있다.

아래는 그룹화된 리스트 형태로 옵션을 제공한 모습이다.

Search

기본적으로 상단의 검색 바는 여러분이 제공한 옵션을 필터링하는 역할을 한다. 몇몇의 경우 옵션이 리스트에 한번에 보이지 않을 정도로 많을 때 검색을 활용할 수 있다.

이를 위해 Intent handler provides search results as the user types 체크박스를 활성화해야 한다. 또한 Prompt Label(검색에 도움을 줄 수 있는?)을 제공할 수도 있다.

Intent handler provides search results as the user types 체크박스를 활성화하면 선택 가능한 옵션을 제공하는 메서드는 searchTerm 파라미터를 추가로 받게 된다.

처음 사용자가 리스트를 마주하면 이 메서드가 호출될 때 searchTermnil로 전달된다. 그리고 이후에 사용자가 타이핑을 하기 시작하면 이 메서드는 입력된 문자열과 함께 다시 호출될 것이다.

영상의 12:23 ~ 15:11 구간에선 동적 옵션을 구현하는 방법을 직접 시현을 통해 소개하고 있다.

Configuration experience

이번엔 위젯의 설정 화면의 외형을 커스터마이징 해보자.

먼저 설정 화면의 제목과 설명을 configurationDisplayName 변경자와 description 변경자를 이용해 각각 지정해줄 수 있다.

background와 accent 색상 역시 별도로 지정해줄 수 있다.

이를 위해선 먼저 위젯 익스텐션의 에셋 카탈로그에 색상의 이름과 함께 색상을 추가해야 한다.

그리고 이렇게 추가된 색상과 색상의 이름을 위젯 익스텐션의 build settings에서 각각 Global Accent Color NameWidget Background Name에 지정해주면 된다.

그리고 설정 화면에서 특정 파라미터의 값을 기반으로 다른 파라미터를 보여주거나 숨기고 싶을 수 있는 경우가 생길 수 있다.

위의 예제를 통해 두 파라미터의 관계를 살펴보면 Mirror Calendar App을 끄면, Calendar 파라미터가 나타나 어떤 캘린더가 보여야 하는지 수동으로 선택할 수 있어야 한다.

이를 위해 Intent Definition 파일

에서 Calendar 파라미터를 선택 후 Mirror Calendar App 파라미터를 Parent Parameter로 지정해주면 된다.

그리고 Mirror Calendar Appfalse일 때만 Calendar가 보여야 하기 때문에 Show If Parenthas exact value로, ValueFalse로 지정해야 한다.

System intelligence

위젯은 단순히 하나만 배치할 수도 있지만 스택안에서 여러 위젯을 관리할 수 있다. 그리고 이 스택을 통해 적절한 타이밍에 특정 위젯을 최상단으로 올려 사용자에게 매번 적절한 위젯을 보여줄 수 있다. 이렇게 시스템이 적절한 타이밍과 위젯을 고르는데 영향을 미치는 것이 바로 intelligence, 즉 지능이다.

이번 챕터에선 다음의 두 가지 물음에 대해 살펴볼 예정이다.

  • How do stacks behave intelligently?
  • How can i appear at just the right time?

첫 스택 지능을 구현하는 기본 설계 원리를 살펴보고 여러분이 개발한 앱을 이번에 새롭게 등장한 홈 스크린 경험(스택)의 일부에 포함시키기 위해선 어떻게 새 API들을 구현해야 하는지를 살펴볼 것이다.

How do stacks behave intelligently?

어떤 것이 좋은 스마트 스택을 만들수 있을까?

스택은 사용자에게 분명한 가치를 적절한 타이밍에 한눈에 볼 수 있는 정보를 제공해야 한다.

예를 들어 뇌우가 오고 있다는 사실을 앱이 안다면 사용자에게 단순히 온도를 주기적으로 갱신해주는 것보다 뇌우가 오고 있다는 사실을 알려주는 것이 더 직관적이다.

시스템은 위젯을 올릴 때 두 가지 요인에 기반한다.

첫 번째는 사용자 행동 기반(behavior-based)이다. 시스템은 사용자가 특정 시간에 주로 찾는 정보를 제공하는 위젯을 해당 시간에 위로 올린다. 날씨 앱을 자주 들여다보는 사용자에겐 날씨 앱을 위로 올려 사용자가 이를 통해 날씨 정보를 빠르게 찾을 수 있도록 한다.

두 번째는 여러분의 앱이 제공하는 관련성 정보(relevant information)이다. 예를 들어 날씨 앱의 경우 뇌우가 왔을 때 위젯이 이를 시스템에 매우 관련성이 높은 정보가 있음을 알릴 수 있으며, 스택은 이를 통해 위젯을 위로 올릴지를 결정한다.

How can i appear at just the right time?

시스템이 위젯을 올리는데 필요한 정보를 제공하는데 사용할 수 있는 API들에 대해 살펴보도록 하자.

Behavior-based

먼저 행동 기반 요인에 영향을 줄 수 있는 API에 대해 살펴보자.

iOS 12에서 등장한 Shortcuts와 사용자 정의 Intent donations를 통해 여러분의 앱에서 사용자가 무엇을 했는지 시스템에 알릴 수 있게 되었다. 그리고 시스템은 이 정보를 바탕으로 Spotlight에서 그 다음 행동을 예측 및 추천할 수 있다. iOS 14에서 시스템은 동일한 정보를 바탕으로 언제 여러분의 위젯을 위로 올릴 것인지를 결정한다.

예제 앱에서 사용자가 앱에서 특정 카드를 확인했을 때 이 사실을 시스템에 알릴 건데, 이를 Intent donation을 통해 시스템에 알릴 것이다. 내부적으로 이게 어떻게 동작하는지 알아보기 전에 먼저 이를 위한 세팅을 하는 법에 대해 살펴보자.

먼저 기존의 Intent Definition 파일에서 Intent is elligible for Siri Suggestions 항목을 활성화한다.

해당 항목이 활성화되면 아래 Suggestions 필드가 추가된다. 우린 사용자가 앱에서 특정 카드를 확인했을 때 해당 카드의 내역을 보여주도록 구성된 위젯을 상단으로 올려야 하기 때문에 Supported Combinationscard 파라미터를 추가해야 한다.

이렇게 설정을 마쳤고, 앱에서 사용자가 카드를 확인했을 때 이 사실을 시스템에 알릴 수 있도록(donate) 코드를 작성해주어야 한다.

예제 앱에서 사용되는 ViewRecentPurchasesIntent를 생성하고 여기에 현재 사용자가 확인한 Card 인스턴스를 담아 이를 donate하는 코드이다. 위에서 Supported CombinationsCard 파라미터만 추가했기 때문에 Category 파라미터를 추가해도 시스템은 Card만 고려한다.

그럼 시스템이 이를 통해 어떻게 동작하는지를 살펴보도록 하자.

사용자가 식료품 가게에선 정오에 AcmeCard로 주로 결제하고 저녁은 주로 SoupPay로 결제한다고 했을 때 donate된 정보를 바탕으로 시스템은 각각 AcmeCard는 정오에, SoupPay는 저녁에 사용된다는 사실을 알 수 있기 때문에 카테고리에 상관없이 AcmeCard로 구성된 위젯을 정오에 위로 올릴 것이다.

만약에 Supported CombinationsCategory를 추가하고 IntentCategory를 함께 donate한다면 시스템은 사용자가 명확하게 AcmeCard와 Groceries 카테고리로 설정한 위젯만 위로 올릴 것이다. 즉 Supported Combinations은 시스템과 소통하는 방법이다.

위의 과정을 요약하자면 아래와 같다.

Relevant Information

이번에 살펴볼 API는 앱에서 중요하고 관련된 정보가 생겼을 때 시스템이 해당 위젯을 위로 올리는데 사용되는 API다.

먼저 Timeline을 간략하게 살펴보면, WidgetKit을 사용하여 다양한 시점에서 위젯의 모양을 결정하는 Timeline을 제공할 수 있다.

또한 최근 구매 내역과 같이 구매가 발생했을 때 각각에 해당하는 entry를 제공함으로써 위젯이 이를 사용해 실시간으로 새 정보에 반응할 수 있다.

예제 앱에서 사용자가 $50 이상의 구매가 발생하면 알람을 받기 원한다고 가정했을 때, 어떻게하면 시스템에게 예제 앱 위젯이 관련성 높은 정보($50 이상의 구매가 발생)를 갖게 되었다고 알릴 수 있을까?

TimelineEntryRelevance 객체를 TimelineEntry와 함께 제공함으로써 이런 정보를 시스템에 전달할 수 있다.

TimelineEntry는 세 가지 요소로 이루어져 있다. Date는 이 entry가 언제 랜더링되어야 하는지를 나타내고, View는 랜더링되어야 할 뷰를 의미한다. 그리고 이 entry의 연관성을 나타내는 RelevanceTimelineEntryRelevance 객체로 scoreduration 프로퍼티를 갖는다.

score를 먼저 살펴보자면, score는 과거에 제공된 모든 entry들과 비교했을 때 이 entry가 얼마나 연관되어 있는지를 나타낸다. 시스템은 다른 entry들과 관련하여 score만을 고려하기 때문에 범위와 스케일은 정의하기에 달려 있다. 예외적으로 0과 그 이하의 score는 시스템에게 현재 위젯이 관련 정보를 갖고 있지 않고, 위로 올라오지 말아야 한다고 알리는데 사용된다.

예제 앱으로 돌아와서 우리가 의도한대로 동작하기 위해 $50 이상의 구매가 발생했을 때는 1, 최근 구매 내역이 없으면 0 그리고 이외의 구매 내역은 0.1로 score로 지정해보자. 이렇게 score를 지정하면 사소한 구매 내역에 대해선 위젯이 올라올 수 있는 기회가 적지만 대신 굵직한 구매 내역은 확실한 우선순위를 갖을 수 있다.

다른 위젯들이 제공하는 score는 상관없음을 기억하자. score는 오로지 여러분이 제공한 score랑만 비교된다.

이번엔 결제 금액을 score로 사용해보자. 이를 통해 특정 금액을 넘는지 아닌지가 아닌 결제 금액에 따른 우선순위가 정해진다.

score를 살펴보았고 이제 duration을 살펴보자.

duration은 잘 정의된 일정 시간동안 관련성 점수(score)를 고정할 때 사용된다. 그렇지 않으면 duration을 0으로 두면 된다. 이는 관련성 점수가 다음 TimelineEntryRelevance가 수신될 때 까지만 지속된다는 것을 의미한다.

다은 duration을 활용한 예제이다.

농구 게임의 진행 상태를 알려주는 위젯으로 게임 시작 전에는 score를 0으로 지정하고 게임이 시작하면 score를 1로 지정하고 이를 게임이 진행되는 동안 고정하기 위해 duration을 3시간으로 지정하였다.

그리고 게임이 진행되는동안 관련성 점수에 영향을 미치지 않도록 TimelineEntryRelevancenil로 두고 TimelineEntry를 갱신할 수 있다.

스택의 지능(intelligence)에 정리해보자.

우린 스마트 스택을 이용해 특정 위젯을 스택의 최상단으로 올릴 수 있는 기회를 갖는다. 이를 가능하게 하는 방법은 다음 두 가지다.

  • Donate INIntents that match your configuration intent (User behavior-based)
  • Provide TimelineEntryRelevance for important information (Relevance information)