SwiftUI : EnvironmentObject '뷰간에 데이터 공유'
앞 게시물 ObservedObject를 먼저 보고 오시는것을 추천합니다. 이번 게시물에서는 앞에서 썼던 코드를 재사용 합니다.
이번에는 EnvironmentObject
에 대해 알아보도록 하겠습니다.
EnvironmentObject
앱의 많은 뷰와 공유해야하는 데이터의 경우 SwiftUI
는 EnvironmentObject
속성 래퍼를 제공합니다.
이를 통해 필요한 곳 어디에서나 모델 데이터를 공유 할 수 있으며, 데이터가 변경 될 때 뷰가 자동으로 업데이트 된 상태로 유지됩니다.
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))
}
}
자, 이렇게 버튼을 만들어줬고 이제 ContentView
에 MyScoreView
를 추가해주겠습니다.
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 👇🏻