在 SwiftUI 中構建服務端驅動的 UI 元件
- 原文地址:Build a Server-Driven UI Using UI Components in SwiftUI
- 原文作者:Anup Ammanavar
- 譯文出自:掘金翻譯計劃
- 本文永久連結:github.com/xitu/gold-m…
- 譯者:chaingangway
- 校對者:lhd951220
在 SwiftUI 中構建服務端驅動的 UI 元件
在不提交給 Apple 稽核的情況下即時修改應用
本文將討論使用可重用 UIComponents 元件來實現服務端驅動的 UI,以及如何建立通用垂直列表檢視。最後將簡要討論如何使用 UI 元件實現不同的需求。
什麼是服務端驅動的 UI ?
- 它是一種架構,其中約定應用程式中 UI 檢視在螢幕上的渲染是由伺服器決定的。
- 應用程式和伺服器之間存在協議。該協議的基礎是讓伺服器可以控制應用程式的 UI。
協議是什麼?—— 伺服器定義的元件列表。對於伺服器上定義的每個元件,我們在應用程式(UIComponent)中都有一個相應的 UI 實現。比如像 Hotstar 這樣的娛樂應用,其協議定義如下。左邊是伺服器中的元件,右邊是相應的 UI 元件。
執行 —— 螢幕上沒有像 storyboard 一樣預定義的佈局。取而代之的是一個普通的列表檢視,它會根據伺服器的響應,在垂直方向上渲染多個不同的檢視。為了實現這一點,我們必須建立獨立並且在整個應用中可重用的檢視。我們將這些可重用的檢視稱為 UIComponent
協議 —— 對於每個服務端的元件,我們有與之對應的 UIComponent。
SwiftUI
SwiftUI 是一個用宣告式程式設計來設計屏幕布局的 UI 框架。
struct NotificationView: View {
let notificationMessage: String
var body: some View {
Text(notificationMessage)
}
}
複製程式碼
在 SwiftUI 中實現服務端驅動的 UI
它分為三個步驟。
- 定義獨立的 UIComponents。
- 根據 API 響應結果構建 UIComponents。
- 在螢幕上渲染 UIComponents。
1. 定義獨立的 UIComponents
輸入:首先,要使 UIComponent 能夠渲染,應為其提供資料。
輸出:UIComponent 中定義的 UI。當螢幕渲染時,它根據提供的資料(輸入)進行渲染。
UIComponent 實現
protocol UIComponent {
var uniqueId: String { get }
func render() -> AnyView
}
複製程式碼
- 所有 UI 檢視都必須遵守 UIComponent 協議。
- 由於元件是在通用垂直列表中渲染的,所以每個 UIComponent 必須有一個獨立的標識。
uniqueId
屬性用於實現標識的功能。 - 我們在
render()
方法中定義元件的 UI。呼叫這個方法時會在螢幕上渲染元件。現在我們來看一下NotificationComponent
的實現。
struct NotificationComponent: UIComponent {
var uniqueId: String
// The data required for rendering is passed as a dependency
let uiModel: NotificationUIModel
// Defines the View for the Component
func render() -> AnyView {
NotificationView(uiModel: uiModel).toAny()
}
}
// Contains the properties required for rendering the Notification View
struct NotificationUIModel {
let header: String
let message: String
let actionText: String
}
// Notification view takes the NotificationUIModel as a dependency
struct NotificationView: View {
let uiModel: NotificationUIModel
var body: some View {
VStack {
Text(uiModel.header)
Text(uiModel.message)
Button(action: {}) {
Text(uiModel.actionText)
}
}
}
}
複製程式碼
-
NotificationUIModel
是元件渲染所需的資料。這是 UIComponent 的輸入。 -
NotificationView
是一個 SwiftUI 檢視,用於定義元件的 UI。它以NotificationUIModel
作為依賴。當螢幕渲染時,此檢視是 UIComponent 的輸出。
2. 根據 API 響應結果構建 UIComponents
class HomePageController: ObservableObject {
let repository: Repository
@Published var uiComponents: [UIComponent] = []
..
..
func loadPage() {
val response = repository.getHomePageResult()
response.forEach { serverComponent in
let uiComponent = parseToUIComponent(serverComponent)
uiComponents.append(uiComponent)
}
}
}
func parseToUIComponent(serverComponent: ServerComponent) -> UIComponent {
var uiComponent: UIComponent
if serverComponent.type == "NotificationComponent" {
uiComponent = NotificationComponent(serverComponent.data,serverComponent.id)
}
else if serverComponent.type == "GenreListComponent" {
uiComponent = GenreListComponent(serverComponent.data,serverComponent.id)
}
...
...
return uiComponent
}
複製程式碼
-
HomePageController
從儲存庫載入伺服器元件並將其轉換為 UIComponents。 -
uiComponent
屬性負責儲存 UIComponents 的列表。我們用@Published
屬性包裝使其轉化為可觀察的物件。其值的任何更改都將釋出到Observer(View)
。這樣可以使檢視
與應用程式狀態保持同步。
3. 在螢幕上渲染 UIComponents
這是最後一部分。螢幕的唯一職責是渲染 UIComponents
。它訂閱了可觀察的 uiComponents
。每當 uiComponents
的值更改時,就會通知 HomePage
,然後更新其 UI。通用的 ListView
用於展示 UIComponent。
struct HomePageView: View {
@ObservedObject var controller: HomePageViewModel
var body: some View {
ScrollView(.vertical) {
VStack {
ForEach(controller.uiComponents,id: \.uniqueId) { uiComponent in
uiComponent.render()
}
}
}
.onAppear(perform: {
self.controller.loadPage()
})
}
}
複製程式碼
通用的 VStack
:VStack
內部所有的 UIComponent 都在垂直方向上展示。因為 UIComponent 是唯一可識別的,所以我們可以使用 ForEach
進行渲染。
所有遵守 UIComponent 協議的元件都必須返回通用型別,因此 render()
方法返回的型別是 AnyView
。以下是 View
的擴充套件,用於將其轉換為 AnyView
。
extension View {
func toAny() -> AnyView {
return AnyView(self)
}
}
複製程式碼
結論
我們學習瞭如何使用 UIComponent
來使伺服器控制應用程式的 UI。其實 UIComponents
還可以實現更多功能。
現在我們考慮沒有伺服器端驅動時介面的情況。這種情況下,UI 片段在整個應用程式中使用很多次。這會導致檢視和檢視邏輯的重複。因此,最好將介面定義為有意義的,可重用的元件。
這種方式可以讓控制層/業務層定義和構造 UI 元件。另外,業務層也可以承擔控制 UI 的責任。
你可以在 GitHub 上找到這個專案。
您還可以閱讀在 Android 中使用 Jetpack Compose 建立基於元件的架構這篇文章,它詳細解釋了 UI 元件原理。文中使用的是 Jetpack compose —— Android 中宣告式的 UI 框架,因此這篇文章的內容也不難理解。
如果發現譯文存在錯誤或其他需要改進的地方,歡迎到 掘金翻譯計劃 對譯文進行修改並 PR,也可獲得相應獎勵積分。文章開頭的 本文永久連結 即為本文在 GitHub 上的 MarkDown 連結。
掘金翻譯計劃 是一個翻譯優質網際網路技術文章的社群,文章來源為 掘金 上的英文分享文章。內容覆蓋 Android、iOS、前端、後端、區塊鏈、產品、設計、人工智慧等領域,想要檢視更多優質譯文請持續關注 掘金翻譯計劃、官方微博、知乎專欄。