SwiftUI - Stanford Lecture 3-1 : MVVM and the Swift type system

SeBin·2023년 4월 27일
0

SwiftUI Stanford

목록 보기
3/4
post-thumbnail

Architecture

MVVM Design Paradigm

SwiftUI를 사용하기 위해선 MVVM (Model View ViewModel) 설계 방식을 적용해야 한다.

UIKit은 MVC (Model View Controller)를 사용한다고 한다.

MVVM과 MVC의 공통점

  • User Interface Code (View)와 Backend Logic (Model)을 분리함

Model

  • Model Swift Code는 SwiftUI를 import 하지 않는다.
  • data와 logic은 전부 Model에 구현되며 Model에만 존재해야 한다.
    Card Game을 구현할 때 모든 Card들은 data이며 카드를 선택할 때의 의사결정은 logic이다.

View

  • Card의 상태 변동을 어떻게 View와 연동시킬지 걱정하지 않아도 된다.
    화면에 보이는 모든 카드들은 Model에 있는 카드와 동일하기 때문이다.
  • 따라서 View는 'Stateless'하다.

@State 변수는 UI 상의 일시적인 변화를 가능하게 만드는 용도로 사용된다. 게임에 관련된 모든 로직은 반드시 Model에만 존재해야 한다.

  • User interface가 생성되는 시점은 system이 body var 값을 받아오는 시점이다.

  • 이러한 코딩 방식을 'declarative'라고 부른다.
    쓰거나 읽기 쉽고 더 안정적이고 증명 가능한 코드를 구현할 수 있다.

  • Model에 대한 변동 결과로써 실제 변경될 수 있는 body var만 요청해야 한다.

  • View는 항상 Model의 변동 사항에 자동으로 효율적으로 반응한다.
    SwiftUI가 Reactive하다고 하는 이유이다.

ViewModel

View를 Model에 바인딩하여 Model의 변경 사항으로 인해 View가 반응(React)하여 다시 Build 되도록 한다.

  • Model과 View 사이의 Interpriter(해석) 역할을 할 수 있다.
    SQL 데이터 베이스 모델의 데이터를 View에 배열로 넘기거나 Int를 Float으로 넘기거나 등등 View가 선호하는 자료형으로 변환하여 넘겨줄 수 있다.

  • ViewModel에는 View 구조체에 대한 정보가 담긴 포인터나 데이터 구조가 존재하지 않는다. 왜냐하면 Model에 접근하기 위해 어떤 View에도 연결할 것을 원치 않기 때문이다.

그래서 MVVM 메커니즘은

  1. ViewModel은 모델의 변경사항을 지속적으로 확인한다.

  2. 변동사항을 감지하면 즉시 변경된 사항을 전체에 공표한다.

  3. ViewModel의 발표를 구독하는 View가 어느 이벤트를 확인하면 View에 body var를 요청하고 다시 그린다.

  • ViewModel은 사용자의 의도를 Model에 대한 특정한 수정으로 변환한 후 Model에 반영한다.

Type System

Struct

ContentView, CardView와 같은 UI, Memorize 게임에서의 Model이 예시

Class

Object-Oriented Programming을 구현할 수 있으며 MVVM에서의 View Model이 예시

Struct와 Class의 공통점

Struct나 Class 내부에 함수, 변수를 선언할 때 구문이 동일하다.

var isFaceUp: Bool // 일반적인 변수 선언의 예
var body: some View {
	return Text("Hello World") // 계산의 결과를 할당하는 변수 선언의 예
}

func multiply (operand: Int, by: Int) -> Int {
	return operand * by // 함수 선언의 예, 인자에 라벨이 붙는다
}
multiply(operand: 5, by: 6)

func multiply (_ operand: Int, by: Int) -> Int {
	return operand * by // 함수 선언의 예, 외부와 내부의 라벨을 구분지을 수 있다
}
multiply(5, by: 6)

구조체의 내부값을 초기화 시켜주는 특별한 함수 initializer를 가질 수 있다. init은 새로운 struct나 class가 정의될 때 호출되는 함수이다.

struct RoundedRectangle {
		init(cornerRadius: CGFloat) {
        //initialize this rectangle with that cornerRadius
    }
    	init(cornerSize: CGFloat) {
        //initialize this rectangle with that cornerSize
    }
}

Struct와 Class의 차이점

  • Struct
    - Value type
    • Copied when passed or assigned

  • Class
    - Reference type
    • Passed around via pointers (Heap)

Generics ("Dont' Care" Type)

타입에 대해 신경쓰지 않도록 도와준다. Array가 대표적인 예시
Array는 Swift가 제공하는 기본 자료형 뿐만 아니라 사용자 지정 자료형도 자유롭게 담을 수 있다.

<Element>에는 어떠한 변수가 들어가도 상관없다. 이와 같은 역할을 하는 인자를 Type Parameter라고 한다.

struct Array<Element> {
		...
	func append(_ element: Element) {...}
}

var a = Array<int>()
a.append(5)
a.append(22)

Function

First Class Type. 함수형 프로그래밍을 지원하는 기본 사항에 해당

(Int, Int) -> Bool	// 2개의 Int를 받고 Bool를 return
(Double) -> Void	// Double을 받고 아무것도 return하지 않음
() -> Array<String>	// 아무런 argument(인자)를 받지 않고 String을 갖고있는 Array를 return
() -> Void	// 아무런 argument를 받지 않고 아무것도 return하지 않음 (Common case), .onTapGesture

var foo: (Double) -> Void	// 변수의 타입으로 함수 사용하여 정의할 수 있다.
func doSomething(what: () -> Bool)	// 함수를 인자로 넘길 수 있다. 함수형 프로그래밍 필수 방식

함수를 정의하고 사용하는 방법

var operation: (Double) -> Double
// Double을 받아서 Double을 return하는 함수 타입 정의

func square(operand: Double) -> Double {
		return operand * operand
}
// 일반적으로 아는 함수의 정의

operation = square
let result1 = operation(4)
// 함수 타입의 변수에 함수를 담고 사용할 수 있다.

중괄호 {}를 사용하여 inline형태로 함수를 구현하는 경우를 Closure라고 한다.

0개의 댓글