作者|王浙劍(柘劍)
編輯|橙子君
出品|阿里巴巴新零售淘系技術
2020 年 6 月 22 日,蘋果召開了第一次線上的開發者大會 - WWDC20。這可謂是一次可以載入史冊的發佈會,宣佈了 ARM 架構 Mac 芯片、軟硬件的生態大統一、iOS 14 系統界面大改等一系列激動人心的消息。
當然,最讓我感興趣的就是讓 iOS 界面大改的 Widget 了。過去幾年,iOS 的桌面交互體驗可謂是一言難盡,Widget 的加入無疑是一次比較大的破局。在看發佈會的時候,我的腦海裡就浮現出一個問題:“這會是下一個互聯網公司競爭的流量入口嗎?”
先不拋結論,讓我們先看一下 WWDC20 介紹了哪些新東西。
什麼是 Widget?
Widget 不是一個小型的 App,它是一種新的桌面內容展現形式,主要是用於彌補主應用程序無法及時展示用戶所關心的數據。如下圖所示:

一個優秀的 Widget 需要有三個特點:簡單明瞭(Glanceable)、恰當展示(Relevant)、個性化定製(Personalized)
▐ 簡單明瞭(Glanceable)
Widget 不是一個小型的 App,這句話被反覆提起。一般用戶每天進入主屏幕的次數超過 90 次,但停留的總時長不過幾分鐘。通常來說用戶只會在主屏幕上停留片刻時間,就會跳轉到其他地方,所以並不需要任何複雜的交互設計來增強 Widget 的作用,也不需要複雜的樣式來豐富 Widget 的內容,簡單明瞭的內容才是 Widget 的關鍵。
和安卓的 Widget 不太一樣,蘋果設計的 Widget 並不支持任何交互行為,也不建議大家設計過於複雜的樣式來呈現內容,這也非常符合蘋果對於主屏幕的改進一直保持克制的特點。
▐ 恰當展示(Relevant)
蘋果期望 Widget 可以和正在執行或者考慮的事情緊密的結合。比如,早上起床,用戶最關心天氣怎麼樣,Widget 可以展示一下天氣情況;起床後,用戶就要了解一下一天的行程,Widget 可以展示一下 Reminders 中的內容;等到一天忙完了,準備睡覺的時候,可以用 Widget 打開音樂稍微放鬆一下。為此,蘋果系統提供了一個叫智能疊放(Smart Stacks)的功能,智能疊放是一個 Widgets 的集合。系統會根據每個人的習慣,藉助端智能的能力,自動的顯示準確的 Widget 在最頂部。

當然,蘋果也考慮到了一些特殊的場景,比如 Widget Gallery 瀏覽時,提供了 Snapshot 的能力給到開發者可以定製展示樣式,當加載內容的時候提供了 Placeholder UI API 而不是單調的 loading 加載框來避免過多的白屏的尷尬局面。這些設計的目的只有一個,蘋果期望 Widget 可以在任何特定的場景都可以展示合理的樣式。
▐ 個性化定製(Personalized)
Widget 需要一定的定製能力,比如當我添加一個天氣 Widget,我只需要關心杭州的天氣怎麼樣。為了實現這個能力,蘋果給 Widget 提供了 Configuration 的能力。顧名思義,就是可配置。一共有兩種配置類型:

dianStaticConfiguration,也就是用戶無需配置,展示的內容只和用戶信息有關係。
IntentConfiguration,支持用戶配置及用戶意圖的推測功能。
IntentConfiguration 的實現是基於 Intents.framework,開發過 SiriKit 和 Shortcuts 一定知道 Intents API 是用於瞭解用戶意圖的。其實就是一個智能的表單系統,開發者創建一個 SiriKit Intent Definition File 之後,只需要簡單的配置,Xcode 會自動幫你生成對應的代碼和類型。

當開發者編寫完配置之後,會藉助 Intents.framework 的能力,在運行的時候直接繪製出一個配置頁面(如下圖所示),開發者並不需要關心如果編寫這個頁面。

Widget 的刷新方式
Widget 的刷新方式是很特別的,相當的剋制。在展開講刷新方式之前,要講一個概念,叫 Timeline。顧名思義,就是時間線,下面的圖就是一條 Timeline。

當系統的 WidgetKit 調用 Reload Timeline API 之後,會要求 Widget Extension 的 Timeline Provider 提供一組 TimelineEntry 和 ReloadPolicy,用來後續刷新頁面。
這裡的概念比較多,我們一個一個來解釋。
首先,Widget 的刷新完全由 WidgetCenter控制。開發者無法通過任何 API 去主動刷新 Widget 的頁面,只能告知 WidgetCenter,Timeline 需要刷新了。
系統提供了兩種方式來驅動 Timeline 的 Reload。System Reloads 和 App-Driven Reloads。
System Reloads: 這個行為由系統主動發起,會調用一次 Reload Timeline 向 Widget 請求下一階段刷新的數據。系統除了會按時發起 System Reloads 之外,還會藉助端智能的能力,動態決策每個不同的 TimeLine 的 System Reloads 的頻次。例如被查看次數很大程度上直接決定了 System Reloads 的頻率。當然還有一些由於設備環境變化觸發的行為也會觸發 System Reloads,比如設備時間進行了變更。
App-Driven Reloads:指的是 App 請求 Widget 下一階段刷新的數據。這裡也要分兩種場景,應用在前臺運行和應用在後臺運行。當應用在前臺運行的時候,App 可以直接請求WidgetCenter的 API 來觸發 Reload Timeline;而當應用處於後臺時,後臺推送(Background Notification)也可以觸發 Reload Timeline。

注意,前面所提到的 Reload Timeline 並不是直接刷新 Widget,而是 WidgetCenter 重新向 Widget 請求下一階段的數據。而 Timeline Provider 就是提供這個數據的對象。
而 Timeline Provider 提供的數據有兩部分,一部分是 TimelineEntry,另外一部分是 ReloadPolicy。
TimelineEntry 是某個時間節點下 Widget 需要呈現的視圖信息和時間點。
而 ReloadPolicy 則是接下來這段時間 Timeline 的刷新策略,一共有三種:
atEnd: 是指 Timeline 執行到最後一個時間片的時候再刷新。
atAfter: 是指在某個時間以後有規律的刷新。
never:是指以後不需要刷新了。什麼時候需要重新刷新需要 App 重新告知 Widget。
當 Timeline Provider 提供完下一階段的數據之後,就會停止運行。系統也會根據 entry 的信息,到點對 Widget 的展示內容進行刷新。值得一提的是,WidgetKit 會把 Timelines 所定義的 Entries 對應的 Views 結構信息緩存到磁盤,然後在刷新的時候才通過 JIT 的方式來渲染。這使得系統可以在極低電量開銷下為眾多 Widgets 處理 Timelines 信息。
簡而言之,蘋果對 Widget 的刷新相當的剋制。開發者無法直接決定 Widget 刷新,只能提供刷新策略。具體的時間和節奏全部由系統來控制。蘋果這麼做,大概率是為了提高主屏幕的性能和減少電量開銷上的考慮。
Widget 和 SwiftUI
Widget 只能用 SwiftUI 來進行開發,確切的說,Widget 的本質是一個隨著時間線而更新的 SwiftUI 視圖。

當我最開始知道這個限制的時候,說實話是相當震驚的。眾所周知,SwiftUI 是一個去年才發佈的新技術,而且最開始的時候 SwiftUI 是相當不穩定的,以至於蘋果自己都是建議開發者暫時不要用到生產環境上,Widget 作為系統主屏幕的功能,強制使用這麼新的技術,會不會太激進了?
顯然是不會。蘋果要求 Widget 只能使用 SwiftUI 主要是基於幾點考慮:
1、SwiftUI 經過一年的發展,有了很大的提升,不僅可以使用 SwiftUI 來構建整個應用程序,而且在一些方面已經優於基於 UIKit 的開發方式了。具體的內容,大家可以看一下《詳解 WWDC 20 SwiftUI 的重大改變及核心優勢》
2、蘋果正在佈局跨平臺,大統一的策略。Widget 作為系統的核心功能,使用 SwiftUI 是唯一的選擇。SwiftUI 精美的 DSL 設計,使得開發者使用一套代碼在 iOS、iPadOS、macOS、watchOS 和 tvOS 等多個平臺展示不同的樣式可以輕鬆的實現。(Widget 只會在 iOS、iPadOS 以及 macOS 上展示)
3、使用了 SwiftUI 使得 Dynamic Type 和 Dark Mode 等問題適配起來成本很低。
4、只有使用 SwiftUI 才能達到很多對於 Widget 的限制。倘若可以使用 UIKit 開發者可能有無數種辦法繞過蘋果的限制。比如開發無法使用 UIViewRepresentable 來橋接 UIKit,只要使用任何 UIKit 的元素會直接 Crash。
5、將 Swift 語言和 SwiftUI 的重要程度提升了一大截。
Widget 的展示形式
▐ 一個 App 可以對應多個 Widget Extension
你可以使用WidgetBundle 來進行組裝。蘋果並沒有對 Widget Extension 有數量上的限制。所以為了避免大家開發過多的 Widget Extension 導致搜索起來麻煩,在 Widget Gallery 中只能看到一個條目。

▐ 一個 Widget Extension 一共只有三種尺寸
考慮到簡單明瞭的特點以及手機屏幕的空間有限的問題。蘋果只提供了三種樣式可以選擇,systemSmall(2 2 icon 區域)、systemMedium(24 icon 區域)、systemLarge(4 * 4 icon 區域)。

▐ 同一種 Widget 可以被多次添加到主屏幕中
而且對於每一個 Widget 來說,都有其對應的獨立 TimeLine,相互獨立,互不干擾。

▐ 開發者無法開發智能疊放(Smart Stacks)
開發者無法開發一個 Widget 的集合。智能疊放(Smart Stacks)是一個系統特有的能力,對於開發者來說,唯一可以做的就是主動提供相關性信息。前文提到了 Timeline 的數據又一組 TimelineEntry 組成,而每個 TimelineEntry 除了包含時間點和視圖信息以外,還可以包含一個 TimelineEntryRelevance 對象,用來表示這個 entry 的相關性。
▐ 不可交互,只可點擊
Widget 的 UI 是無狀態的,不支持滾動,也不支持像 Switch 一樣的互動元素。唯一開放的能力只有通過點擊和DeepLink 來喚起主 App。
蘋果提供了兩種 API 給到開發者,第一種是SwiftUI widgetURL API,代碼如下所示:

而 widgetURL 的可點擊區域如下:

對於 systemSmall 類型來說,只支持 widgetURL 的方式,但是 systemMedium 和 systemLarge 還可以使用 SwiftUI Link API,代碼如下所示:

而 Link 的可點擊區域如下:

同時,為了性能和耗電量的考慮。Widget 不能展示視頻和動態圖像。所以期待通過動效吸引用戶眼球的方式可以暫時息熄火了~
總結與展望
Widget 的出現,讓 iOS 系統的桌面有了破局,一定會有很多產品都期待藉助 Widget 來豐富自己產品的內容表達。
但是,Widget 設計的初衷是簡單明瞭的在恰當的時機展示一些帶有個性化定製的內容,為了不讓主屏幕的整體使用體驗變得複雜,Widget 從技術上就做的很剋制,限制了很多很多的能力。因此我認為Widget 不會成為下一個互聯網公司競爭的流量入口,它會成為 App 提高用戶體驗的利器。
從技術角度看,SwiftUI Only 這種看似“激進”的策略其實也是一種信號,其實也是在告訴大家蘋果對於 Swift 以及 SwiftUI 的重視程度。
雖然,從目前來看 Pure SwiftUI 的設計,可以做的事情真的很少,但是我也相信,蘋果會不斷優化 Pure SwiftUI 的能力。讓開發者可以以最低的開發成本,適配更多的平臺。
最後,也期待大家可以好好研究一下 Widget,結合自己的產品,給到用戶極致的用戶體驗。
What's new in SwiftUI
鏈接文字
Add configuration and intelligence to your widgets
Build SwiftUI views for widgets
Building Widgets Using WidgetKit and SwiftUI
手淘客戶端團隊
負責手淘移動端的基礎PaaS及平臺技術。涉及移動網關、網絡加速、長連通道、圖片體驗等基礎技術,以及海量消息推送、浮層搭投全域觸達等平臺型技術,並對移動端系統進行前沿探索,打造了全站IPv6、iOS用戶態網絡棧、Android最小核、自適應線程調度等高性能技術和架構。
在這裡,你會面臨超級App在性能、體驗、安全等方面的極致追求;在這裡,你會站在業務和數據視角針對目標進行充分了解和深入優化;在這裡,你會與業界各領域大牛並肩作戰、快速成長。
我們期待有技術、有理想的你加入,與我們共享積極、透明、開放的團隊氛圍,伴隨著各種乾貨滿滿的分享培訓以及業務和技術挑戰,我們將一同在技術領域不斷攻堅、推陳出新,共同駛向屬於我們的星辰大海。
職位:iOS 開發、Android 開發、C++開發、Java 服務端開發、前端開發、數據工程師、算法工程師
感興趣的同學可將簡歷發送到:[email protected],獲取優先內推資格!
關注「淘系技術」微信公眾號,一個有溫度有內容的技術社區~
