본문 바로가기
Design Patterns/MVVM

Swift: How to Migrate MVC to MVVM

by 탄이. 2020. 6. 17.

Swift: How to Migrate MVC to MVVM & Intro Unit Testing

1. 기능이름ViewModel.swift 생성

  1. class 기능이름ViewModel or struct 기능이름ViewModel 만들기
  2. class/struct 안에 필요한 변수와 생성자 만들기 (의존성 주입)
    • 의존성 주입
struct CourseViewModel {

    let name: String

    // Dependency Injection (DI)
    init(course: Course) {
        self.name = course.name
    }

}

2. VC에 있는 Model 객체(e.g. fetchArray)를 ViewModel 객체로 변경

class CoursesController: UITableViewController {

    var courseViewModels = [CourseViewModel]()
...
  1. API 응답안에서 응답값을 ViewModel 객체로 map해줌

     fileprivate func fetchData() {
             Service.shared.fetchCourses { (courses, err) in
                 if let err = err {
                     print("Failed to fetch courses:", err)
                     return
                 }
    
                 self.courseViewModels = courses?.map({return CourseViewModel(course: $0)}) ?? []
                 self.tableView.reloadData()
             }
         }
  2. UITableViewCell 하위클래스에서도 Model 객체를 ViewModel 객체로 변경

    • (cell class의 Model객체 변수가 didSet 될 때 셀의 내용을 구성하는 방식으로 구현했다면)

      class CourseCell: UITableViewCell {
      
        var courseViewModel: CourseViewModel! {
            didSet {
                textLabel?.text = courseViewModel.name
                detailTextLabel?.text = courseViewModel.detailTextString
                accessoryType = courseViewModel.accessoryType
            }
        }
      ...
  3. tableView(_:cellForRowAt:) 에서도 ViewModel 객체로 변경

     override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
             let cell = tableView.dequeueReusableCell(withIdentifier: cellId, for: indexPath) as! CourseCell
             let courseViewModel = courseViewModels[indexPath.row]
             cell.courseViewModel = courseViewModel
             return cell
         }

3. cell에서 계산해서 보여줘야하는 부분은 struct 기능이름ViewModel 의 생성자에서 계산해서 만들어지도록 생성자 안에 계산로직을 넣는다. (Dependency Injection)

struct CourseViewModel {

    let name: String

    let detailTextString: String
    let accessoryType: UITableViewCellAccessoryType

    // Dependency Injection (DI)
    init(course: Course) {
        self.name = course.name

        if course.number_of_lessons > 35 {
            detailTextString = "Lessons 30+ Check it Out!"
            accessoryType = .detailDisclosureButton
        } else {
            detailTextString = "Lessons: \(course.number_of_lessons)"
            accessoryType = .none
        }
    }

}

댓글