SWIFTUI/Grammar

SwiftUI : EnvironmentObject '뷰간에 데이터 공유'

서근 2021. 4. 5. 18:48
반응형

앞 게시물 ObservedObject를 먼저 보고 오시는것을 추천합니다. 이번 게시물에서는 앞에서 썼던 코드를 재사용 합니다.

 

이번에는 EnvironmentObject에 대해 알아보도록 하겠습니다.

 

EnvironmentObject

앱의 많은 뷰와 공유해야하는 데이터의 경우 SwiftUIEnvironmentObject속성 래퍼를 제공합니다.

 

이를 통해 필요한 곳 ​​어디에서나 모델 데이터를 공유 할 수 있으며, 데이터가 변경 될 때 뷰가 자동으로 업데이트 된 상태로 유지됩니다.

 

A View에서 데이터를 생성 후 👉🏻 B View 👉🏻 C View 로 전달 후, 최종적으로 👉🏻 D View로 전달하는 대신에, 특정 뷰에서 데이터를 생성 후 B,C,D가 전달 없이 공용으로 사용 가능 하게 할 수 있습니다.

 

그리고 EnvironmentObject를 사용하면 @ObservedObject를 사용하지 않아도 됩니다.

 

프로젝트를 함께 만들어보며 알아보도록 하겠습니다.

 

앞 게시물에서 사용했던 Score 코드를 그대로 가져와서 새로운 Score버튼을 만들고, 두 버튼중 어느것을 누르던 두 버튼에 모두 값이 증가하도록 만들어 보겠습니다.

 

새로운 SwiftUI 파일을 만들어 이름은 'MyScoreView'라고 정해주고, UserSetting에서 정의했던 score을 바인딩해서 가져오겠습니다. 그리고 score값이 증가하는 값을 만들고 아래와같이 코드를 작성해줍니다.

//MyScoreView


import SwiftUI

struct MyScoreView: View {
    
    //UserSetting의 score을 바인딩해서 가져옴
    @Binding var score: Int
    
    var body: some View {
        VStack {
            Text("\(self.score)")
            Button("클릭시 score 증가") {
                self.score += 1
            }
            .padding()
            .background(Color.orange)
            .foregroundColor(.black)
            
        }
        .padding()
        .background(Color.yellow)
        
        
        
    }
}

struct MyScoreView_Previews: PreviewProvider {
    static var previews: some View {
        //바인딩해온 값을 .constant값으로 설정
        MyScoreView(score: .constant(0))
    }
}

자, 이렇게 버튼을 만들어줬고 이제 ContentViewMyScoreView를 추가해주겠습니다.

 

MyScoreView()를 그냥 가져오면 당연히 컴파일 오류가 생깁니다. 이유는 바인딩해서 가져오는 값이기 때문에 매겨변수를 반드시 가져와야 합니다. 달러($)표시는 필수겠죠?

//ContentView 

import SwiftUI

struct ContentView: View {
    
    @ObservedObject var userSetting = UserSetting()
    
    var body: some View {
        VStack {
            Text("\(self.userSetting.score)")
                .font(.largeTitle)
            Button("클릭시 score 상승") {
                self.userSetting.score += 1
            }
            //구분선 추가
            Divider()
                .padding()
            
            //바인딩해서 가져오기때문에 $표시
            MyScoreView(score: self.$userSetting.score)
        }
    }
}

이렇게 까지 코드를 작성하면 아래와 같은 화면을 볼 수 있습니다.

그런데 이렇게 코드를 전달 전달 전달 하면 코드도 너무 길어지고 복잡해지지 않나요?? 😭

 

동일한 작업을 수행하는 훨씬 더 나은 접근이 있습니다. 이럴때 사용하는것이 바로 EnvironmentObject입니다. 위에서 공동으로 사용되었던 UserSetting의 'Score'을 공통 위치에 두어 둘다 엑세스 할 수 있게 하는거죠!

 

그렇다면 어떻게 사용해야 할까요?

첫번째

SceneDelegate.swift 파일을 클릭해서 들어가줍니다. ScenDelegate 내부에서 수정해야할 곳은 이곳입니다.

보시면 contentView = ContentView()로 지정되어있죠? 그렇기 때문에 프로젝트 내부에서 어떠한 Swift파일을 만들어도 ContentView를 엑세스 할 수 있는것입니다. :)

두번째

userSetting변수를 추가해줍니다.

//SceneDelegate

        let contentView = ContentView()

        // Use a UIHostingController as window root view controller.
        if let windowScene = scene as? UIWindowScene {
            let window = UIWindow(windowScene: windowScene)
            //userSetting을 contentView에 전달해줌
            let userSetting = UserSetting()
            window.rootViewController = UIHostingController(rootView: contentView)
            self.window = window
            window.makeKeyAndVisible()
        }
    }

userSetting을 이제 contentView에 전달 할 수 있지만 생성자가 아닙니다. 추가해야 할 코드가 더 있습니다.

window.rootViewController = UIHostingController(rootView: contentView)

rootView: contentView 바로 뒤에 EnvironmentObject를 반드시 추가해줘야 합니다.

let userSetting = UserSetting()
window.rootViewController = UIHostingController(rootView: contentView.environmentObject(userSetting))

이 작업이 완료되었으면 ContentView로 돌아가서 @ObservedObject객체를 @EnvironmentObject로 대체해줘야 합니다.

//ContentView

struct ContentView: View {
    //@ObservedObject var userSetting = UserSetting()를 아래와같이 수정
    @EnvironmentObject var userSetting: UserSetting
    
    var body: some View {
        VStack {
            Text("\(self.userSetting.score)")
                .font(.largeTitle)
            Button("클릭시 score 상승") {
                self.userSetting.score += 1
            }
        
            Divider()
                .padding()
            
            /*MyScoreView(score: self.$userSetting.score)를 아래와같이 수정
                MyScoreView의 바인딩을 Environment로 수정해줘야함*/
            MyScoreView()
        }
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        //.environmentObject(UserSetting()) 추가
        ContentView().environmentObject(UserSetting())
    }
}

MyScoreView@Binding을 삭제하고 EnvironmentObject로 수정하고 나머지 부분도 아래와같이 수정합니다.

//MyScoreView

import SwiftUI

struct MyScoreView: View {
    
    //before : @Binding var score: Int
    @EnvironmentObject var userSetting: UserSetting
    
    var body: some View {
        VStack {
            // before : Text("\(self.score)")
            Text("\(self.userSetting.score)")
            Button("클릭시 score 증가") {
                //before : self.score += 1
                self.userSetting.score += 1
            }
            .padding()
            .background(Color.orange)
            .foregroundColor(.black)
        }
        .padding()
        .background(Color.yellow)
        
    }
}

struct MyScoreView_Previews: PreviewProvider {
    static var previews: some View {
        //before : MyScoreView(score: .constant(0))
        MyScoreView().environmentObject(UserSetting())
    }
}

 

 

읽어주셔서 감사합니다🤟

 

본 게시글의 전체코드 GitHub 👇🏻

 

Seogun95/SwiftUI_EnvironmentObject_TUT

EnvironmentObject에 대해 알아봅시다. Contribute to Seogun95/SwiftUI_EnvironmentObject_TUT development by creating an account on GitHub.

github.com