MVC -> MVVM

Zion·2021년 11월 6일
1

서론

부쩍 MVVM에 관심이 많아졌다.
보면 볼수록 이렇게 좋은게 있다고?! 싶다.
Massive ViewController 는 이제 겪었으니 다음 레벨 히비고 해야한다.

참고자료

[Raywenderlich] iOS MVVM Tutorial: Refactoring from MVC

이 수업을 바탕으로 정리한 내용이다.
자 그럼 시작 ~

[Observer] Box

final class Box<T> {
  //1
//typealias Listener = (T) -> Void
  var listener: ((T) -> Void)?
  //2
  var value: T {
    didSet {
      listener?(value)
    }
  }
  //3
  init(_ value: T) {
    self.value = value
  }
  //4
  func bind(listener: ((T) -> Void)?) {
    self.listener = listener
    //e.
    listener?(value)
  }
}

Box 라는 클래스를 만들었다.

typealias 는 별명으로 이해하면 되겠다.

(T) -> Void = (T) -> () 랑 같다. Void = ()

그러나 return 값을 쓸 때는 Void 로 쓰는게 CONVENTION이라고 한다.

서론이 길었다. bind 함수에 대해서 설명해보겠다.

a. (T) → Void 라는 listener를 인자로 받는다.
b. 음음 listenerclosure네.
c. (T) 라는 인자를 받고 return값이 없는 closure이군.
d. 이 closureself.listener 로 update해주는군.
e. 그리고 이 listener( 인자로 받은. not self.listener)에다가는 가지고 있는 value를 넣어주는군

'e' 는 왜 하는걸까 ?

인자로 받아온 listener 에 기존에 가지고 있었던 value 를 알려주는것이다.

이 과정이 없다면 인자로 받아온 listener는 보시다시피 어디서 update되겠는가 ?

value안에 선언된 property observer인 didSet에서만 바뀐다. 이말은 즉 value의 '변화'가 있을 때만 listener가 작동한다는 말이 되시겠다.

e과정이 없게 된다면 value의 '초기값'을 모르게 된다.

Observer(여기선 Box) 객체를 다른 곳에서 만들었다고 가정했을 때, 다른 ViewController에서 해당 Observer 객체에 bind(ing)한다고 하면, ViewController에서는 현재 값을 모르니까 값이 전달이 안되서 보여지거나 하지 않겠지?

그렇담 사용 해볼까?

[ViewModel] class ViewModel

public class ViewModel {

	let name = Box(" ")
	let meetingDate = Box(" ")
	let profileImage = Box<UIImage?> = Box(nil)

	init() {
		holdMeeting()
	}

	func holdMeeting() {

		self.name.value = "Aiam"
		self.meetingDate.value = "2023-09-20"
		self.profileImage = UIImage(named: "AiamProfile")

	}
}

ViewModel class를 만들었다. init 에 holdMeeting함수를 넣어줬다.

[ViewController] class ViewController

class ViewController: UIViewController {
	
	let nameLabel = UILabel()
	let dateLabel = UILabel()
	let profileImageView = UIImageView()

	private let viewModel = ViewModel()

	override func viewDidLoad() {
		super.viewDidLoad()
		
		viewModel.name.bind { [weak self] name in
			self?.nameLabel.text = name
		}

		viewModel.meetingDate.bind { [weak self] date in
			self?.dateLabel.text = date
		}

		viewModel.profileImage.bind { [weak self] image in
			self?.profileImageView.image = image
		}

	}
}

viewModelproperty 에 해당하는 Layout-components들을 binding 시켰다.

우리는 viewModelholdMeeting() 함수를 지금 코드에서 불러줄 필요는 없다. viewModelinit되는 시점에 holdMeeting()을 넣어놨기 때문이다. (예시를 위한 함수지 재사용은 못하는 holdMeeting()함수이다...)

Box의 → bind 함수를 보자.

((T) -> Void)? 라는 closurelistener라는 인자로 받는다.

{ name in 
	self.nameLabel.text = date
}

작동은 앞에서 설명한 것처럼 되겠죠?

이해가 안간다면

→ 바로 위의 코드블록인 nameLabel.bind.closure 가 bind의 입력 인자인 listener가 된다

→ 해당 Box classlistener가 된다. (묶묶!)

→ 이 클로저에는 Box의 초기값인 value가 전달된다.

ViewController에서 viewModel에 해당하는 값들을 binding해 줬으니 이제 뚝딱뚝딱 기능( e.g 서버 통신.. )들은 viewModel에서 구현해 주면 된다.

profile
어제보다만 나아지는

0개의 댓글