티스토리 뷰
[SwiftUI] @Observable 매크로 (1) 에서 이어집니다.
[ 요약 ]
# 1.
예전에 뷰에서 썼던 프로퍼티 래퍼
@State, @Binding, @ObservedObject, @StateObject, @EnvironmentObject, @Enviroment
iOS 17+ 부터는 4개만 쓰면 됨
@State, @Binding, @Bindable (NEW), @Enviroment
# 2.
ObservableObject 가 Observale 매크로로 대체되고
뷰에서 프로퍼티 래퍼로 쓰던 것은 다음과 같이 매핑됨
✓ @ObservedObject ----> 안써도 됨
ㄴ 하지만 텍스트 필드처럼 Binding을 넘겨줘야하는 경우는 @Bindable
✓ @StateObject ----> @State
✓ @EnvironmentObject ----> @Environment
[1] @Bindable
Bindable은 이번에 새로나온 프로퍼티 래퍼입니다!! (iOS 17+)
mutable property 가 변화하기 전에 view가 binding을 기대하는 경우가 있습니다.
예를들어 TextField 같은 케이스 입니다. (초기화할때 Binding 타입을 넘겨줘야함)
아래의 코드에서는 컴파일 에러가 납니다.
@Observable final class Book {
var title = "Sample Book Title"
}
struct BookEditView: View {
var book: Book
var body: some View {
/*
컴파일 에러 발생!
Cannot convert value of type 'String' to expected argument type 'Binding<String>'
*/
TextField("Title", text: book.title)
.textFieldStyle(.roundedBorder)
}
}
model data 를 Bindable property wrapper 로 래핑한 후, binding 전달이 필요한 프로퍼티에
$ syntax 를 붙여주면 됩니다.
@Observable final class Book {
var title = "Sample Book Title"
}
struct BookEditView: View {
@Bindable var book: Book
var body: some View {
TextField("Title", text: $book.title)
.textFieldStyle(.roundedBorder)
}
}
또한 아래의 예제처럼 @Bindable variable 을 만들 수도 있습니다.
local, global variable 모두 가능합니다.
struct LibraryView: View {
@State private var books = [Book(), Book(), Book()]
var body: some View {
List(books) { book in
@Bindable var book = book
TextField("Title", text: $book.title)
}
}
}
[2] Observable 매크로 + State Property Wrapper
model data 에 대해 source of truth 를 만들려면
observable data model 을 private 으로 선언 후, State property wrapper 로 래핑합니다.
이렇게 하면 SwiftUI는 model instance 저장을 관리해줍니다.
SwiftUI가 BookView를 re-create 할때마다 book 을 연결해줍니다. (뷰에 모델 데이터에 대한 single source of truth 를 제공하는 셈)
struct BookView: View {
@State private var book = Book()
var body: some View {
Text(book.title)
}
}
위의 내용은
Observable 매크로 + State = @StateObject 라고 이해하면 됩니다~
또한 App 이나 Scene 같은 더 top-level 에서도 동일하게 사용할 수 있습니다.
@main
struct BookReaderApp: App {
@State private var library = Library()
var body: some Scene {
WindowGroup {
LibraryView()
.environment(library)
}
}
}
[3] Observable 매크로 + Enviroment Property Wrapper
view hierarchy 를 거쳐 모델을 share 하고 싶은 경우가 있습니다.
이때 view hierarchy 가 깊으면 전달 하기 힘드니까 view's environment 에 등록해두고 사용합니다.
기존에는 environmentObject modifier 를 사용했습니다. (ObservableObject, @EnvironmentObject 와 함께)
이제는 environment(_:_:) 또는 environment(_:) modifier 를 사용할 수 있습니다. (Observable 매크로, @Environment 와 함께)
3.1 key path를 이용한 방법
environment(_:_:) modifier 를 사용하기 전에
custom EnvironmentKey 를 만들어주고 EnvironmentValues 를 확장해줘야합니다.
예를들어 library 에 대한 코드 입니다.
extension EnvironmentValues {
var library: Library {
get { self[LibraryKey.self] }
set { self[LibraryKey.self] = newValue }
}
}
private struct LibraryKey: EnvironmentKey {
static var defaultValue: Library = Library()
}
그리고 LibaryView에 Library instance 에 대한 source of truth 를 추가해줍니다.
@main
struct BookReaderApp: App {
@State private var library = Library()
var body: some Scene {
WindowGroup {
LibraryView()
.environment(\.library, library)
}
}
}
library instance 를 서브뷰에서 접근하려면
각 뷰는 local variable 을 선언해줘야합니다. Environment property wrapper 로 감싸고 key path 를 전달합니다.
struct LibraryView: View {
@Environment(\.library) private var library
var body: some View {
// ...
}
}
# 3.2 data type을 이용한 방법
또 다른 방법으로
environment(_:) modifier 를 사용해서 model data 를 직접적으로 enviroment 에 저장하는 방법이 있습니다.
(3.1 처럼 custom environment value 를 별도로 정의하지 않아도 됩니다.)
@main
struct BookReaderApp: App {
@State private var library = Library()
var body: some Scene {
WindowGroup {
LibraryView()
.environment(library)
}
}
}
서브뷰에서 접근을 하려면 동일하게 local variable 을 선언해주고 Environment property wrapper 로 감싸줍니다.
KeyPath 대신 data type 을 전달합니다.
struct LibraryView: View {
@Environment(Library.self) private var library
var body: some View {
// ...
}
}
# 3.3 옵셔널
environment 에서 object를 읽으면 non-optional object 를 리턴하는 것이 디폴트입니다.
이는 현재 뷰 hierarchy 에서 non-optional instance 가 저장되어있다는 전제에 의합니다.
만약 가져오려는 object가 environment 에 없다면, SwiftUI 는 exception을 던집니다.
object가 environment에 있다는 보장이 없으면 optional 버전으로 불러올 수 있습니다.
그러면 SwiftUI는 exception 대신 nil을 리턴해줍니다.
@Environment(Library.self) private var library: Library?
'🍏 > SwiftUI + Combine' 카테고리의 다른 글
[SwiftUI] animation(_:body:) 로 돌발애니메이션 막기 (1) | 2023.11.30 |
---|---|
[SwiftUI] Drawing Curved Path with Animation (2) | 2023.10.29 |
[SwiftUI] @Observable 매크로 (1) (1) | 2023.07.31 |
[SwiftUI] GridItem - adaptive vs flexible (0) | 2023.06.09 |
[SwiftUI] task modifier (0) | 2023.03.08 |
- Total
- Today
- Yesterday
- Python Type Hint
- DRF APIException
- Flutter Text Gradient
- cocoapod
- Watch App for iOS App vs Watch App
- ipad multitasking
- Flutter getter setter
- Django Firebase Cloud Messaging
- 플러터 얼럿
- Sketch 누끼
- drf custom error
- Dart Factory
- Django Heroku Scheduler
- Flutter Clipboard
- PencilKit
- SerializerMethodField
- flutter build mode
- 구글 Geocoding API
- ribs
- 장고 Custom Management Command
- 플러터 싱글톤
- flutter dynamic link
- Django FCM
- flutter 앱 출시
- METAL
- flutter deep link
- 장고 URL querystring
- github actions
- Flutter 로딩
- Flutter Spacer
일 | 월 | 화 | 수 | 목 | 금 | 토 |
---|---|---|---|---|---|---|
1 | 2 | 3 | 4 | 5 | 6 | 7 |
8 | 9 | 10 | 11 | 12 | 13 | 14 |
15 | 16 | 17 | 18 | 19 | 20 | 21 |
22 | 23 | 24 | 25 | 26 | 27 | 28 |
29 | 30 | 31 |