ViewModal是View和數據的中間層。ViewModel是視圖和數據之間的一層。 ViewModel一般使用service objects來獲取數據,對其進行格式化後向View提供格式化的數據。swift
當蘋果將ObservableObject協議移至Combine框架時,蘋果公司開始推廣MVVM模式。讓咱們看一下ObservableObject協議,以瞭解發生了什麼。架構
/// A type of object with a publisher that emits before the object has changed. public protocol ObservableObject : AnyObject { /// The type of publisher that emits before the object has changed. associatedtype ObjectWillChangePublisher : Publisher = ObservableObjectPublisher where Self.ObjectWillChangePublisher.Failure == Never /// A publisher that emits before the object has changed. var objectWillChange: Self.ObjectWillChangePublisher { get } }
ObservableObject協議具備惟一的要求,即在對象更改以前發出的發佈者。讓咱們編寫第一個符合ObservableObject協議的ViewModel。app
final class PostsViewModel: ObservableObject { let objectWillChange = PassthroughSubject<Void, Never>() private (set) var posts: [Post] = [] func fetch() { // fetch posts objectWillChange.send() // assign new data to the posts variable } }
在這裏,咱們有ViewModel來獲取帖子,將它們存儲在變量中,並經過objectWillChange發佈者發出通知。讓咱們看一下使用此ViewModel的示例ViewController。框架
final class PostsViewController: UIViewController { let viewModel: PostsViewModel override func viewDidLoad() { super.viewDidLoad() bindViewModel() viewModel.fetch() } private func bindViewModel() { viewModel.objectWillChange.sink { [weak self] in guard let self = self else { return } self.renderPosts(self.viewModel.posts) } } }
如您在上面的示例中看到的,咱們有PostsViewController,它開始觀察ViewModel中的更改,而後要求ViewModel提取數據。一旦ViewModel提取數據,它就會發出,而且ViewController調用renderPosts函數,該函數顯示下載的帖子。ide
咱們可使用@Published屬性包裝器進行進一步操做。 @Published屬性包裝器容許咱們將發佈者包裝任何屬性,只要屬性更改,發佈者就會發出當前值。函數
final class PostsViewModel: ObservableObject { @Published private(set) var posts: [Post] = [] func fetch() { // fetch posts and assign them to `posts` variable } }
正如您在上面的示例中看到的那樣,咱們不須要手動將值發送給objectWillChange發佈者,這是Swift編譯器合成的全部工做。而且咱們能夠保持PostsViewController的相同實現。工具
如前所述,@ Published屬性包裝器將咱們的屬性與發佈者包裝在一塊兒。讓咱們看看如何在PostsViewController中使用它post
final class PostsViewController: UIViewController { let viewModel: PostsViewModel override func viewDidLoad() { super.viewDidLoad() bindViewModel() viewModel.fetch() } private func bindViewModel() { viewModel.$posts.sink { [weak self] posts in self?.renderPosts(posts) } } }
在這裏,咱們有一個PostsViewController的重構版本。請看一下咱們如何更改bindViewModel函數。它如今訂閱$ posts,而且僅當特定屬性更改時,它才容許咱們更新視圖。只要您的ViewModel有愈來愈多的會影響視圖的字段,您就會當即看到好處。fetch
咱們可使用RxSwift,ReactiveSwift或任何其餘反應式框架(如Bond)輕鬆實現相同的邏輯。但我以爲MVVM將成爲架構iOS應用程序的默認選擇。至少到如今,當Apple爲咱們提供了開箱即用的全部必要工具時。ui
https://swiftwithmajid.com/2020/02/05/building-viewmodels-with-combine-framework/
QQ:3365059189
SwiftUI技術交流QQ羣:518696470