[WWDC21] Add intelligence to your widgets

Add intelligence to your widgets - WWDC 2021 - Videos - Apple Developer

Widget intelligence overview

intelligence.. 사전적 의미는 지능, 기밀, 정보입니다. 아직은 정확히 뭘 뜻하는지는 모르겠네요. 느낌에 정보같긴한데..

Smart Rotate

  • Automatically scrolls to a widget in a Smart Stack
  • Uses relevance signals from the widget
  • Uses behavioral patterns learned from app usage

위젯 써보시면, 여러 개 겹쳐서 위젯 스택을 만들 수 있는거 아시죠? 그 위젯 스택이 알아서 rotate 되도록 해주는게 Smart Rotate라고 하네요..

저는 그냥 시간 초 지나면 자동으로 rotate 해주는 거라고 생각했는데, 이게 글쎄 사용자의 동작 패턴을 학습한다고 하네요… 🤔 체감은 안드는데 그렇다고 합니다.

_2021-06-21__10 11 04

보시는 것과 같이, 시간 별로 알아서 뜬다고 하네요… 근데 애플에서 제공하는 앱 말고 다른 앱의 위젯들도 뜰런지는 모르겠어요..

일단 뭐 아침에는 날씨가 뜨고, 일정 관리에 따라 미팅에 잡혀져 있을 경우엔 미리 알림이 스택에 나타나는 형태입니다. 이런 것만 봤을 땐 User의 행동패턴을 학습했다기 보단 state의 변화가 있거나, 일정이 가까운 것 먼저 보여주는 식인거 같은데.. 이건 어디까지나 저의 생각입니다.

Widget Suggestions

  • Inserts a new widget into a Smart Stack
  • Uses relevance signals from the app
  • Uses behavioral patterns learned from app usage

_2021-06-21__10 15 15

iOS 15에선 Widget Suggestions라는게 생겼다고 합니다. 직역하자면, 사용자가 위젯을 발견하고 안전하고 예방적인 위젯에 대한 정보를 받을 수 있다? 라고 하는데, 아마도 위젯 스택에 자동으로 추가되는 거겠죠?

또한, Smart Rotate가 사용자 행동 패턴을 학습해서 Widget Suggestions이 가능하다고 합니다.

_2021-06-21__10 22 21

위젯에 대한 예시입니다. 보시는 바와 같이 스택에 2개의 위젯이 있습니다.

이 경우, 일정관리 위젯이 없어도 일정이 있는 경우, 위젯에 자동으로 일정관리 위젯이 추가된다고 합니다. 그리고 더 이상 관련이 없어진다면, 시스템이 위젯을 스택에서 제거합니다.

그래서 이 기능이 불러오는 효과는 사용자 입장에선 있는지도 모르는 위젯의 존재를 알게 되는 것에 도움을 줄 수 있다고 합니다… 좋은건가… 모르고 못쓰는 것보단 좋겠네요…🤷‍♂️

흠.. 너무 이상적인데, 위젯의 종류가 정말 많은데 진짜 이게 이상적으로 추가, 삭제가 가능할지는 직접 써봐야 알 것 같습니다.

Provide timely, glanceable information with obvious value to the user

이 새로운 Widget Suggestions의 핵심인 듯 합니다. 시기적절하게 분명한 가치를 가진 알기쉬운 정보를 user에게 제공하자!! 그렇게 하기위한 3가지 방법이 있다고 합니다.

  • INRelevantShortcut
    • 이 값은 donate해서 Widget Suggestions를 생성하는 시기를 알릴 수 있습니다.
  • TimelineEntryRelevance
    • relevance score를 제공해서 위젯이 스택에 있을 때 Smart Rotation을 활성화할 수 있습니다.
  • INInteraction
    • 구성 의도를 donate하면 시스템 사용자의 동작패턴을 학습하여 Smart Rotate, Smart Suggestions에 모두 쓰일 수 있습니다.

여기서의 donate라는 말은 기부한다고 해석한다기보단, 제공한다 정도로 생각해야 될 것 같습니다. 잘하는 영어는 아니라서 틀렸다면 지적해주세요..

INRelevantShortcut

  • Donated from an app to tell the system to insert a widget into a stack
  • Specifies time-based or behavioral relevance
  • Supports both static and intent-configured widgets

요약하자면, 스택에 추가 시키기 위한 값인가봐요.. 행동 패턴에 따라 삽입 시기를 결정하고, static widget이랑 intent-configured widget에서 둘다 사용 가능하다고 합니다.

Donating INRelevantShortcut

  • Set the array of relevant shortcuts in the default store
  • Update donated relevant shortcuts by donating a new array
  • Invalidate a relevant shortcut by ommitting it from a newly donated array
  • Donations with an intent can surface the intent on the Siri watch face

INRelevantShortcut을 array를 만들어서 관리하나봐요. 역시 이렇게 말로만 서술되어 있는 건 직관적이지 않네요.. 얼른 코드 나와라..

INRelevanceProvider

  • Use INDateRelevanceProvider for known time periods of relevance
  • Use no providers to tell the system to suggest based on behavioral pattens
  • Other relevance providers support only the Siri watch face

WidgetKit을 써보면 알 수 있는데 Widget에서 Provider를 사용해서 일정 주기마다 원하는 데이터를 가공해서 Widget에 넘겨줍니다. 뇌피셜인데 Provider를 상속받아서 추가적인 기능을 하는 protocol이겠죠.

예정된 이벤트나 속보 같은 관련 기간(period)의 시작 및 종료 날짜를 제공합니다.

여러 provider를 사용하여 둘 이상의 관련 기간을 지정할 수 있다고 합니다. (..? 역시 말로는 너무 어렵네요..)

시스템은 사용자가 위젯의 앱을 사용하고 있을 때, 사용하는 시기를 기준으로 Widget Suggestions을 한다고 합니다.

9분 14초 - Donate INRelevantShortcuts for Widget Suggestions

// Donate INRelevantShortcut for Widget Suggestions in app
// User has just made a purchase

var relevantShortcuts: [INRelevantShortcut] = []

let intent = ViewRecentPurchasesIntent()
intent.card = Card(identifier: card.identifier)
intent.category = .all

if let shortcut = INShortcut(intent: intent) {
    let relevantShortcut = INRelevantShortcut(shortcut: shortcut)
    relevantShortcut.shortcutRole = .information
    relevantShortcut.widgetKind = CardRecentPurchasesWidget

    let dateProvider = INDateRelevanceProvider(start: Date(), 
                                               end: Date(timeIntervalSinceNow: 1800))
    relevantShortcut.relevanceProviders = [dateProvider]

    relevantShortcuts.append(relevantShortcut)
}

INRelevantShortcutStore.default.setRelevantShortcuts(relevantShortcuts) { (error) in
    if let error = error {
        print("Failed to set relevant shortcuts. \(error))")
    } else {
        print("Relevant shortcuts set.")
    }
}

코드를 보니 말로만 듣던거보단 훨씬 나아진 것 같아요.

이 코드는 주석에도 나와있듯이 사용자가 구매를 했을 때 실행되는 로직인데, 여기서 유효 기간을 설정하고 INRelevantShortcutStore에 저장하는 방식인 것 같아요.

위에서 생각했던 INDateRelevanceProvider가 위젯 코드에서 쓰는 Provider를 상속받아서 만든거라고 생각했었는데, 이걸보니 조금 다른 것 같네요..

Widget Suggestion을 제어하는게 Widget 코드에서 제어하는 게 아니라 App 코드에서 제어하는게 좀 새롭게 느껴집니다.

만약에 저렇게 어떤 이벤트가 발생했을 때 shortcut array가 제공되는 거라면 충분히 사용성이 좋을 것 같은데요??

TimelineEntryRelevance

  • Indication on a widget’s timeline entry about its worthiness of rotation
  • Relative to all other timeline entries provided by the widget

_2021-06-22__10 29 08

꼭 이걸로 생성해!! 이런느낌이 아니라 옵셔널이네요. relevance struct는 보시는 바와 같이 score랑 duration이라는 프로퍼티를 가지고 있습니다. score는 다른 entry와 비교되기 위한 값입니다.

다른 위젯의 score보다 더 높은 값이 rotate 됩니다.

duration은 얼마나 이 score의 값이 유효한지에 대한 기간을 나타냅니다. duration이 지나면, 시스템은 score를 0으로 설정합니다.

12분 35초 - Adopting TimelineEntryRelevance for Smart Rotate

// Appending TimelineEntryRelevance to a TimelineEntry in widget extension for Smart Rotate

struct CardRecentPurchasesEntry: TimelineEntry {
    let date: Date
    let relevance: TimelineEntryRelevance?
    let card: IntentCard?
    let category: PurchaseCategory
}

let relevance = TimelineEntryRelevance(score: 16.29, duration: 1800)
let entry = CardRecentPurchasesEntry(date: Date(), relevance: relevance, card: card,
                                     category: category)

card나 category는 데이터 타입을 새로 추가시킨 겁니다.

Entry 자체가 이제 위젯에 제공되는 데이터 타입이라고 보시면 될 듯 한데, relevance를 옵셔널하게 추가시켜서 rotate될 항목을 score 기준으로 entry끼리 비교해서 선정하는 것 같아요..

살짝 이해가 안가는게 entry끼리 비교하면… 음.. 뭐가 달라지나 싶네요.. 같은 위젯에서 보여지는 entry에 대한 중요도?를 설정해서 score가 높은 entry를 우선적으로 보여줍니다.

_2021-06-22__10 51 29

이게 예시인데요.. 같은 위젯에서 구매 금액이 큰 내역을 우선적으로 보여주고 있습니다. 기준은 개발자가 설정하기 나름이라서 여러 상황에서 적용해볼 수 있을 것 같습니다.

만약에 score 값이 같다면 최근 entry 값을 보여준다고 합니다.

InInteraction

  • App donation when the user views information
  • Data point for the system’s behavioral model
  • Predicted by the system using user’s patterns
  • Enables suggestions for a configured widget

_2021-06-22__11 03 56

어.. 영상 화질이 좀 안좋아서 살짝 흐릿하게 나오네요… 아무튼 앱을 사용할 때 InInteraction이 쌓이고, 이게 즉, 행동 패턴을 분석하는 모델에 제공되는 형태입니다.

17분 01초 - Donate INIntents through INInteraction for Widget Suggestions and Smart Rotations

// Donate INIntent in a card's purchases list in the app

.onAppear {
    let intent = ViewRecentPurchasesIntent()
    intent.card = Card(identifier: card.id.uuidString, displayString: card.name)
    intent.category = .all

    let interaction = INInteraction(intent: intent, response: nil)
    interaction.donate { error in
        if let error = error {
            print(error.localizedDescription)
        }
    }
}

생성한 Intent에 값을 넣어주고 Intent로 INIntercation을 생성한 다음 donate해주는 모습입니다. Intent를 쓰는 위젯을 아직 써보지 않아서 이 코드가 정확히 어디에서 쓰여지는지는 잘 모르겠습니다… 🥲

학습이 좀 더 필요한 부분.. 여기서 말하기로는 onAppear 즉, 앱이 보여지는 시점부터 donate 된다고 하네요..

근데 앱이 보이는 동안 계속 donate하는지는 잘 모르겠네요.. 아마도 처음 Appear될 때만 되는거겠죠?

onAppear 말고도 lock screen, Spotlight, siri suggestion widget에서도 donate될 수 있다고 하네요.