BMI 앱 리팩토링 MVC 패턴을 통해 진행
// 계산된 bmi 를 받아서 그리는 두번째 화면의 코드
// 참고로 bmi 의 타입은 BMI?이다.
// BMI 구조체에는 value, advice, matchColor 속성이 있다.
guard let bmi = bmi else { return }
//💡 어차피 양쪽 모두 옵셔널 이므로 옵셔널 체이닝 불필요한 경우(바로 밑 코드한줄)
bmiNumberLabel.text = String(bmi.value)
// bmi 자체는 옵셔널이 아니지만 String 화 하면서 옵셔널이 됨 => bmi.value 는 옵셔널이 아니지만 String(bmi.value)는 옵셔널임
// 밑에는 왼쪽은 옵셔널, 오른쪽은 옵셔널이 아닌코드이므로 담을 수 있음
adviceLabel.text = bmi.advice
bmiNumberLabel.backgroundColor = bmi.matchColor
강의를 들을때 헷갈려서 내가 이해한 바로 위의 코드를 토대로 옵셔널이 담기는 경우를 정리해보았다.
bmiNumberLabel.text = String(bmi.value)
=> bmi.value 는 옵셔널이 아니지만 String 생성자 혹은 "()"와 같은 string Interpolation으로 값을 변경하면 옵셔널 string 즉 String? 타입이 된다.
adviceLabel.text = bmi.advice
bmiNumberLabel.backgroundColor = bmi.matchColor
=> 다음의 경우 왼쪽은 옵셔널, 오른쪽은 옵셔널이 아닌코드이므로 담을 수 있음
대부분의 경우 private으로 모델의 속성은 접근제어를 걸어주고, 이때 만약 다른 화면에서(실제 controller 을 의미) 해당 속성의 접근이 필요시 메서드로 접근할 수 있도록 설계한다
//모델인 BMICalculatorManager구조체 내부
// private 으로 접근제어 함
// 외부에서 바로 접근 불가능
private var bmi: BMI?
// 메서드를 통해 bmi 접근 가능
mutating func getBMIResult(height: String, weight: String) -> BMI {
calculateBMI(height: height, weight: weight)
return bmi ?? BMI(value: 0.0, matchColor: .white, advice: "문제발생")
// 기타 등등 코드
}
이렇게 속성 자체는 private 선언을 해주고 대신 BMI를 리턴해주는 매서드를 작성함으로써 해당 매서드를 통해 간접적으로 bmi 를 받을 수 있도록 한다
해당 bmi를 받아쓰는 예시
//viewController 클래스 내부
// 참고 : 버튼이 눌리면 shouldPerformSegue => prepare 까지 자동호출됨
var bmiManager = BMICalculatorManager()
override func prepare(<for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "toSecondVC" {
let secondVC = segue.destination as! SecondViewController
guard let height = heightTextField.text,
let weight = weightTextField.text else { return }
// bmiManager.bmi => 불가능함 (private)
// bmi에 간접적으로 접근할 수 있는 매서드를 호출하고 결과값을 담는 역할 (직접적으로 접근하는 것은 private 제어걸려서 불가능)
secondVC.bmi = bmiManager.getBMIResult(height: height, weight: weight)
}
bmiManager.bmi => 불가능함 (private)
bmiManager.getBMIResult(height: height, weight: weight)
=> 다음과 같이 bmi를 리턴하는 모델 내부 메서드를 사용하여 간접적으로 bmi 값을 받아와야함