Contents
Chào bạn đến với Fx Studio!
Series Combine đã đi được gần một nữa chặng đường. Bài viết này sẽ là bài đầu tiên cho phần 3 của toàn series. Đó là Combine vs. MVVM. Trước tiên, mình sẽ có một lưu ý như thế này:
Phần MVVM của series từ Fx Studio là đó mình suy nghĩ và tự phát triển. Có rất nhiều các repo trên GitHub hay các bài viết về mô hình MVVM với Combine hoặc với RxSwift. Nên sereis bài viết của Fx Studio chỉ mang tính chất tham khảo bạn có một cái hình tổng quả về MVVM trong thế giới Reactive Programming.
Nếu bạn chưa tìm hiểu về Combine Framework thì bạn nên đọc qua 2 phần đầu tiên của series.
Còn bạn đã biết về nó rồi thì …
Bắt đầu thôi!
Chuẩn bị
- Xcode 11.0
- Swift 5.1
- iOS 13.0
Project sử dụng trong bài viết này sẽ dùng cho toàn bộ các bài viết liên quan tới MVVM trong series. Bạn có thể xem qua cấu trúc file ban đầu như sau:
Bạn không cần quá để tâm đâu. Vì bạn có thể tự tạo 1 project và bắt đầu xây dựng mọi thứ đầu con số 0. Project demo sẽ có màn hình đầu tiên là Welcome.
Để có thể linh hoạt nhất thì mình khuyến cáo bạn nên sử dụng project không sử dụng Storyboard. Nếu bạn chưa biết hoặc đã quên cách tạo iOS Project không sử dụng storyboard với Xcode 11 thì có thể xem link sau:
Vì Xcode 11 và iOS 13 mới sử dụng được Combine Framework.
1. MVVM là gì?
Câu hỏi này khá là dư thừa và mình cũng có trình bày cụ thể mô hình MVVM trong iOS ở bài viết sau:
Trong bài viết này, mình sẽ không giải thích lại MVVM và chức năng của các thành phần trong đó. Thay vì đó, tại sao mình chọn MVVM với Combine?
Đó là …
Quen thuộc & phổ biến
Đây là 2 yếu tố chính quyết định.
- Quen thuộc vì có thể bạn bắt đầu học lập trình là đã tiếp cận với mô hình MVC rồi. Lớn lên 1 tí, bạn sẽ tiếp cận với mô hình MVVM. Và đó cũng chính là cái bạn sử dụng hằng ngày khi làm việc. Bạn cũng như mình, sẽ không dám đập đi hết code cũ để xây dựng một thứ gì đó mới.
- Phổ biến vì giờ đây, đâu đâu cũng là MVVM. Cách cấu trúc và tổ chức file. Có mặt trong nhiều ngôn ngữ lập trình và nhiền nền tảng hiện nay.
Đó là dẫn truyện để tới phần tiếp theo của bài. MVVM với Combine có gì đặc biệt?
2. MVVM với Combine
Theo như trên thì:
Bình mới rượu cũ mà thôi!
2.1. Tư tưởng
Vẫn là MVVM, vẫn là các thành phần quen thuộc trong đó. Cũng như các chức năng và nhiệm vụ mà thành phần đảm nhiệm. Và chúng ta sẽ thêm bớt một chút, để cho hương vị thêm đậm đà hơn.
Cái cần thêm vào chính là tư tưởng
. Và có 2 điểm bạn cần chú ý để giữ đúng tôn chỉ của tư tưởng này.
Không phải là Combine
Nghe qua thì sẽ thấy gì đó sai sai. Ngay khi tiêu đề đã là MVVM với Combine rồi. Tuy nhiên, bạn nhớ lại các bài đầu tiên trong series, thì người tiền nhiệm của Combine đó là RxSwift. Và mở rộng hơn nữa đó chính là Reactive Programming.
Đó là cái cốt lõi đầu tiên cần phải nắm được và giữ nó xuyên suốt cả project. Mọi thức không còn là các class & đối tượng. Mà sẽ là việc phát và nhận dữ liệu.
Các thành phần sẽ tự động biển đổi & phản ứng lại các dữ liệu nhận được từ các nguồn phát. Khi làm được như vậy, bạn sẽ không lệ thuộc vào Combine hay RxSwift hay bất cứ framework nào khác. Và mình tin tưởng là bạn vẫn có thể áp dụng mô hình này không chỉ mỗi iOS mà thôi.
Sử dụng được cho các project cũ
Về bản chất Combine vẫn là Framework. Và nó sẽ giải quyết một số vấn đề trong rất nhiều vấn đề gặp phải. Chứ nó không thay thế được hết cả project. Bạn cũng không muốn code mới của bạn lại không thể tái sử dụng vào trong các project khác.
Hoặc bạn mang trong mình đầy hoài bão giải cứu thế giới này bằng Combine. Và nhanh chóng đưa nó vào trong project của bạn. Nhưng bạn có cả một team chục người, ai nấy mặt cũng đầy sát khí … Mỗi hành động gõ code của bạn đề có thể ảnh hưởng tới sự sống chết sau này của cả team bạn.
Vâng vâng … nhiều vấn đề sẽ phát sinh. Nếu chúng ta không biết …
Dung hợp giữa Combine Code & Non-Combine Code.
Vì vậy, 2 tư tưởng đó sẽ ảnh hưởng tới toàn bộ mô hình MVVM mới. Bạn cần phải ghi nhớ kĩ và luôn tuân theo.
2.2. Mô hình
Đây là phần chính trong bài. Mình có nghe 1 câu của 1 cu cậu trong team mình nói:
Ngàn lời nói không bằng một hình ảnh.
Tuy là như vậy, nhưng mình không thể diễn đạt thông qua một hình duy nhất được. Mình sẽ chia ra nhiều bài và mới mỗi bài mình sẽ có một hình vẽ mô tả cho bạn để dễ hình dung nhiều hơn.
Đầu tiên chính là sơ đồ tổng quát nhất của mô hình MVVM với Combine
Vẫn là những từ khoá quen thuộc. Có thêm vài từ khoá mới, vì nó thuộc tư tưởng lập trình Reactive Programming.
- View
- Ở đây bao gồm luôn cả View & ViewController
- Nhiệm vụ vẫn là hiển thị dữ liệu và truyền sự kiện người dùng cho ViewModel
- Khác biệt là nội dung hiện thị tuỳ thuộc vào trạng thái dữ liệu của ViewModel
- Tạo nên mối liên kết dữ liệu từ nguồn phát là ViewModel và nơi nhận là các thuộc tính của View
- ViewModel
- Trung tâm điều hành chính của các mô hình
- Lưu trữ dữ liệu. Nhưng nâng cấp việc lưu trữ thành nguồn phát dữ liệu
- Setup các state phản ứng lại với từng loại sự kiện/dữ liệu nhận được. Sau đó phát đi để View biết được và có phản ứng lại từng state này
- Model
- Vẫn như trước đây
- Sẽ Combine hoá các function của từng Model
- Hai vấn đề cần chú ý là notify & call back
2.3. Cấu trúc file
Đây là cấu trúc đề nghị cho mô hình MVVM với Combine. Bạn có thể tuỳ biến theo sở thích và đam mê code của riêng bạn.
Trước tiên bạn xem lại hình ảnh sau & mình sẽ nói ý nghĩa từng thư mục.
- AppDelegate
- AppDelegate.swift : giữ nguyên hiện trạng, là nơi quản lý cấu hình chung cho thiết bị
- SceneDelegate.swift : thiết lập việc hiển thị cho project với
rootViewController
- Define
- Các struct với các thuộc tính
static
- Các biến, key, value … dùng để lưu cấu hình và thông tin chung của project
- Các struct với các thuộc tính
- Controllers
- Base
- Chứ các sub-class kế thừa các Controller gốc (như UIViewController, UINavigationController …)
- Dùng để cấu hình và cài đặt các thành phần chung cho các Controller sử dụng trong project
- Các thư mục khác là các màn hình. Đặt tên theo tên màn hình. Trong đó có
- ViewController
- Giao diện (*. xib)
- ViewModel
- Base
- Views
- Chức các view custom và các sub-class của các UI Control
- Exts
- Chứa các file
extension
của các class - Sử dụng hâụ tố
Publisher
để biết extension đó được Combine hoá
- Chứa các file
- Models
- Entities
- Chứa các class/struct/enum … đại diện cho các đối tượng sử dụng trong project
- API
- Phần core tương tác với API/Webservice …
- Managers
- Các Model bọc các xử lý liên quan tới một cùng vấn đề nào đó
- Tuỳ thuộc vào mỗi project mà sẽ có thêm các core khác được thêm vào
- Entities
- Resources
- Images
- Fonts
- Media (sound, video … )
- …
3. Hoạt động
Bạn thử xem tư tưởng của mô hình MVVM mới sẽ sử dụng như thế nào. Chúng ta sẽ tìm hiểu thông qua code ví dụ.
3.1. ViewModel
Bạn xem hình ở mô tả WelcomeViewController, thì sẽ thấy cần có các dữ liệu cho:
- Name
- About
- Trạng thái đăng nhập
Trước tiên tại thư mục Entities, bạn tạo một file tên là User.swift
, như sau:
struct User { var name: String var about: String var isLogin: Bool }
Cũng khá là thô sơ, nhưng đó là thực thể của chúng ta sẽ sử dụng cho màn hình Welcome này.
Bước tiếp theo, ta tạo class ViewModel cho màn hình Welcome, với tên là WelcomeViewModel
final class WelcomeViewModel { var user: User //MARK: init init(user: User) { } }
Vì các thuộc tính của ViewModel sẽ là ánh xạ
của các UI Control trên View. Ví dụ như:
- Với UILable thì sẽ là String
- Với UITableView thì sẽ là Array
- …
Cái khó ở đây là bạn phải chọn kiểu dữ liệu cho các thuộc tính của ViewModel sao cho đảm bảo được:
- Lưu trữ dữ liệu
- Nguồn phát dữ liệu
Và đó chính là các Subject
. Thực thể đầy quyền năng trong Combine, nhằm kết nối được với cả 2 thế giới Combine & Non-Combine Code. Bạn xem thử code mới sẽ như thế nào. Đừng quên việc import Combine
vào class nha.
import Foundation import Combine final class WelcomeViewModel { //MARK: Properties let name = CurrentValueSubject<String?, Never>(nil) let about = CurrentValueSubject<String?, Never>(nil) let loginEnabled = CurrentValueSubject<Bool, Never>(false) let errorText = CurrentValueSubject<String?, Never>(nil) var user: User //MARK: init init(user: User) { } }
Các CurrentValueSubject
có:
- Lưu trữ được dữ liệu
- Có vai trò như là 1 Publisher
- Sẽ yêu cầu dữ liệu lúc khởi tạo
- Khi 1 subscriber
subscribe
tới thì sẽ nhận được ngay dữ liệu
Ta có thêm phần dữ liệu cho error
. Cái này là giả tưởng thôi, cho thêm phần sinh động.
Tiếp tục hoàn thiện function init
của ViewModel với dữ liệu từ tham số.
init(user: User) { self.user = user }
Bạn đã xong phần setup cơ bản cho việc lưu trữ của ViewModel.
3.2. State
Đây là khái niệm mới xuất hiện trong mô hình MVVM truyền thống. Do đối tượng chúng ta quan tâm lúc này chính là dữ liệu. Mọi hành vi và biến đổi trên giao diện đều phải dựa theo trạng thái của dữ liệu.
ViewModel là nơi tập trung việc lưu trữ dữ liệu. Đồng thời sẽ phát đi các tín hiện về trạng thái của dữ liệu đang lưu trữ. Từ đó View sẽ có những hành động tương ứng. Các hành vì đó sẽ được cài đặt và mình sẽ trình bày ở phần dưới. Còn bây giờ chúng ta tập trung việc khai báo các State
trong ViewModel.
Tiếp tục với file WelcomeViewModel
, thêm đoạn code sau để định nghĩa các State
của nó.
enum State { case initial case error(message: String) }
Trong code ví dụ này chúng ta khai báo 2 trạng thái trước tiên là
initial
cho khởi tạo của ViewModelerror
cho trường hợp có lỗi, dùng để show Alert thông báo cho người dùng biết
Bạn thấy chúng ta có thể thêm các tham số vào các case
của enum
. Đó chính là điều tự hào với ngôn ngữ Swift đã cho mình thêm khá nhiều vũ khí. Và cuối cùng bạn có thể thêm các case
của riêng mình vào đó (ví dụ : fetched
dành cho việc gọi API xong).
Sang phần triển khai các State
trong ViewModel của bạn. Bạn thêm 1 function sau vào trong class ViewModel. Lưu ý nên khai báo private
, do phần này là nội bộ ViewModel chém giết lẫn nhau thôi.
private func processState(_ state: State) { switch state { case .initial: name.value = user.name about.value = user.about loginEnabled.value = user.isLogin errorText.value = nil case .error(let message): errorText.value = message } }
Function này sẽ xử lý từng trường hợp của State
. Với mỗi trường hợp chúng ta có thể:
- Thay đổi dữ liệu lưu trữ
- Gọi các request tới các Model
- Thực hiện một số hành động
- Tiến hành điều hướng cho View
Ở trên, bạn đã xong phần setup cho State
chừ đến việc triển khai. Vấn đề của chúng ta là phải dữ đúng tư tưởng của Combine. Nên việc thay đổi các State
cũng phải do 1 Publisher quyết định. Lựa chọn hàng đầu vẫn là Subject
. Mở file WelcomeViewModel
, bạn thêm khai báo sau vào trong class:
- Output là kiểu
State
- Failure là Never
let state = CurrentValueSubject<State, Never>(.initial)
Subject state
này sẽ chịu trách nhiệm việc thay đổi trạng thái dữ liệu của ViewModel. Và cũng với tư tưởng của Combine thì bạn cần phải cài đặt các phản ứng lại Publisher này. Chúng ta sẽ chọn function init
để triển khai việc này.
init(user: User) { self.user = user //subscriptions _ = state.sink(receiveValue: { [weak self] state in self?.processState(state) }) }
Vì state
là Publisher nên đơn giản là bạn subscription
tới nó. Tại closure của sink
bạn gọi function processState
. OKE, bạn đã xong phần State
, giờ triển khai phần tiếp theo nào.
3.3. Actions
Đây cũng là một khái niệm mới được thêm vào mô hình MVVM này. Trước đây, việc xử lý hành động của người dùng thì chỉ là các lời gọi hàm liên tiếp nhau qua các lớp.
View —> IBAction —> ViewModel —> function —> Model
Và thật là khó khăn khi phải qua tới 3 hay 4 lớp. Rồi vấn đề xử lý phản hồi sau khi đã thực hiện xong nhiệm vụ …
Tất nhiên, nó vẫn còn đó, không có gì sai hay cần phải thay đổi ở đây. Nhưng ta tạm thời không sử dụng cách này. Chúng ta cũng phải đưa tư tưởng của Combine vào.
- Các sự kiện của người dùng thì cũng được xem là
luồng dữ liệu bất đồng bộ
- Cần có một Publisher để phát đi các sự kiện này
- Việc xử lý các sự kiện này sẽ tuỳ thuộc vào dữ liệu của Publisher nhận được mà triển khai.
Do đó, chúng ta hoàn toàn có thể khai báo & cài đặt hết các phản ứng lại các sự kiện người dùng. Và quên đi việc sử dụng các đối tượng ở các lớp để gọi lẫn nhau & xử lý call back.
Giờ sang phần code demo. Bạn tiếp tục khai báo enum
cho Action và 1 Publisher cho Action ở class ViewModel.
enum Action { case gotoLogin case gotoHome } let action = PassthroughSubject<Action, Never>()
Nó cũng tương tự như State. Lần này thì sử dụng PassthroughSubject, vì chúng ta không cần lưu trữ dữ liệu, khi có sự kiện thì sẽ phát liền đi cho subscriber biết.
Tiếp theo, thêm function để xử lý các trạng thái của Action. Tạm thời chúng ta sẽ print
nó ra thôi.
private func processAction(_ action: Action) { switch action { case .gotoHome: print("goto HomeVC") case .gotoLogin: print("goto LoginVC") } }
Và sang phần subscribe
Action để gọi function xử lý nó. Và chúng ta sẽ khai báo ở init
, nơi tập trung của thế giới văn minh.
init(user: User) { self.user = user //subscriptions _ = state.sink(receiveValue: { [weak self] state in self?.processState(state) }) _ = action.sink(receiveValue: { [weak self] action in self?.processAction(action) }) }
Action cũng tương tự như State, cũng không có gì khó hiểu ở đây. Chúc mừng bạn đã xong phần cài đặt ViewModel và thiết lập các phản ứng lại với các Publisher.
3.4. View
View = View + UIViewController
Đây là phần biến tấu chính, kịch hay bắt đầu ở đây. Bạn tiếp tục với cấu hình View. Đầu tiên, chúng ta khai báo các IBOutlet của View.
//MARK: Outlets @IBOutlet weak var nameLabel: UILabel! @IBOutlet weak var aboutLabel: UILabel! @IBOutlet weak var homeButton: UIButton! @IBOutlet weak var loginButton: UIButton!
Bạn cần gì? Thì hay tiến hành viết Outlet cho nó mà thôi. Tại WelcomeViewController, ta cần:
nameLabel
để hiển thị tên của người dùngaboutLabel
để hiển thị thêm chút thông tin của người dùnghomeButton
sẽ đượcenable
khi người dùng đã loginloginButton
sẽ đượcenable
khi người dùng chưa login- 2 button này đối ngịch lẫn nhau
À, bạn đừng quên import Combine
cho ViewController nha. Còn giờ chúng ta sẽ tiến hành khai báo đối tượng ViewModel cho ViewController.
let viewModel = WelcomeViewModel(user: .init(name: "Fx", about: "Admin", isLogin: false))
Sử dụng dữ liệu giả như trong dòng code trên nha. Tiếp theo là khai báo phần lưu trữ cho các subscriptions
phát sinh trong View.
var subscriptions = Set<AnyCancellable>()
Vẫn là chuyện cũ của năm xưa, với AnyCancellable thì nó giúp bạn việc quản lý bộ nhớ khi sử dụng nhiều thành phần trong Combine. Bạn muốn đọc thêm về nó thì quay phần 1 của series, mình đã giải thích ở đó rồi.
Giờ sang phần cài đặt việc lắng nghe sự thay đổi dữ liệu từ ViewModel ảnh hưởng tới View như thế nào. Bạn mở file WelcomeViewController và tại viewDidLoad. Tiến hành subscribe
các subject của ViewModel.
override func viewDidLoad() { super.viewDidLoad() // title title = App.Text.titleWelcomeVC // subscriptions // name viewModel.name .assign(to: \.text, on: nameLabel) .store(in: &subscriptions) // about viewModel.about .assign(to: \.text, on: aboutLabel) .store(in: &subscriptions) // buttons viewModel.loginEnabled .sink { isLogin in self.loginButton.isEnabled = !isLogin self.homeButton.isEnabled = isLogin } .store(in: &subscriptions) }
Đoạn code trên thì không có gì mới hết. Mình giải thích lại chút:
- Bạn có 2 cách để
subscription
tới 1 Publisher, đó là:assign
: dùng để đưa dữ liệu tới trực tiếp thuộc tính của đối tượngsink
: dùng để xử lý nhiều tác vụ đối với dữ liệu nhận được
- Cần phải
store
các subscription này lại - Tuỳ thuộc vào ý đồ của bạn mà lựa chọn cách subscription phù hợp
- Với
name
&about
thì dữ liệu sẽ trực tiếp lên tới thuộc tínhtext
của UILabel. Bạn cũng không cần quan tâm tới đối tượng View làm gì. - Với
loginEnable
thì cần có 2 xử lý ở đây, nên sẽ chọnsink
. Ngoài ra, bạn cũng có thể tốn 2 subscriptionassign
cho 2 button cùng tới 1 Publisher, để xử lý 2 button này.
- Với
Tới đây, bạn có thể build và xem mô hình chúng ta có hoạt động đúng theo ý đồ chưa. Nếu kết quả hiển thị đúng như dữ liệu giả của bạn, thì chúng mừng bạn đã thành công.
Bạn sẽ thấy
- name là FX
- about là Admin
- và button Home bị disable vì isLogin = false
Bạn xem lại tất cả các code chúng ta ở trên, thì hầu như đều là việc khai báo và lắng nghe các Publisher. Giờ sang phần cuối đó là Action.
Bạn cũng không cần phải cài đặt subscription tới các action của Button làm gì. Hay tìm cách Combine/Reactive hoá các UIButton này.
Bạn dùng ở UIKit và hãy để UIKit làm các phần việc của nó phải làm.
Với Action, bạn chỉ cần khai báo các IBAction
là ổn rồi. Mở file WelcomeViewController, thêm các IBAction
cho 2 button của nó
@IBAction func loginButtonTourchUpInside(_ sender: Any) { } @IBAction func homeButtonTourchUpInside(_ sender: Any) { }
Tại đây chúng ta sẽ thực hiện việc gọi action
ở ViewModel. Thông qua việc phát đi các giá trị. Bạn xem tiếp:
@IBAction func loginButtonTourchUpInside(_ sender: Any) { viewModel.action.send(.gotoLogin) } @IBAction func homeButtonTourchUpInside(_ sender: Any) { viewModel.action.send(.gotoHome) }
Bạn build project và tận hưởng kết quả nào. Nếu như kết quả vẫn không đúng, thì bạn cần phải debug lại. Hoặc các subscription của bạn ở ViewModel đã bị giải phóng rồi. Bạn cần phải tiến hành lưu trữ chúng.
Quay lại file WelcomeViewModel, thêm thuộc tính sau, để lưu trữ các subscription phát sinh.
var subscriptions = Set<AnyCancellable>()
Edit lại function init
một chút với store
cho các Subject.
init(user: User) { self.user = user //subscriptions state .sink(receiveValue: { [weak self] state in self?.processState(state) }) .store(in: &subscriptions) action .sink(receiveValue: { [weak self] action in self?.processAction(action) }) .store(in: &subscriptions) }
Thêm function deinit
, để giải phóng các subscriptions. Mặc dù điều này không cần thiết lắm, nhưng làm cho vui cũng được. Biết đâu nó sẽ hữu ích cho tương tai.
deinit { subscriptions.removeAll() }
Giờ bạn build lại project và tận hưởng kết quả nào!
OKAY! Mọi thứ đã ổn và mình xin hết thúc bài viết này ở đây. Đó chỉ là phần giới thiệu về mô hình MVVM với Combine Framework thôi. Các bài sau sẽ đi vào chi tiết từng mối quan hệ giữa các thành phần. Bạn hãy đón chờ xem nó.
Bạn có thể checkout project demo tại đây:
Tạm kết
- Mọi thuộc tính của View phải ánh xạ với mọi thuộc tính của ViewModel. Chúng có mối quan hệ với nhau theo kiểu dữ liệu của từng thuộc tính
- ViewModel sẽ tiến hành cài đặt & thiết lập cho State và Action
- View sẽ thay đổi dựa vào các dữ liệu từ các Publisher của ViewModel phát ra
- View điều khiển các Action của ViewModel thông qua việc phát ra giá trị
- Sau khi xử lý các hành động, ViewModel sẽ thay đổi trạng thái của State. Từ đó, các Publisher của ViewModel sẽ phát ra các dữ liệu tương tứng.
Nếu bạn thấy bài viết này hay và hưu ích, thì hãy share cho nhiều người cùng đọc. Nếu bạn muốn đóng góp hoặc góp ý cho mình, thì hãy để lại comment hoặc email hoặc theo contact của website.
Cảm ơn bạn đã đọc bài viết này!
Written by chuotfx
Hãy ngồi xuống, uống miếng bánh và ăn miếng trà. Chúng ta cùng nhau đàm đạo về đời, về code nhóe!
2 comments
Leave a Reply Cancel reply
Fan page
Tags
Recent Posts
- CO-STAR – Công thức vàng để viết Prompt hiệu quả cho LLM
- Prompt Engineering trong 10 phút
- Một số ví dụ sử dụng Prompt cơ bản khi làm việc với AI
- Prompt trong 10 phút
- Charles Proxy – Phần 1 : Giới thiệu, cài đặt và cấu hình
- Complete Concurrency với Swift 6
- 300 Bài code thiếu nhi bằng Python – Ebook
- Builder Pattern trong 10 phút
- Observer Pattern trong 10 phút
- Memento Pattern trong 10 phút
Archives
- December 2024 (4)
- September 2024 (1)
- July 2024 (1)
- June 2024 (1)
- May 2024 (4)
- April 2024 (2)
- March 2024 (5)
- January 2024 (4)
- February 2023 (1)
- January 2023 (2)
- November 2022 (2)
- October 2022 (1)
- September 2022 (5)
- August 2022 (6)
- July 2022 (7)
- June 2022 (8)
- May 2022 (5)
- April 2022 (1)
- March 2022 (3)
- February 2022 (5)
- January 2022 (4)
- December 2021 (6)
- November 2021 (8)
- October 2021 (8)
- September 2021 (8)
- August 2021 (8)
- July 2021 (9)
- June 2021 (8)
- May 2021 (7)
- April 2021 (11)
- March 2021 (12)
- February 2021 (3)
- January 2021 (3)
- December 2020 (3)
- November 2020 (9)
- October 2020 (7)
- September 2020 (17)
- August 2020 (1)
- July 2020 (3)
- June 2020 (1)
- May 2020 (2)
- April 2020 (3)
- March 2020 (20)
- February 2020 (5)
- January 2020 (2)
- December 2019 (12)
- November 2019 (12)
- October 2019 (19)
- September 2019 (17)
- August 2019 (10)
Anh ơi!
Em thấy trên bài viết biến name anh dùng CurrentValueSubject nhưng source trong link git anh lại dùng @Published?
Vậy khi nào dùng Published khi nào dùng CurrentValueSubject subject ạ?
Hi em,
Do github là lưu source code cuối cùng. Nó đè source code trước đó em.
@Published: dùng trường hợp biến chỉ để lưu trữ dữ liệu thôi nha. Thì @Published còn dùng nhiều cho Binding 1 và 2 chiều.
Ví dụ: nối giữa UITextField với 1 property bên ViewModel vậy. Update bên UI thì bên Data cũng thay đổi. Hay Array bên ViewModel với UITableView bên View nữa, fetch dữ liệu xong thi cập nhật array –> View cũng reload theo (chỗ này chú ý việc objectwillChanged)
CurrentValueSubject: dùng để biểu diễn trạng thái của của đối tượng.
Dễ hiểu hơn thì:
private @Published var ….
public var …. : CurrentValueSubject
Về ngữ nghĩa chung thì giống nhau. Nên tuỳ cách em sử dụng phạm vi cho tụi nó thôi. Chứ cái này không có nguyên tắc gì hết.