Skip to content

Conversation

@presto95
Copy link
Collaborator

노티피케이션 센터는 이벤트를 전달하는 방법 중 하나가 맞지만 MVVM에서는 각 요소가 서로 관계를 맺고 있으므로 델리게이트 패턴으로도 충분합니다. 노티피케이션 센터는 분명 유용한 패턴이지만, 유연한 만큼 설계를 해칠 수 있습니다.

뷰모델에는 프레젠테이션 로직이 위치합니다. 기존 코드는 뷰컨트롤러가 뷰모델로부터 Int 값을 받고 String으로 변환하여 label에 표시하고 있는데, 모델로부터 String 값을 받아 있는 그대로 label에 표시하도록 해야 합니다.

MVVM 설계 관련해서는 위 두 부분 말고는 잘 구현하셨다고 생각합니다.

코멘트에 세부설명.

Copy link
Collaborator Author

@presto95 presto95 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚀

// If any sessions were discarded while the application was not running, this will be called shortly after application:didFinishLaunchingWithOptions.
// Use this method to release any resources that were specific to the discarded scenes, as they will not return.
func application(_ application: UIApplication,
didDiscardSceneSessions sceneSessions: Set<UISceneSession>) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

불필요한 템플릿은 제거했습니다.


func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
// Override point for customization after application launch.
final class AppDelegate: UIResponder, UIApplicationDelegate {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

상속되지 않는 클래스에 대해 final class로 선언하는 것은 성능상 이점이 있습니다.

관련 내용은 검색해보시고, 접근 수준에 대해서 최대한 낮은 수준(private)부터 부여한 후 높여가는 것처럼, 클래스 선언시에도 final class로 선언하여 상속 불가능한 클래스로 만들고, 나중에 해당 클래스를 상속받을 필요가 있을 때 final을 풀어주는 식의 습관을 들이는 것을 추천드립니다.

let storyboard = UIStoryboard(name: "Main", bundle: nil)
guard let viewController = storyboard.instantiateInitialViewController() as? CalculatorViewController else { return }
viewController.setViewModel(CalculatorViewModel())
window?.rootViewController = viewController
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

뷰모델을 밖에서 넣어주기 위해 씬델리게이트에 이런 코드를 작성했습니다. 뷰모델을 밖에서 생성해서 넣어줬으니 DI를 한 것입니다.

다양한 뷰모델의 구현을 뷰컨트롤러에 넣고 싶다면 뷰모델을 추상화하고, 그것의 구현체를 넣어주면 될 것입니다.

enum OperationType: String {

enum Command: String {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

result, initialize가 연산자(OperationType)인지 생각해볼 필요가 있습니다. 이들은 연산자가 아니기 때문에 좀 더 넓은 의미의 Command라는 이름으로 바꿨습니다.


struct CalculatorModel {
private(set) var beforeValue: Int = 0
private(set) var nowValue: Int = 0 {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

기존에 getNowValue()나 getBeforeValue() 같은 메소드가 있었습니다. 이렇게 해줄 필요 없이 private(set) 접근 지정자를 붙여서 밖에서는 접근만 가능하도록 할 수 있습니다.

}
}

func setViewModel(_ viewModel: CalculatorViewModel) {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

뷰모델을 밖에서 만들어서 넣어주기 위해 이런 메소드를 하나 만들었습니다.

// Copyright © 2020 jun. All rights reserved.
//

protocol CalculatorViewModelBinding: class {
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

뷰모델에 대한 델리게이트 프로토콜을 선언했습니다. 이 프로토콜을 구현하는 객체는 뷰모델의 이벤트를 받아 적절한 처리를 하게 될 것입니다.

}
}

// MARK: - Output
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

뷰모델의 인풋과 아웃풋을 익스텐션을 활용하여 나누었습니다. 인풋 쪽에는 인풋만, 아웃풋 쪽에는 아웃풋만 위치하게 되었습니다.

func calculatorModelDidChangeNowValue(with value: Int) {
bindingObject?.calculatorDidFinishCalculation(with: String(value))
}
}
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

모델의 연산 결과로 Int 값을 받고, 프레젠테이션 로직에 따라 Int 값을 String 값으로 변환합니다.


extension CalculatorViewController: CalculatorViewModelBinding {
func calculatorDidFinishCalculation(with result: String) {
presentLabel.text = result
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

뷰는 뷰모델이 전달하는 값을 그대로 받아 UI를 갱신합니다.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants