수업 내용 정리.
https://www.youtube.com/watch?v=W1ymVx6dmvc&t=984s
struct과 class 차이점

초기화 변경 과정과 후행 클로저(Trailing Closures)
뷰모델에서 모델을 가져오기 위해 초기화 설정을 위해 private var model: MemoryGame<String> = MemoryGame<String>() 인수없이 생성하려고 하면 에러가 발생. MemoryGame은 struct 구조인데, 여기안의 cards는 초기화 되지 않은 배열 타입의 변수가 있음(제네릭). 그러나 이 MemoryGame을 쓰려고 하는 뷰모델에서는 인수를 넣어서 초기화 해줘야 함.
이 때 모델에서 필요한 인수가 아닌 뷰모델 자체적으로 생성하는데 필요한 정보 numberOfPairsOfCards: 4 를 넣어줌, 모델에 가서도 매개변수를 만들어줌.
모델을 초기화하기 위해 마지막 인수는 클로저로 사용
private var model = MemoryGame(
numberOfPairsOfCards: 4,
cardContentFactory: (forPairAtInext index: Int) -> String {
return ["👻", "🎃", "🕷️", "😈", "💀", "🕸️", "🧙♀️", "🙀", "👹", "😱", "☠️", "🍭"][index]
}
)
cardContentFactory 이렇게 바꿔줌 ->>
cardContentFactory: { (index: Int) -> String in
return ["👻", "🎃", "🕷️", "😈", "💀", "🕸️", "🧙♀️", "🙀", "👹", "😱", "☠️", "🍭"][index]
}
타입 추론으로 Int, String 삭제 → 더 간결하게 만듬
cardContentFactory: { index in
return ["👻", "🎃", "🕷️", "😈", "💀", "🕸️", "🧙♀️", "🙀", "👹", "😱", "☠️", "🍭"][index]
}
마지막 인수가 함수라면 마지막 인수를 () 밖으로 따로 빼어 후행 클로저(Trailing Closures))로 만들 수 있음
private var model = MemoryGame(numberOfPairsOfCards: 4){ index in
return ["👻", "🎃", "🕷️", "😈", "💀", "🕸️", "🧙♀️", "🙀", "👹", "😱", "☠️", "🍭"][index]
}
Static var
let emojis = ["👻", "🎃", "🕷️", "😈", "💀", "🕸️", "🧙♀️", "🙀", "👹", "😱", "☠️", "🍭"]
emojis라는 변수를 만들어 배열을 여기다 넣으려고 하지만 에러 발생!
Cannot use instance member 'emojis' within property initializer; property initializers run before 'self' is available
속성 초기화할 때 순서가 결정되지 않음. 언제 초기활 될지 모르기 때문에 let의 변수를 다른 초기화를 할 때 사용할 수 없음 → 더 빨리 초기화하는 static 을 사용
변수가 초기화 되기전에 먼저 초기화 되도록 → 이것을 정적변수라 한다
private static let emojis = ["👻", "🎃", "🕷️", "😈", "💀", "🕸️", "🧙♀️", "🙀", "👹", "😱", "☠️", "🍭"]
모델 - 뷰모델 - 뷰 연결하는 방법

mutating
모델에 있는 cards를 변경하려고 하는 메서드를 만들었는데 에러 발생.
Cannot use mutating member on immutable value: 'self' is immutable
클래스는 참조 타입이고 구조체는 값 타입인데
값 타인인 구조체 안의 프로퍼티를 변경하는 메서드를 실행할 경우 에러 발생
이 때 메서드 앞에 mutating 키워드를 입력하여 준다.
mutating func shuffle() {
cards.shuffle()
}
전체코드
MemoryGame
// 어떤 타입이 들어올지 몰라 제네릭 타입으로 설정. 뷰모델에서 어떤 타입인지 결정.
struct MemoryGame<CardContent> {
//외부에서 접근하지 못하도록 private
private(set) var cards: Array<Card>
// struct은 초기화 설정 하지 않아도 되지만, CardContent타입의 종류와 카드의 갯수를 받아 올 수 있도록 설정.
init(numberOfPairsOfCards: Int, cardContentFactory: (Int) -> CardContent) {
//카드 타입의 배열을 추론함
cards = []
// add numberOfPairsOfCards x 2 cards
// for in
for pairIndex in 0..<max(2,numberOfPairsOfCards) {
//content: CardContent 타입 추론 가능하여 삭제하여도 됨
let content = cardContentFactory(pairIndex)
cards.append(Card(content: content))
cards.append(Card(content: content))
}
}
func choose(_ card: Card) {
}
mutating func shuffle() {
cards.shuffle()
}
struct Card {
var isFaceUp: Bool = true
var isMatched: Bool = false
var content: CardContent
}
}
EmojiMemoryGame
// 뷰모델은 class로 만들어주는데, 여러 뷰에서 공유할 수 있도록 하기 위해. heap data
class EmojiMemoryGame: ObservableObject {
//여기도 private, 초기화 진행을 위해 모델을 만들기 위해 필요한 변수를 static 변수로 만들어줌.
// 변수 model에 초기화하기 위해 쓰는 변수에 static을 해주면 그 파일 내에 가장 먼저 초기화됨. -> 그 후 변수 model 초기화때 쓰임.
private static let emojis = ["👻", "🎃", "🕷️", "😈", "💀", "🕸️", "🧙♀️", "🙀", "👹", "😱", "☠️", "🍭"]
// private, static
private static func CreateMemoryGame()-> MemoryGame<String> {
return MemoryGame(numberOfPairsOfCards: 8){ index in
// 배열 인덱스 범위를 벗어난 경우를 대비하여 if..else 구문
if emojis.indices.contains(index) {
return emojis[index]
} else {
return "⁉️"
}
}
}
// private 외부에 노출되지 않으면서, @Published - 변화가 있으면 감지하여 뷰에게 알려줌.
@Published private var model = CreateMemoryGame()
var cards: Array<MemoryGame<String>.Card> {
return model.cards
}
// MARK - Intents
func shuffle() {
model.shuffle()
}
func choose(_ card: MemoryGame<String>.Card) {
model.choose(card)
}
}
'Swift & SwiftUI' 카테고리의 다른 글
| Simple Task App (0) | 2023.12.21 |
|---|---|
| 100days of SwiftUi - project 7 (0) | 2023.12.09 |
| Networking for beginners (0) | 2023.10.11 |
| Optional Binding, Chaining (0) | 2023.10.09 |
| 사용자의 위치정보 가져오기 (0) | 2023.10.05 |