[SwiftUI Bootcamp](중요반복) beginner11

Woozoo·2023년 1월 18일
0

[SwiftUI]

목록 보기
11/26

@StateObject & @ObservableObject

  • [커맨드 옵션p]누르면 프리뷰 새로고침 됨!

앱이 복잡해질수록 로직을 분산해서 관리하면 좋음

struct FruitModel: Identifiable {
    let id = UUID()
    let name: String
    let count: Int
}

struct ViewModelBootcamp: View {
    
    @State var fruitArray: [FruitModel] = []
    var body: some View {
        NavigationView {
            List {
                ForEach(fruitArray) { fruit in
                    HStack {
                        Text("\(fruit.count)")
                            .foregroundColor(.red)
                        Text(fruit.name)
                            .font(.headline).bold()
                    }
                }
                .onDelete(perform: deleteFruit)
            }
            
            .listStyle(.grouped)
            .navigationTitle("Fruit List")
            .onAppear {
                getFruits()
            }
        }
    }
    
    func getFruits() {
        let fruit1 = FruitModel(name: "Orange", count: 1)
        let fruit2 = FruitModel(name: "Banana", count: 2)
        let fruit3 = FruitModel(name: "Watermelon", count: 88)
        fruitArray.append(fruit1)
        fruitArray.append(fruit2)
        fruitArray.append(fruit3)
    }
    
    func deleteFruit(index: IndexSet) {
        fruitArray.remove(atOffsets: index)
    }
}

요렇게 작성했을 때 이 로직들을 분리해보자

먼저 getFruits와 deleteFruit은 모델(데이터)과 관련된 로직임


FruitViewModel이라는 클래스를 새로 만들어줌
근데 @State가 아니라 @Published를 붙여줬다
클래스에서는 @Published를 써야함
(hey something's changed. you might have to update something)
용도는 @State랑 비슷함!

기존에 있던 @State는 코멘트아웃해주고
fruitViewModel이라는 새로운 변수를 만들어줌

기존에 있던 로직들을 ViewModel로 옮기자


그럼 이렇게 깔끔하게 정리가 가능해졌다
그런데 여기서 문제가 발생함
view가 제대로 안나오고 있음
어떤 게 문제일까?

viewModel에 프로퍼티 래퍼인 @ObservedObject를 붙여서 데이터의 변경사항들이 있을 때 관찰되게 해줘야함


관찰이될라면 관찰이 가능해야겠죠
class viewModel에도 프로토콜 ObservableObject를 채택해줘야한다

그런데!!
@ObservedObject 같은 경우 포함된 뷰가 reload되게 되면 같이 refresh됨!!
이럴 때 해당 뷰모델은 유지되야할 필요가 있는데

@StateObject를 붙여서 persist되게 해준다

앱에서 처음 만들어지는 object다 -> @StateObject
넘겨 받아서 사용되는 object다 -> @ObservedObject


navigationBarItems 옛날 표현이긴 한데 그냥 사용했다
navigationLink를 넣고 싶었는데 link같은 경우 navigationView 안에 있어야해서
지금처럼 navigationItem으로 다른 뷰를 띄우고 싶을 때 어떻게 해줄 지 달리 방법이 안보임
HIG에서 저렇게 사용하지는 말라고 얘기하는 거 같기도 하고

이대로 뷰를 왔다갔다 할 경우에
.onAppear가 계속 실행되서 array가 계속 추가됨

ViewModel에서 init될 때 array가 추가되는 걸로 바꾸자

이제 새로 만든 뷰에서 전의 패런트 뷰의 데이터를 가지고 오고 싶다
어떻게할까?


@ObservedObject로 fruitViewModel을 가져와주면 됨


그리고 패런트뷰에서 fruitViewModel 그대로 넘겨주면 사용 가능!


@EnvironmentObject


ViewModel을 새로 만들어줬다
그리고 data를 가져오는 메소드를 작성했는데
append 메소드 중에 여러가지의 element를 가져올 수 있는 방법이 있다!

contentsOf 프로퍼티가 붙은 메소드를 사용해서 이렇게 시퀀스를 넘겨줄 수도 있음


디테일뷰를 만들어줬다


그리고 NavigationLink의 목적지가 될 뷰를 디테일뷰로 설정하고,
item들을 넘겨주면?!

선택된 리스트의 item이 다음뷰에서 전달되게 된다

뷰 하나 더 만들어보자

오케이!


디테일뷰에서 NavigationLink로 FinalView가 뜨게 해줬다.

여기서 잠깐!



위의 파이널뷰에서 처음 메인뷰에 있던 데이터를 가져오고 싶을 때 어떻게 해야할까?
뷰에 계속 중첩해서 binding을 하는 건 상당히 비효율적이지 않나?!
데이터를 쓰지 않는 뷰도 있으니!!

요렇게 계속 중첩해야되잖음

이럴 때 사용하는 게 environmentObject


메인뷰에서 상단에 .environmentObject를 붙이고 환경객체로 사용할 ViewModel을 넣어줌

지금 상단이라고 표현했는데 NavigationView 안에 포함되게 되는 모든 뷰들은
viewModel에 접근이 가능해진다

이제 두번째 뷰에선 @ObservedObject가 필요 없어졌다 (지워줌)

세번째 뷰에선 @ObservedObject로 선언했던 프로퍼티 래퍼를 바꿔주면 됨

@EnvironmentObject 등장!

그럼 끝임.
짱 편하쥬
.environmentObject에 담겼던 데이터를
하위뷰에서 @EnvironmentObject로 선언하는 것만으로도 그 데이터를 불러올 수 있어졌다!

profile
우주형

0개의 댓글