Swift

MVVM 디자인 패턴

khon98 2021. 3. 4. 11:26

MVVM

- Model View ViewModel

 

리팩터링

- 기술 부채를 줄이고, 재사용 가능하면서 유지보수 비용을 적게 하기 위해 코드를 수정하는 과정

 

MVC

- Model View Controller

 

 

Cocoa Touch Class

import UIKit

// MVVM

// Model
// BountyInfo 만들기

// View
// - ListCell에 필요한 정보를 ViewModel에게서 받기
// - ListCell은 ViewModel로 부터 받은 정보로 뷰 업데이트

// ViewModel
// BountyViewModel을 만들고 뷰 레이어에서 필요한 메서드 만들기
// BountyInfo들의 모델 가지고 있기

class BountyViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
    
    let viewModel = BountyViewModel()
    let bountyList = [30000000, 50, 5000000, 500000000, 10000000, 15000000, 200000000, 350000000]
    
    // segue를 수행하기 직전에 수행하는 것에 대해서 준비하는 메서드
    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
        if segue.identifier == "showDetail" {
            
            let vc = segue.destination as? DetailViewController
            if let index = sender as? Int {
                
                let bountyInfo = viewModel.bountyInfo(at: index)
                vc?.viewModel.update(model: bountyInfo)
            }
        }
    }
    
    override func viewDidLoad() {
        super.viewDidLoad()
    }
 
    
    // UITableViewDataSource
    func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
        return viewModel.numOfBountyInfoList
    }
    
    func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
        guard let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as? ListCell else {
            return UITableViewCell()
        }
        
       let bountyInfo = viewModel.bountyInfo(at: indexPath.row)
        cell.update(info: bountyInfo)
        return cell
    }
    
    // UITableViewDelegate
    func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) {
        print("\(indexPath.row)")
        performSegue(withIdentifier: "showDetail", sender: indexPath.row)
    }
}

// custom cell 생성
class ListCell: UITableViewCell {
    @IBOutlet weak var imgView: UIImageView!
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var bountyLabel: UILabel!
    
    func update(info: BountyInfo) {
        imgView.image = info.image
        nameLabel.text = info.name
        bountyLabel.text = "\(info.bounty)"
    }
}

// ViewModel
class BountyViewModel {
    let bountyInfoList: [BountyInfo] = [
        BountyInfo(name: "brook", bounty: 30000000),
        BountyInfo(name: "chopper", bounty: 50),
        BountyInfo(name: "franky", bounty: 5000000),
        BountyInfo(name: "luffy", bounty: 500000000),
        BountyInfo(name: "nami", bounty: 10000000),
        BountyInfo(name: "robin", bounty: 15000000),
        BountyInfo(name: "sanji", bounty: 200000000),
        BountyInfo(name: "zoro", bounty: 350000000)
    ]
    
    // 순위 정하기
    var sortedList: [BountyInfo] {
        let sortedList = bountyInfoList.sorted { prev, next in
            return prev.bounty > next.bounty
        }
        
        return sortedList
    }
    
    var numOfBountyInfoList: Int {
        return bountyInfoList.count
    }
    
    func bountyInfo(at index: Int) -> BountyInfo {
        return sortedList[index]
    }
}

 

 

 

Cocoa Touch Class

import UIKit

// MVVM

// Model
// BountyInfo 만들기

// View
// - ImgView, nameLabel, bountyLabel
// - View들은 ViewModel을 통해서 구성

// ViewModel
// DetailViewModel
// 뷰 레이어에서 필요한 메서드 만들기
// BountyInfo들의 모델 가지고 있기

class DetailViewController: UIViewController {
    
    @IBOutlet weak var imgView: UIImageView!
    @IBOutlet weak var nameLabel: UILabel!
    @IBOutlet weak var bountyLabel: UILabel!
     
    let viewModel = DetailViewMomdel()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        updateUI()
    }
    
    func updateUI() {
        if let bountyInfo = viewModel.bountyInfo {
            imgView.image = bountyInfo.image
            nameLabel.text = bountyInfo.name
            bountyLabel.text = "\(bountyInfo.bounty)"
        }
    }
    
    // close 버튼을 누르면 창이 닫힘
    @IBAction func close(_ sender: Any) {
        dismiss(animated: true, completion: nil)
    }
}

class DetailViewMomdel {
    var bountyInfo: BountyInfo?
    
    func update(model: BountyInfo?) {
        bountyInfo = model 
    }
}

 

 

 

Cocoa Touch Class

import UIKit

// Model
struct BountyInfo {
    let name: String
    let bounty: Int
    
    var image: UIImage? {
        return UIImage(named: "\(name).jpg")
    }
    
    init(name: String, bounty: Int) {
        self.name = name
        self.bounty = bounty
    }
}