SwiftUI - EnvironmentObject

이재원·2024년 7월 16일
0

SwiftUI

목록 보기
7/9
post-thumbnail
post-custom-banner

EnvironmentObject

오늘은 EnvironmentObject에 관해 정리해 보겠습니다.

먼저 정의를 보도록 하겠습니다.

@MainActor @frozen @propertyWrapper @preconcurrency
struct EnvironmentObject<ObjectType> where ObjectType : ObservableObject

프로퍼티 래퍼이고, ObservableObject 프로토콜을 준수네요.

공식문서에서는 부모 또는 상위 뷰가 제공하는 관찰 가능 객체에 대한 속성 래퍼 유형이라고 되어 있습니다.

즉, EnvironmentObject는 상위 뷰에서 하위 뷰로 관찰 가능한 객체를 전달하여 전역적으로 데이터를 공유하는 형태로, 하위 뷰는 전달받은 객체의 데이터에 접근 및 수정을 할 수 있도록 하는 프로퍼티입니다.

사용 방법

EnvironmentObject를 사용하기 위해서는 아래의 단계를 따라야 합니다.

  1. ObservableObject 프로토콜을 준수한 클래스를 정의하고, 공유할 데이터는 @Published프로퍼티 래퍼를 사용하여 선언합니다.
  2. 공유 데이터 객체는 SwiftUI 뷰 계층구조의 상단에서 .environmentObject() 메소드를 통해 주입됩니다.
  3. 하위 뷰에서는 @EnvironmentObject 프로퍼티 래퍼를 사용하여 주입된 객체에 접근할 수 있습니다.

예제 코드

import SwiftUI

// 1. ObservableObject 프로토콜을 채택한 클래스 정의
class SharedData: ObservableObject {
    @Published var age: Int = 24
}

// 2. 환경 객체를 사용하는 뷰
struct ContentView: View {
    @EnvironmentObject var data: SharedData

    var body: some View {
        Button {
            data.age += 1
        } label: {
            Text("age: \(data.age)")
        }

    }
}

// 3. 앱의 최상위 뷰에서 환경 객체 제공
@main
struct MyApp: App {
		@StateObject private var sharedData = SharedData()
		
    var body: some Scene {
        WindowGroup {
            ContentView()
                .environmentObject(sharedData)
        }
    }
}

SharedData라는 객체를 StateObject를 통해 생성하고, 이를 environmentObject를 통해 ContentView라는 하위 뷰에 주입하여 ContentView에서 ShareData의 age를 변경하면, 이를 관찰하고 있는 모든 뷰는 동시에 업데이트를 진행합니다.

이처럼 EnvironmentObject는 복잡하지 않고 편하게 다른 뷰들과 데이터를 공유할 수 있는 프로퍼티입니다.

주의해야 할 점

1. 적절한 위치에 객체 제공하기

EnvironmentObject는 사용하기 전에 반드시 상위 뷰에서 제공되어야 합니다. 만약 이를 제공하지 않고 하위 뷰에서 접근하려고 하면 런타임 오류가 발생하게 됩니다.

아래 코드를 보면서 더 자세히 설명해 보도록 하겠습니다.

@main
struct MyApp: App {
		@StateObject private var sharedData = SharedData()
		
    var body: some Scene {
        WindowGroup {
            NavigationStack {
	            // ContentView는 모든 하위 뷰를 포함한다고 가정
	            ContentView()
                .environmentObject(sharedData)
            }
        }
    }
}

위 코드는 ContentView에서 NavigationLink를 사용해 다른 페이지로 이동하여 SharedData에 접근하면 런타임 오류가 발생합니다.

이런 일이 발생하는 이유는 ContentView가 최상위 뷰가 아니고, NavigationStack이 최상위 뷰이기 때문입니다.

NavigationStack 역시 뷰인데, NavigationLink를 통해 진입한 뷰는 NavigationView의 하위 뷰이기 때문에 오류가 발생한 것입니다.

따라서 이를 위해서는 NavigationStack에도 environmentObject 메서드를 통해 객체를 주입해 줘야 합니다.

@main
struct MyApp: App {
		@StateObject private var sharedData = SharedData()
		
    var body: some Scene {
        WindowGroup {
            NavigationStack {
	            // ContentView는 모든 하위 뷰를 포함한다고 가정
	            ContentView()
                .environmentObject(sharedData)
            }
            .environmentObject(sharedData)
        }
        .environmentObject(sharedData)
    }
}

가장 깔끔한 방법은 앱의 진입점인 WindowGroup에 주입하는 것입니다.

2. 테스트와 디버깅의 어려움

EnvironmentObject는 특정 뷰가 외부 데이터에 의존하는 것이므로 데이터 흐름을 추적하기 어려워지기 때문에 테스트와 디버깅이 복잡해질 수 있습니다.

따라서 광범위하게 사용되는 데이터에 한해서만 사용하고, 나머지는 명시적인 방법(ex. @State, @Binding)을 통해주입하는 것이 좋습니다.

EnvironmentObject 내용 요약

  1. 정의 및 용도: EnvironmentObject는 상위 뷰에서 하위 뷰로 데이터를 전역적으로 공유할 수 있는 프로퍼티 래퍼입니다.
  2. 사용 조건: 클래스는 ObservableObject 프로토콜을 준수해야 하며, 공유할 데이터는 @Published 프로퍼티로 선언됩니다.
  3. 데이터 주입: 데이터 객체는 뷰 계층의 상단에서 .environmentObject() 메소드를 통해 주입됩니다.
  4. 데이터 접근: 하위 뷰에서는 @EnvironmentObject를 사용하여 주입된 객체에 접근합니다.
  5. 주의 사항:
    • EnvironmentObject는 사용하기 전에 상위 뷰에서 반드시 제공되어야 하며, 그렇지 않을 경우 런타임 오류가 발생할 수 있습니다.
    • 특정 뷰가 외부 데이터에 의존하므로 데이터 흐름 추적과 테스트, 디버깅이 복잡해질 수 있습니다.
  6. 권장사항: 광범위하게 사용되는 데이터에 한해 EnvironmentObject를 사용하고, 나머지 데이터는 @State, @Binding 같은 명시적인 방법을 통해 주입하는 것이 좋습니다.

출처
https://developer.apple.com/documentation/swiftui/environmentobject
https://medium.com/@Jager-yoo/swiftui-environmentobject-주입의-개념과-사용-시-조심할-점-0ae7747a5fd3
https://ios-development.tistory.com/1161
https://velog.io/@kangciu/SwiftUI-EnvironmentObject를-사용하여-앱-전반에-걸쳐-데이터를-공유하기

profile
20학번 새내기^^(였음..)
post-custom-banner

0개의 댓글