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
}
}