- vừa được xem lúc

MVVM in iOS Swift

0 0 10

Người đăng: Hoang Anh Tuan B

Theo Viblo Asia

Bất cứ khi nào chúng ta xây dựng ứng dụng iOS, câu hỏi này luôn luôn xuất hiện trong đầu chúng ta, chọn mô hình nào cho project mới. Mô hình được sử dụng nhiều nhất trong iOS là MVC. Hầu hết các developer đều sử dụng MVC cho project của họ. Những project nhỏ hoạt động tốt với MVC. Nhưng khi size của project tăng lên, nó bắt đầu khiến cho source code của bạn lộn xộn.

Tôi luôn luôn tìm mô hình tốt để sử dụng, nhưng chúng ta không nên tuân thủ nghiêm ngặt một mô hình trong dự án của mình. Không phải mô hình nào cũng đủ tốt để cung cấp cho bạn mọi thứ. Mỗi mô hình đều có ưu và nhược điểm. Nếu chúng ta có quá nhiều modules, chúng ta có thể quyết định mô hình theo modules. Một vài modules phù hợp tốt với MVVM, nhưng có thể modules mới không hoạt động tốt với MVVM, vì vậy chúng ta có thể sử dụng các mô hình thay thế như MVP, VIPER. Vì vậy, chúng ta không nên hoàn toàn dựa vào một mô hình duy nhất, thay vào đó, chúng ta có thể kiểm tra nó theo modules cũng được.

Có rất nhiều bài viết có sẵn trên internet giải thích định nghĩa, ưu và nhược điểm của MVVM, Vì vậy tại đây tôi sẽ tập trung nhiều hơn vào việc triển khai thực tế thay vì chỉ đọc định nghĩa.

Bắt đầu

Trong project này, chúng ta xây dựng một ứng dụng đơn giản sử dụng MVVM. Trong hầu hết các ứng dụng của chúng ta, chúng ta có một ViewController, lấy dữ liệu từ server, và hiển thị nó trong UI. Chúng ta sẽ thực hiện hành vi tương tự sử dụng MVVM.

Đây là kết quả mong đợi của chúng tôi ở bài viết này.

Tại đây, chúng ta sẽ sử dụng một dummy web service có sẵn trên internet

http://dummy.restapiexample.com/api/v1/employees

Web service này cung cấp cho chúng ta 1 list data nhân viên và chúng ta sẽ show nó lên tableview.

Tổng quan về thành phần và vai trò

View Controller: Chỉ thực hiện những thứ liên quan đến UI — Show/get thông tin, một phần của view layer

ViewModel: Nhận thông tin từ VC, xử lý tất cả những thông tin này và gửi lại nó cho VC.

Model: Đây là những model của bạn, không có gì nhiều ở đây. Nó giống với model trong mô hình MVC, nó được sử dụng bởi ViewModel và update bất cứ khi nào ViewModel gửi update mới.

Hãy cấu trúc code của chúng ta và tạo ra những file được yêu cầu trong những group tương ứng của chúng. Vì vậy chúng ta tạo ra 3 file, mỗi file trong một group (Model, ViewModel, APIService)

Model

Model đại diện cho dữ liệu đơn giản, Nó đơn giản nắm giữ dữ liệu và không làm bất cứ thứ gì liên quan tới logic. Bạn có thể nói một cách đơn giản đó là một cấu trúc dữ liệu mà chúng ta mong đợi từ API của chúng ta.

Tại đây chúng ta có thể kiểm tra response cho URL trên và chúng ta sẽ tạo một class model cho response này. Bạn có thể tạo một model của riêng mình hoặc ngược lại bạn có thể sử dụng bất kỳ model online nào.

// MARK: - Employee
struct Employees: Decodable { let status: String let data: [EmployeeData]
} // MARK: - EmployeeData
struct EmployeeData: Decodable { let id, employeeName, employeeSalary, employeeAge: String let profileImage: String enum CodingKeys: String, CodingKey { case id case employeeName = "employee_name" case employeeSalary = "employee_salary" case employeeAge = "employee_age" case profileImage = "profile_image" }
}

Flow ứng dụng sẽ như thế này:

  1. ViewController sẽ được gọi và View sẽ có một tham chiếu tới ViewMdoel
  2. View sẽ nhận một vài hành động của user và View sẽ gọi ViewModel
  3. ViewModel sẽ request APIService và APIService gửi response ngược lại cho ViewModel
  4. Khi chúng ta nhận được một response, ViewModel thông báo cho View thông qua binding
  5. View sẽ update UI với data

Bây giờ, chúng ta sẽ bắt đầu viết code theo trình tự. Đầu tiên ViewController sẽ được gọi và từ ViewController chúng ta gọi class ViewModel. Chúng ta không binding chúng bây giờ, việc đó để làm sau.

ViewModel

ViewModel là thành phần chính của mô hình này. ViewModel không biết View là gì và những gì View làm. Điều này làm cho mô hình này dễ kiểm tra hơn và loại bỏ sự phức tạp từ view.

Trong ViewModel chúng ta sẽ gọi APIService và lấy data từ server

Khi chúng ta viết code trong class ViewModel, nó sẽ cho bạn một số lỗi vì chúng ta chưa triển khai class API Service. Vì vậy bây giờ chúng ta sẽ triển khai class API Service

API Service

API Service là một class đơn giản, nơi mà chúng ta lấy data nhân viên sử dụng URLSession. Bạn có thể sử dụng bất kỳ networking model nào. Từ class ViewModel của chúng ta, chúng ta sẽ gọi API Service.

import Foundation class APIService : NSObject { private let sourcesURL = URL(string: "http://dummy.restapiexample.com/api/v1/employees")! func apiToGetEmployeeData(completion : @escaping (Employees) -> ()){ URLSession.shared.dataTask(with: sourcesURL) { (data, urlResponse, error) in if let data = data { let jsonDecoder = JSONDecoder() let empData = try! jsonDecoder.decode(Employees.self, from: data) completion(empData) } }.resume() }
}

Khi chúng ta nhận được response trong ViewModel. Bây giờ là lúc chúng ta thực hiện binding giữa ViewController và ViewModel

MVVM Bindings

MVVM binding đóng vai trò quan trọng trong project của chúng ta. Cách chúng ta giao tiếp giữa ViewModel và ViewController rất quan trọng. Chúng ta có thể binding bằng nhiều cách.

import Foundation class EmployeesViewModel : NSObject { private var apiService : APIService! private(set) var empData : Employees! { didSet { self.bindEmployeeViewModelToController() } } var bindEmployeeViewModelToController : (() -> ()) = {} override init() { super.init() self.apiService = APIService() callFuncToGetEmpData() } func callFuncToGetEmpData() { self.apiService.apiToGetEmployeeData { (empData) in self.empData = empData } }
}

Chúng ta tạo ra một thuộc tính trong ViewModel tên là bindEmployeeViewModelToController

var bindEmployeeViewModelToController : (() -> ()) = { }

Thuộc tính này cần để gọi từ ViewController.

Chúng ta tạo ra những thuộc tính khác trong ViewModel tên empData thuộc kiểu Employees (Model) cái mà giữ lại kết quả từ APIService và thông báo cho View rằng đã có sự thay đổi.

private(set) var empData : Employees! { didSet { self.bindEmployeeViewModelToController() } }

empData được set thành response nhận được từ API Service, bằng cách sử dụng thuộc tính observer, ngay khi chúng ta nhận được giá trị là response của API, didSet của empData sẽ được gọi và chúng ta đã gọi bindEmployeeViewModelToController() bên trong didSet của empData.

Khi chúng ta nhận được data từ ViewModel tới View, bây giờ là lúc chúng ta update UI

View

Để nhận data từ ViewModel, chúng ta link thuộc tính ViewModel bên trong ViewController

self.employeeViewModel.bindEmployeeViewModelToController = { self.updateDataSource() }
import UIKit class ViewController: UIViewController { @IBOutlet weak var employeeTableView: UITableView! private var employeeViewModel : EmployeesViewModel! private var dataSource : EmployeeTableViewDataSource<EmployeeTableViewCell,EmployeeData>! override func viewDidLoad() { super.viewDidLoad() callToViewModelForUIUpdate() } func callToViewModelForUIUpdate(){ self.employeeViewModel = EmployeesViewModel() self.employeeViewModel.bindEmployeeViewModelToController = { self.updateDataSource() } } func updateDataSource(){ self.dataSource = EmployeeTableViewDataSource(cellIdentifier: "EmployeeTableViewCell", items: self.employeeViewModel.empData.data, configureCell: { (cell, evm) in cell.employeeIdLabel.text = evm.id cell.employeeNameLabel.text = evm.employeeName }) DispatchQueue.main.async { self.employeeTableView.dataSource = self.dataSource self.employeeTableView.reloadData() } } }

Để cập nhật giao diện, bạn có thể viết code TableView trong ViewController, nhưng để cho ViewController bớt lộn xộn, chúng ta sẽ tách rời class EmployeeTableViewDataSource extend từ UITableViewDataSource.

import Foundation
import UIKit class EmployeeTableViewDataSource<CELL : UITableViewCell,T> : NSObject, UITableViewDataSource { private var cellIdentifier : String! private var items : [T]! var configureCell : (CELL, T) -> () = {_,_ in } init(cellIdentifier : String, items : [T], configureCell : @escaping (CELL, T) -> ()) { self.cellIdentifier = cellIdentifier self.items = items self.configureCell = configureCell } func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { items.count } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { let cell = tableView.dequeueReusableCell(withIdentifier: cellIdentifier, for: indexPath) as! CELL let item = self.items[indexPath.row] self.configureCell(cell, item) return cell }
}

Như tôi đã nói với mỗi mô hình đều có ưu và nhược điểm, nếu chúng ta có rất nhiều ưu điểm khi sử dụng MVVM thì cũng có một số nhược điểm như sau:

  • Với người mới bắt đầu, MVVM rất khó để triển khai
  • Những ứng dụng UI đơn giản, MVVM có thể là quá mức cần thiết, MVC là đủ
  • Với những ứng dụng lớn, việc bind data sẽ phức tạp, vì vậy debug sẽ khó hơn

Kết luận

Tại đây, chúng ta đã hoàn thành việc xây dựng một ứng dụng đơn giản sử dụng mô hình MVVM, Hi vọng điều này sẽ hữu ích. Cảm ơn các bạn

Đây là một bài viết khá hay, hữu ích và dễ hiểu mà mình đọc được trong lúc bắt đầu tìm hiểu về MVVM trong swift. Mọi người có thể tham khảo bài viết gốc tại đây nhé

Bình luận

Bài viết tương tự

- vừa được xem lúc

Học Flutter từ cơ bản đến nâng cao. Phần 1: Làm quen cô nàng Flutter

Lời mở đầu. Gần đây, Flutter nổi lên và được Google PR như một xu thế của lập trình di động vậy.

0 0 254

- vừa được xem lúc

Học Flutter từ cơ bản đến nâng cao. Phần 3: Lột trần cô nàng Flutter, BuildContext là gì?

Lời mở đầu. Màn làm quen cô nàng FLutter ở Phần 1 đã gieo rắc vào đầu chúng ta quá nhiều điều bí ẩn về nàng Flutter.

0 0 189

- vừa được xem lúc

Swift: Tạo custom phép toán tử (Operator) của riêng bạn!

Swift cho phép bạn tạo các toán tử có thể tùy chỉnh của riêng bạn. Điều này đặc biệt hữu ích khi bạn xử lý các loại dữ liệu của riêng mình. Operator Types in Swift. Có năm loại toán tử chính trong Swift.

0 0 46

- vừa được xem lúc

Chương 6 Protocol oriented programming.

Cuốn sách này là về lập trình hướng protocol. Khi Apple thông báo swift 2 ở WWDC 2015.

0 0 29

- vừa được xem lúc

Ví dụ về UIActivityViewController

Trên iOS, UIActivityViewController cung cấp giao diện thống nhất để người dùng chia sẻ và thực hiện các hành động trên văn bản, hình ảnh, URL và các mục khác trong ứng dụng. let string = "Hello, world!". let url = URL(string: "https://nshipster.com").

0 0 46

- vừa được xem lúc

Quản lý self và cancellable trong Combine.

. . Công việc quản lý bộ nhớ memory management thường trở nên phức tạp khi chúng ta thực hiện các tác vụ bất đồng bộ asynchronous vì chúng ta thường phải lưu giữ một số object nằm ngoài scope mà object được define trong khi vẫn phải đảm bảo được việc giải phóng object đó được thực hiện đúng quy trìn

0 0 29