이전 글에서 SwiftUI 에서 Data를 다루는 방법으로 @State, @Binding, @Environment 등의 프로퍼티래퍼를 사용한다고 했습니다.
따라서 이번에 @State, @Binding, @Environemnt 각각 Apple Developer 문서를 통해 알아보고자 합니다.
Apple Developer 문서에 표현된 State, Binding, Environment 정의 탐방
https://developer.apple.com/documentation/swiftui/state
https://developer.apple.com/documentation/swiftui/binding
https://developer.apple.com/documentation/swiftui/environment
UPDATE: 2023-10-26 15:28
https://developer.apple.com/documentation/swiftui/state
SwiftUI로 관리되는 read/write 가능한 property wrapper 타입
State<Value>
- App, Scene, View 등
view 계층구조 내
에서single source of truth로 값을 저장
하기 위하여기본값을 포함한 @State 속성을 적용한 property를 사용
하세요.- SwiftUI에서 제공하는 스토리지 관리와 충돌할 수 있는 memberwise initializer에서 state를 설정하는 것을 방지하려면 private로 사용하세요.
struct PlayButton: View {
@State private var isPlaying: Bool = false // Create the state.
var body: some View {
Button(isPlaying ? "Pause" : "Play") { // Read the state.
isPlaying.toggle() // Write the state.
}
}
}
- SwiftUI는 state property의 저장소를 관리합니다.
값이 변경되면 SwiftUI는 해당 property 값을통해 표시되는 view 계층구조의 일부분을 자동으로 업데이트
합니다.- state property는 기본값으로
wrapped 된 값인 wrappedValue를 사용
하지만,property를 직접 참조하면 Swift가 단축키 형태로 wrappedValue를 참조
합니다.- 위의 예시에서 state property인
isPlaying property의 직접 참조
하는 식으로wrappedValue를 read/write
하고 있습니다.
- state property 값에 접근해야 하는
view 계층구조의 가장 높은 view 내에서 private property로 선언
하세요.- 해당 state property 값을
하위 view 내에서 read/write
할 수 있도록binding을 통해 공유
하세요.- class와 같은
reference type을 state property
로 사용하고자 하는 경우 @StateObject를 사용하세요.
여기까지 Overview 내용을 살펴봤는데요, 이전에 Property Wrapper를 살펴본 것 처럼 wrappedValue
를 SwiftUI View 내에서 사용하는 형태입니다!
여기서 @State
프로퍼티래퍼를 사용하면 SwiftUI가 값의 변화를 인지하여 자동으로 이 property를 사용하는 view를 업데이트
해준다는 것이 큰 특징입니다!
(아마도 이 동작이 setter 내에서 publish 하고, getter 내에서 subscribe 할 것 같은 느낌이 드네요..!)
그러면 위에서 얘기한 것 처럼 하위 view로 전달하는 Binding 내용을 살펴보겠습니다!
- state property를 하위 view로 전달하면
SwiftUI는 값이 변경될 때 마다 하위 view를 업데이트
하지만,하위 view는 값을 수정할 수 없습니다.
하위 view에서 수정이 가능
하도록 하려면 @Binding을 통해 전달하면 됩니다.- state property 에서
projectedValue를 통해 binding
값을 얻을 수 있으며, 이 값은$property명
형식으로 사용이 가능합니다.
- 예를 들어
하위 view
내 button에서binding 값을 적용
하여 isPlaying값을 false로 수정
할 수 있습니다.- 그리고
상위 view
에서하위 view로 @Binding 값으로 전달
하기 위하여 상위 view에서state property를 선언
하고,$property명
형식으로state property에 대한 binding 값을 전달
합니다.
struct PlayButton: View {
@Binding var isPlaying: Bool // Play button now receives a binding.
var body: some View {
Button(isPlaying ? "Pause" : "Play") {
isPlaying.toggle()
}
}
}
struct PlayerView: View {
@State private var isPlaying: Bool = false // Create the state here now.
var body: some View {
VStack {
PlayButton(isPlaying: $isPlaying) // Pass a binding.
// ...
}
}
}
state property를 선언
시 항상기본값을 할당하여 초기값을 설정
하세요.state property
를view와 하위 view 내에서만 저장소
로 사용하세요.
여기까지 내용을 통해 @State 값을 하위 view로 @Binding으로 전달하기 위한 내용을 살펴봤습니다.
Prooperty Wrapper 에서 projectedValue를 사용할 수 있었는데, 여기서 @State 프로퍼티래퍼의 경우 projectedValue를 통해 binding값을 반환
한답니다!
그리고 이런 binding 값
을 통해 하위 view 내에서도 read와 더불어 write까지 가능
한 바인딩을 제공하고 있었어요!
@State
는 SwiftUI 내 view 계층구조 내에서 값을 저장
하는 용도의 property wrapperwrappedValue
: view를 표시하기 위한 데이터값
해당 property를 사용하는 view를 자동으로 업데이트
합니다.projectedValue
: binding 값
을 반환하위 view 내에서도 read와 더불어 write까지 가능
하도록 전달 가능$property명
형식으로 사용이렇게 @State와 @Binding을 통해 정보를 공유하면 thread-safe하게, 그리고 SwiftUI가 자동으로 변화를 감지하여 표시해준다는 점이 너무나 매력적인 것 같아요!
그러면 다음으로 @Binding 프로퍼티래퍼를 살펴보겠습니다
https://developer.apple.com/documentation/swiftui/binding
source of truth 정보를 read/write 가능한 property wrapper 타입
Binding<Value>
- binding을 통해
데이터를 저장하고 있는 property인 source of truth 정보
와데이터를 표시하고 변경하는 view
간의양방향 연결
을 만듭니다.
struct PlayButton: View {
@Binding var isPlaying: Bool
var body: some View {
Button(isPlaying ? "Pause" : "Play") {
isPlaying.toggle()
}
}
}
- 예를 들어, play와 pause를 전환할 수 있는 button은
@Binding
프로퍼티래퍼를 사용하여상위 view의 property에 대한 바인딩
을 통해 만들 수 있습니다.- 그리고 상위 view는
@State
프로퍼티래퍼를 사용하여 playing 상태를 지니는 property를 선언하여source of truth 값을 지닙니다.
struct PlayerView: View {
var episode: Episode
@State private var isPlaying: Bool = false
var body: some View {
VStack {
Text(episode.title)
.foregroundStyle(isPlaying ? .primary : .secondary)
PlayButton(isPlaying: $isPlaying) // Pass a binding.
}
}
}
- 상위 view인 PlayerView는 하위 view인 PlayButton을 초기화할 때 @State 프로퍼티래퍼의 binding 값인
Binding<Bool>
값을 전달합니다.- @State 프로퍼티래퍼 변수명 앞에
$
를 붙이면 projectedValue 값인binding 값이 반환
됩니다.- 그리고 binding으로 연결된 양방향 연결을 통해 PlayButton을 탭할 때 마다 isPlaying 상태가 변경됩니다.
- Observavle 프로토콜을 준수하는 type에 대한 바인딩을 만드려면 @Bindable 프로퍼티래퍼를 사용하세요.(iOS17+ 이상 가능)
여기까지가 @Binding 프로퍼티래퍼 내용이였습니다.
간단하죠?
그리고 projectedValue 내용을 간단하게 살펴보니, 아쉽게도 어떻게 양방향 바인딩이 구현되었는지까지는 내용이 없더라구요..ㅠㅠ
마지막으로 Binding이 없이도 모든 하위 view에서 접근가능한 데이터 형식인 @Environment 프로퍼태래퍼에 대해 알아보겠습니다!
https://developer.apple.com/documentation/swiftui/environment
view의 environment 값을 읽는 property wrapper 타입
Environment<Value>
- @Environment 프로퍼티래퍼를 사용하여 view의 environment 값을 읽을 수 있습니다.
- 선언시 EnvironmentValues key path 값을 사용하여 읽을 값을 나타냅니다.
- 예를 들어
\.colorScheme
key path를 통해현재 view의 color scheme 값을 읽을 수 있습니다.
@Environment(\.colorScheme) var colorScheme: ColorScheme
- 선언된
@Environment property의 wrappedValue
값으로 얻을 수 있으며, 조건에 따른 컨텐츠를 표시하면 됩니다.- 다른 property wrapper와 같이
property를 직접 참조
하여wrappedValue 값을 접근
합니다.if colorScheme == .dark { // Checks the wrapped value. DarkContent() } else { LightContent() }
- 값이 변경되면 SwiftUI는 해당 값으로 표시되는 view의 모든 부분을 자동으로 업데이트합니다.
- 예를 들어, 사용자가
Appearance setting 값을 변경
하면\.colorScheme
key path 값으로 선언한@Environment property를 사용하는 모든 view는 자동으로 업데이트
됩니다.
environment 프로퍼티래퍼
를 통해read는 가능
하지만 write는할 수 없습니다.- SwiftUI는 system setting에 따라 자동으로 업데이트 하고, 다른 environment 값에는 기본값을 제공합니다.
- 그리고 .environment(_:_:) view 모디파이어를 통해
사용자 정의 environment를 정의
하거나 기존 environment를 재정의할 수 있습니다.- 사용자 정의 environment를 생성시 EnvironmentKey 프로토콜을 참고하여 생성합니다.
여기까지가 @Environment 프로퍼티 내용이였습니다.
쉽게 말해 dark mode & light mode
와 같은 system setting
값들을 EnvironmentValues 형식으로 제공
하고 있으며 이러한 환경값을 읽고 변화를 감지해야 하는 경우 @Environment 프로퍼티래퍼
를 통해 값을 얻을 수 있답니다!
그리고 네트워킹
과 같은 사용자 정의 environment
또한 정의가 가능하며, 이때는 EnvironmentKey 프로토콜을 사용하여 정의
할 수 있습니다!
SwiftUI 에서 Model data를 전달하는데 사용되는 @State, @Binding, @Environment 프로퍼티래퍼를 살펴봤습니다
SwiftUI View를 표시하기위한 값은 wrappedValue를 사용
합니다.@State 프로퍼티래퍼
의 경우 write가 가능한 양방향 바인딩
이 필요할 때 $를 사용하여 projectedValue 값인 Binding을 사용
합니다.@Binding 프로퍼티래퍼
를 통해 Binding값을 받아 사용
합니다.@Environment 프로퍼티래퍼
를 통해 view 내에서 system setting과 같은 환경값을 읽고 변화를 감지
할 수 있습니다.사용자 정의 환경
도 설정 가능합니다.음.. 그런데 이런생각은 혹시 안드실까요?
viewModel
식으로 한번에 데이터들을 전달
할수는 없나?따라서 이와 관련하여 다음 글에서 @StateObject, @ObservedObject, @EnvironmentObject 프로퍼티래퍼를 살펴보겠습니다!
다음글: SwiftUI / @StateObject, @ObservedObject, @EnvironmentObject