minuco
article thumbnail
Published 2023. 11. 28. 11:40
[TIL] MVVM(2) iOS/TIL

내용: View -> ViewModel, didSet + closure

 

⭐️ViewModel - View를 위한  (데이터 모델): 모델(데이터)을 과 로직을 가져야한다.


fetchMusic {...}의 결과가 success 가됐을때 self.music에 데이터가 담긴 다고 했다.

즉, var music: Music? 에 데이터가 담긴다는 소리고 이 담긴 시점(데이터가 변한 것)을 늘려야 한다.

어디에? 

View -> ViewModel 

 

사용자가 터치하는 순간

1) View(Controller + View)에서 start버튼이 눌림

@IBAction func startButtonTapped(_ sender: UIButton) {
        viewModel.handleButtonTapped()
}

2) Start Button이 눌렸다는 걸 ViewModel이 감지해야 함.

3) 어떻게????????

didSet과 closure를 사용(다른 방법들도 있음)

class MusicViewModel {
    
    var music: Music? {
        didSet {
            onCompleted(music)
        }
    }
    			...
    var onCompleted: (Music?) -> Void = { _ in }
    
    func handleButtonTapped() {
        fetchMusic { [weak self] result in
            switch result {
                case .success(let music):
                    self?.music = music
                case .failure(let error):
                    switch error {
                        ...
                    }
            }
        }
    }
    			...
}

 

Music Type의 music(속성감시자 (property observer))을 didSet을 사용해 onCompleted() 클로저에 넘겨준다.

그럼 Star버튼을 누를 때마다 didSet의 onCompleted()가 호출되겠지?

 

 ⭐️⭐️didSet은 프로퍼티의 값이 새로 할당될 때마다 호출이 된다.⭐️⭐️ 

즉, 새로운 값이 저장된 후에 호출이 되므로  새 값이 이미 프로퍼티에 저장된 상태이다.

 

그럼 생각해 보자

ViewModel의 music 프로퍼티에서 didSet은 music 프로퍼티가 새로운 ViewModel인스턴스가 업데이트될 때마다 onCompleted클로저를 호출하겠네? 

 

여기서 생각해 봐야 할 것.

 

⭐️ 하고자 하는 것은 Start 버튼이 눌리면 music 정보가 화면에 나오는 것.

그럼 실행되야 하는 절차는

1) Start 버튼이 눌렸을 때 네트워크 통신

2) 그 결괏값을 화면에 보여주기

 

이 두 가지의 상황을 로직으로 살펴보면

 

1) Start버튼이 눌렸을 때 handleButtonTapped() 메서드의 fetchMusic {... } 클로저가 싱행하면서 네트워크 통신의 성공 여부를 알 수 있다.
2) fetchMusic {... }가

그럼 onCompleted {... } 클로저가 실행되겠네?

 

근데, closure에 아무 로직도 없네? 

onCompleted 클로저는 기본적으로 비어있는 상태('{ _ in}')로 초기화했다. 이유는 View(Controller + View)에서 ViewModel의 인스턴스에 View를 변경해 주기 위함이다.

 

바로 요렇게 사용하려고.

self.viewModel.onCompleted = { [weak self] _ in
    DispatchQueue.main.async {
        self?.nameLabel.text = self?.viewModel.nameString
        self?.songLabel.text = self?.viewModel.songString
        self?.artistLabel.text = self?.viewModel.artistString
    }
}

 

 

근데 여기서 didSet만 사용해도 될꺼같은데 왜 클로저를?이라고 생각할 수도 있다. (내가 그럼..)

 

내가 공부해본 바로는 안된다.

 

why?

didSet만 사용하면 MVVM패턴에 어긋난다

MVVM 패턴에선 ViewModel은 View를 직접 업데이트하거나 View가 ViewModel을 관찰하도록 해야 한다.

didSet만 사용할 경우 ViModel과 View의 결합이 더욱 의존이 깊어진다.(의존을 안하게하려고 분히하는거 잖아?)

 

목표는 View가 ViewModel을 관찰하고 있다가 데이터가 변경되면(music가 변경되면) View를 업데이트해줘야 하는 것이다.

즉, ViewModel을 독립적으로 유지하는 것.

다시 말하자면 View(Controller+View)에서 ViewModel의 인스턴스객체인 viewModel을 사용해 구현한

1) Start버튼이 눌리면 2) 네트워크 통신이 이루어지고 성공하면 music(모델)에 새 값을 할당하고 3) didSet이 실행된다.
ViewModel.onCompleted() 클로저가 실행해 View를 업데이트한다.

 

여기서 View는 ViewModel의 속성감시자 didSet을 통해 ViewModel을 감시하는 것이다.

didSet은 뭐다?

 

⭐️⭐️didSet은 프로퍼티의 값이 새로 할당될 때마다 호출이 된다.⭐️⭐️ 

 

그리도 여기서 클로저를 사용하는 이유는 뭐다?

⭐️⭐️didSet만 사용할 경우 ViModel과 View의 결합이 더욱 의존이 깊어진다.

 


오늘은 여기까쥐.. 

'iOS > TIL' 카테고리의 다른 글

[TIL] Combine (1)  (1) 2024.01.30
[TIL] MVVM(3)  (0) 2023.12.05
[TIL] MVVM(1)  (2) 2023.11.25
[TIL] NaverMapAPI  (0) 2023.03.16
profile

minuco

@minuco

포스팅이 좋았다면 "좋아요❤️" 또는 "구독👍🏻" 해주세요!