-
[iOS] MVVMPrograming/iOS 2020. 4. 2. 02:33
안녕하세요 :)
오늘은 이번학기 새로 시작할 프로젝트를 MVVM 디자인 패턴으로 프로젝트를 만들어가기 위해,
MVVM 디자인 패턴에 대해 살짝쿵 공부를 해보았습니다.
이번 프로젝트에서 MVVM 디자인 패턴으로 구조를 구성하기로 생각을 한 이유는 현재 진행중인 동아리 프로젝트에서는 MVC로 하는데 점점 가면 갈수록, 프로젝트가 비대해질수록, 프로젝트의 구조를 파악하고 고쳐야할 부분을 찾기가 힘들다는 점이 가장 컸습니다.
MVVM 디자인 패턴의 기본적인 구조를 그려놓은 이미지입니다 아무튼!!
Model은 어플리케이션 데이터를 가지고 있고, Model들은 보통 struct나 간단한 class로 구성됩니다,
View는 스크린에 보여지고, 컨트롤하는 구조입니다. 보통은 UIView로 만들어집니다
ViewModel은 model 정보를 view에 표시할수있는 값으로 변환합니다. 보통 Class로 만들어지기 때문에 reference로 사용될 수 있습니다.
이런 기본적인 구조를 파악했으니 코드가 어떻게 구성되는지 보면서 디자인 패턴에 대해 파악을 해볼까요?
아래의 예제 코드들은 참고에 있는 페이지에서 가져왔습니다.
import PlaygroundSupport import UIKit // MARK: - Model public class Pet { public enum Rarity { case common case uncommon case rare case veryRare } public let name: String public let birthday: Date public let rarity: Rarity public let image: UIImage public init(name: String, birthday: Date, rarity: Rarity, image: UIImage) { self.name = name self.birthday = birthday self.rarity = rarity self.image = image } }
Pet이라는 이름을 가진 Model에서 모든 pet에게 있을 name, birthday, rarity, image를 정의해줍니다.
name과 image의 경우에는 View에 바로 표시할 수 있지만, birthday와 rarity의 경우에는 View에서 바로 표시를 할 수 없기때문에 ViewModel에서 변형이 필요합니다.
// MARK: - ViewModel public class PetViewModel { // 1 private let pet: Pet private let calendar: Calendar public init(pet: Pet) { self.pet = pet self.calendar = Calendar(identifier: .gregorian) } // 2 public var name: String { return pet.name } public var image: UIImage { return pet.image } // 3 public var ageText: String { let today = calendar.startOfDay(for: Date()) let birthday = calendar.startOfDay(for: pet.birthday) let components = calendar.dateComponents([.year], from: birthday, to: today) let age = components.year! return "\(age) years old" } // 4 public var adoptionFeeText: String { switch pet.rarity { case .common: return "$50.00" case .uncommon: return "$75.00" case .rare: return "$150.00" case .veryRare: return "$500.00" } } }
ViewModel에서 위 코드에서 한 일들을 정리해보면
1. pet과 calenadar라는 두 개의 private property를 만들고 init에서 초기화를 시켜줍니다.
2. name과 image에 대해 pet 프로퍼티에서 받아옵니다.
3. pet의 birthday에서 받아와 계산을 하는 ageText를 선언합니다. retrun은 O years old로 계산됩니다.
4. 마지막으로 pet의 rarity에서 enum을 받아와 return되는 값을을 switch문으로 정의합니다.
이 다음으로는 UIView에서 ViewModel의 계산 방식을 가지고 사용자에게 보여줍니다.
// MARK: - View public class PetView: UIView { public let imageView: UIImageView public let nameLabel: UILabel public let ageLabel: UILabel public let adoptionFeeLabel: UILabel public override init(frame: CGRect) { var childFrame = CGRect(x: 0, y: 16, width: frame.width, height: frame.height / 2) imageView = UIImageView(frame: childFrame) imageView.contentMode = .scaleAspectFit childFrame.origin.y += childFrame.height + 16 childFrame.size.height = 30 nameLabel = UILabel(frame: childFrame) nameLabel.textAlignment = .center childFrame.origin.y += childFrame.height ageLabel = UILabel(frame: childFrame) ageLabel.textAlignment = .center childFrame.origin.y += childFrame.height adoptionFeeLabel = UILabel(frame: childFrame) adoptionFeeLabel.textAlignment = .center super.init(frame: frame) backgroundColor = .white addSubview(imageView) addSubview(nameLabel) addSubview(ageLabel) addSubview(adoptionFeeLabel) } @available(*, unavailable) public required init?(coder: NSCoder) { fatalError("init?(coder:) is not supported") } }
위 PetView에서는 사용자에게 보여줄 4가지의 subView를 만듭니다.
먼저 petView의 크기와 위치를 정의해주고
각각 4가지의 subView의 위치와 크기를 지정해줍니다.
// MARK: - Example // 1 let birthday = Date(timeIntervalSinceNow: (-2 * 86400 * 366)) let image = UIImage(named: "stuart")! let stuart = Pet(name: "Stuart", birthday: birthday, rarity: .veryRare, image: image) // 2 let viewModel = PetViewModel(pet: stuart) // 3 let frame = CGRect(x: 0, y: 0, width: 300, height: 420) let view = PetView(frame: frame) // 4 view.nameLabel.text = viewModel.name view.imageView.image = viewModel.image view.ageLabel.text = viewModel.ageText view.adoptionFeeLabel.text = viewModel.adoptionFeeText // 5 PlaygroundPage.current.liveView = view
마지막으로 사용자에게 보여지는 class부분입니다
해당하는 코드는 playground로 작성이 되어있기 때문에 보시는 여러분들이 Single View App에서 사용하실때에는 UIViewController에서 사용을 해주시면 되겠습니다.
사용한 방법은
1. stuart의 이름을 가진 Pet을 생성해주고
2. 생성한 stuart를 사용해 ViewModel을 생성해줍니다.
3. 미리 만들어둔 petView를 이용해 view를 생성해줍니다.
4. 위 코드에서 만들어둔 petViewModel을 이용해 view안에 넣어줍니다.
5. 마지막으로 사용자에게 보여주는 코드!!입니다.
위와 같은 구조로 사용되는 MVVM인데요 하나씩 정리를 하면서 보니깐
이해가 어렵지는 않네요.
Model을 만들어 구조를 잡아준 다음!
ViewModel에서 출력될 값들을 정의해주고,
View를 만들어 사용자에게 보여질 UI를 구성한 뒤
UIViewController에서 구조에 맞는 값들을 ViewModel에 넣어 View를 통해 보여주는 구조로 이해했습니다.
제가 이해한 MVVM디자인 패턴이 이상하거나 추가적으로 이해해야하는 개념이 있다면 얼마든지 댓글로 작성 혹은
junhyeon@5anniversary.dev
으로 답장해주시면 감사하겠습니다!!
참고
'Programing > iOS' 카테고리의 다른 글
[iOS] xib를 활용한 채팅 말풍선 만들기 (0) 2020.05.12 [iOS] 로딩 뷰 만들기 (0) 2020.05.10 [Swift] log (0) 2020.04.25 [iOS] detecting background => foreground in ViewController (0) 2020.04.24 [iOS] YPImagePicker 사용기 (0) 2020.03.26