iT邦幫忙

2024 iThome 鐵人賽

DAY 8
0

昨天的結尾我們稍微提到 MVVM 架構,在開始動手製作 App 之前,讓我們先來深入了解一下 MVVM 吧!MVVM(Model-View-ViewModel)是一種常見的架構模式,它能幫助我們更好地分離邏輯與 View,使 App 的維護與擴展變得更加容易。

什麼是 MVVM?

https://ooorito.com/wp-content/uploads/2024/08/MVVM.webp

MVVM 是一種將資料處理與 UI 呈現分離的架構模式。它主要由三個部分組成:

  • Model:負責管理 App 的資料邏輯。這包含了我們要處理的所有資料,例如家用品的名稱、數量、到期日等。
  • View:就是 App 的 UI,負責顯示 Model 中的資料,並將使用者的操作傳遞給 ViewModel。
  • ViewModel:位於 Model 和 View 之間,負責將 Model 中的資料轉換成 View 可以使用的格式,並處理商業邏輯。

在 MVVM 架構中,View 不直接與 Model 互動,而是通過 ViewModel 來實現。這樣做的好處是,我們可以更好地測試和管理 App 中的邏輯,因為資料和 UI 是完全分開的。

參考資料:SwiftUI MVVM

在 SwiftUI 中實現 MVVM

SwiftUI 的設計理念非常適合與 MVVM 架構搭配使用。透過 SwiftUI 的宣告式語法,我們可以輕鬆地建立 View,並利用 @StateObject@Published 等屬性來管理 View 與 ViewModel 之間的資料同步,讓 UI 隨著資料變動而自動更新。

@Published

@Published 是 Swift 中的一個屬性包裝器,它用來標記那些可能會隨時改變且需要通知 View 更新的屬性。當我們在 ViewModel 中使用 @Published 來修飾一個屬性時,任何與這個屬性相關的 View 都會在屬性值變化時自動重新渲染。這樣,我們無需手動更新 UI,資料的變動會自動反應在 View 上。例如:

@Published var items: [Item] = [
    Item(name: "牛奶", quantity: 2),
    Item(name: "麵包", quantity: 1)
]

在這個例子中,當 items 陣列中的資料變化時,使用這個資料的 View 會自動更新,顯示最新的內容。

參考資料:SwiftUI-什麼是@Published? ObservableObject?

@StateObject

@StateObject 是 SwiftUI 中用來管理 ViewModel 的屬性包裝器。當我們在 View 中使用 @StateObject 來初始化一個 ViewModel 時,SwiftUI 會負責管理這個物件的生命週期,並在 View 更新時保留這個物件。即使 View 被多次重建,@StateObject 所管理的 ViewModel 也會保持不變。例如:

@StateObject var viewModel = ItemViewModel()

這段程式碼讓 ItemViewModel 只會被初始化一次,並在 View 的生命週期內持續存在,這樣我們的資料狀態就能在多次 UI 更新中保持一致。

參考資料:SwiftUI view 的生命週期影響 StateObject & State property 儲存的資料

實作範例

假設我們要顯示一個家用品清單,首先我們會定義一個 Item 模型來代表每一個家用品:

struct Item: Identifiable {
    var id = UUID()
    var name: String
    var quantity: Int
}

在這裡,我們使用了 Identifiable protocols 來讓每個 Item 都有一個唯一的 id。這個 id 屬性允許 SwiftUI 能夠唯一識別每一個資料項目。當我們在使用像 ListForEach 這樣的元件來顯示資料時,SwiftUI 需要這樣的唯一識別來有效追蹤和管理資料的變化。即使兩個 Item 的 name 和 quantity 相同,由於 id 是唯一的,SwiftUI 還是能夠正確地辨識它們是不同的項目。

參考資料:

接著,我們需要建立一個 ViewModel 來管理這些資料,並且將它們提供給 View:

class ItemViewModel: ObservableObject {
    @Published var items: [Item] = [
        Item(name: "牛奶", quantity: 2),
        Item(name: "麵包", quantity: 1)
    ]
    
    func addItem(name: String, quantity: Int) {
        let newItem = Item(name: name, quantity: quantity)
        items.append(newItem)
    }
}

這裡我們使用了剛剛提到的 @Published 屬性來修飾 items,當 items 的值改變時,會自動通知與之綁定的 View 進行更新。

ObservableObject

ObservableObject 是 SwiftUI 中用來協調資料變化與 View 更新的 protocol。在宣告型別時,必須要遵從 ObservableObject 這個 protocol ,這時就可以讓需要自動更新的物件使用 @Published 來標記。這樣當這些屬性變化時,任何觀察這個物件的 View 都會被通知並更新顯示。這使得 ViewModel 可以作為資料和 UI 之間的橋樑,確保資料變化能夠即時更新在 View 上。

@StateObject 和 @ObservedObject 的差異

在 SwiftUI 中,@StateObject@ObservedObject 都是用來觀察 ObservableObject 的,但它們有一些關鍵的差異:

  • @StateObject
    • 使用情境:當一個 View 需要負責建立並管理 ObservableObject 的生命週期時,應使用 @StateObject
    • 功能@StateObject 只會在 View 的生命週期內建立一次 ObservableObject,並且當 View 銷毀時,這個物件也會被銷毀。
    • 適合場景:適合用在 View 內部建立 ViewModel,並且希望該 View 負責管理這個 ViewModel 的情況。
  • @ObservedObject
    • 使用情境:當一個 View 只是監聽已經由其他地方建立和管理的 ObservableObject 時,使用 @ObservedObject。
    • 功能@ObservedObject 不會建立或擁有 ObservableObject,它只負責觀察這個物件的變化。
    • 適合場景:適合當 ViewModel 是由父 View 或外部控制器建立和管理,子 View 只需監聽變化而不負責管理生命週期的情況。

在這個範例中,ItemViewModel 遵從了 ObservableObject 協議,讓 SwiftUI 能夠監聽 items 的變化,並自動更新使用這些資料的 View。

我們使用 @StateObject 來管理 ItemViewModel。這是因為 ContentView 是第一個建立並使用這個 ViewModel 的 UI,我們希望這個 UI 負責管理 ViewModel 的生命週期。

使用 @StateObject 可以避免重複建立 ViewModel,即使 ContentView 被重新渲染,它仍然會保持對同一個 ItemViewModel 的引用,這樣可以避免潛在的問題。

接下來,我們的 View 就可以使用 ItemViewModel 來顯示這些資料:

struct ContentView: View {
    @StateObject var viewModel = ItemViewModel()

    var body: some View {
        List(viewModel.items) { item in
            Text("\(item.name) - \(item.quantity)")
        }
    }
}

在這個範例中,ContentView 使用了 @StateObject 來初始化 ItemViewModel,並通過 List 元件將資料顯示在 UI 上。

https://ooorito.com/wp-content/uploads/2024/08/%E6%88%AA%E5%9C%96-2024-08-20-%E4%B8%8A%E5%8D%8812.05.24-542x1024.webp

為什麼選擇 MVVM?

MVVM 可以讓我們的程式更加模組化和易於測試。因為 View 與 ViewModel 之間的清晰分離,我們可以單獨測試 ViewModel 中的商業邏輯,而不需要與 UI 綁定在一起。同時,這種模式也讓我們的 App 更容易擴展,因為邏輯與 View 是完全獨立的。

總結

今天我們練習在 SwiftUI 中使用 MVVM 架構,這是建立大型 App 時非常重要的一個模式。透過 MVVM,我們可以更好地管理 App 中的資料和 View 之間的關係,讓我們的程式碼更具結構性和可維護性。接下來,我們會繼續運用這個架構來開發我們的家用品庫存 App,敬請期待!


上一篇
Day 7: 認識 SwiftUI 的 State 與 Binding
下一篇
Day 9: 使用 SwiftUI 的 List 顯示家用品清單
系列文
用 SwiftUI 掌控家庭日用品庫存30
圖片
  直播研討會
圖片
{{ item.channelVendor }} {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言