[Lecture 3] MVVM and the Swift type system


Generic

'don't care' Type이다.

자료구조를 생성할 때, 정수형이나 문자열 등 타입을 신경 쓰지 않고(don't care) 범용적으로 다루고 싶다면 이때 사용하는 타입이 Generic이다. Generic이 적용된 구조체로는 배열(Array)을 예시로 들 수 있다.

배열의 선언부를 보게 되면 다음과 같은 형태로 구성되어있다고 한다. 저기서 사용되는 ElementGeneric으로서 사용되는 데이터이다.

따라서 배열의 항목을 추가하는 append 메서드에서도 element를 사용하여 타입별로 메서드를 따로 구현하지 않아도 된다.

 

Functions as Type

swift를 접하면서 가장 생소했던 부분이다. swift는 함수를 일급객체로서 사용하는 함수형 프로그래밍 언어이다.

 

일급 객체라는 단어 또한 생소한데, 일급 객체(first-class object)란 다른 객체들에 일반적으로 적용 가능한 연산을 모두 지원하는 객체를 가리킨다. 일급 객체가 되기 위한 조건이 세 가지가 있다.

  • 객체를 변수와 상수에 할당할 수 있다.
  • 객체를 인자(argument)로 넘길 수 있다.
  • 객체를 반환값(return)으로 사용할 수 있다.

swift는 함수를 일급 객체로서 다루는 언어이다. 따라서 swift에서는 함수를 변수 및 상수에 할당, 함수를 다른 함수의 인자로 넘기거나, 함수의 반환값으로 함수를 리턴할 수 있다는 얘기이다. 



Lectrue 3에서는 Functions as Types라는 주제로 함수를 타입으로서 활용하며 변수 및 상수로 사용하는 것에 대해 이야기하였다.

(Int, Int) -> Bool 

두 개의 Int형 매개변수를 받아 Bool 타입의 결괏값을 리턴하는 함수타입

 

(Double) -> Void

Double형 매개변수 하나를 입력받고 리턴값이 없는 함수타입

 

() -> Array <String>

매개변수를 받지 않고 문자열 배열을 리턴하는 함수타입

 

이런 식으로 swift는 함수를 변수 및 상수에 타입으로 선언 및 할당이 가능하며, 함수 그 자체로서 동작하게 되어 다른 변수 및 상수에 결괏값을 전달할 수 있게 된다.

 

closure

"inlined function"

다음으로 함수의 인자로서 함수를 받는다는 것을 언급했다.

LazyVGrid(columns: [GridItem(.adaptive(minimum: 65))], content: {
	return ForEach(emojis[0..<emojiCount], id: \.self){ emoji in
		return CardView(content: emoji).aspectRatio(2/3,contentMode: .fit)
	}
})

Lecture 2에서 보았던 뷰 생성파트를 보면 content라는 인자에 중괄호를 열고 View를 반환하는 코드를 작성하는데, 해당 부분이 바로 함수의 인자로 함수를 받는다고 보면 된다. 단지 우리는 inlined fucntion 형태로 작성하였고, swift에서는 inlined functionclosure(폐쇄)라고 부른다. 어째서 closure라는 이름이 붙은 건지는 다음 Lecture에서 설명할 예정이라고 하니 잘 기억해 두자.

 

Type property

개발하는 데에 있어 전역변수는 사용하기에 편하지만, 의도치 않은 실수를 유발하기에 사용 시 리스크가 크다는 단점이 있다.

피치 못할 사정으로 전역변수를 사용해야 하는 경우가 생기는데, 이럴 때 전역변수로서 동작하지만 타입자체에 귀속시키는 방법이 Type property이다. 

 

class EmojiMemoryGame{
    let emojis = ["🚗","🚕","🚙","🚌","🚎","🏎️","🚓","🚑","🚒","🚐","🛻","🚚",
                  "🚛","🚜","🛵","🏍️","🛺","🚔","🚍","🚘","🚖","✈️","🚤","🛥️"]
    
    func createMemoryGame() -> MemoryGame<String>{
        MemoryGame<String>(number0fPairsOfCards: 4){ pairIndex in
            emojis[pairIndex]	//error!
        }
    }
    
    private var model: MemoryGame<String> = createMemoryGame()
    
    var cards : Array<MemoryGame<String>.Card> {
        return model.cards
    }
}

Lecture 1, 2에 걸쳐 작성된 memorize 데모에 MVVM 디자인 패턴을 적용하기 위한 작업 중 ViewModel 작성 부분이다.

이때 Model 생성 시에 상수로 선언된 emojis 배열을 사용하려 했으나, 오류가 발생한다. 이유는 바로 ViewModel의 인스턴스 생성 시 model이 생성되기 이전에 emojis 배열이 먼저 생성된다는 보장이 없기 때문이다.

 

emojis 배열을 ViewModel 상위로 생성하여 전역변수로서 선언을 해버리면 되겠지만, 개발론적인 시선에서 코드의 품질유지에 적합한 방법이 아니다.

 

이때 사용한 것이 타입 속성(Type Property)이다. 전역변수처럼 사용하고 싶은 속성에 static 키워드를 붙이면 해당 속성을 전역변수처럼 사용할 수 있다. 단, 소속된 타입을 명시해 주어야 하므로 어느 자료와 연관된 속성인지 단번에 유추가 가능하므로 코드의 유지보수에 용이하게 된다.

 

class EmojiMemoryGame{
    static let emojis = ["🚗","🚕","🚙","🚌","🚎","🏎️","🚓","🚑","🚒","🚐","🛻","🚚",
                  "🚛","🚜","🛵","🏍️","🛺","🚔","🚍","🚘","🚖","✈️","🚤","🛥️"]
    
    static func createMemoryGame() -> MemoryGame<String>{
        MemoryGame<String>(number0fPairsOfCards: 4){ pairIndex in
            EmojiMemoryGame.emojis[pairIndex]
        }
    }
    
    private var model: MemoryGame<String> = EmojiMemoryGame.createMemoryGame()
    
    var cards : Array<MemoryGame<String>.Card> {
        return model.cards
    }
}

 

즉 속성자체를 인스턴스가 아닌 타입 그 자체에 저장하는 것과 같다 해서 타입 속성(Type property)라고 부른다. 해당 개념을 알기 전부터 사실 나도 모르게 자주 쓰고 있는 기능이다. 대표적인 예시로 Int.max 와 같이 인스턴스 생성 없이 바로 접근하여 사용되는 속성들이다.

 

참고 영상

https://www.youtube.com/watch?v=--qKOhdgJAs&list=PLpGHT1n4-mAsxuRxVPv7kj4-dQYoC3VVu&index=9 

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

[Assignment2] More Memorize  (1) 2023.10.16
[Lecture 4] Memorize Game Logic - 1  (0) 2023.08.18
[Lecture 3] MVVM and the Swift type system - 1  (0) 2023.07.08
[Assignment 1] Memorize  (0) 2023.07.06
[Lecture 2] Learning more about SwiftUI  (0) 2023.07.05

+ Recent posts