[G1-Sync] Manual knowledge update
This commit is contained in:
@@ -0,0 +1,119 @@
|
||||
---
|
||||
id: ios-swiftui-state-property-wrappers
|
||||
title: SwiftUI State Property Wrappers — 어떤 걸 언제
|
||||
category: Coding
|
||||
status: draft
|
||||
source_trust_level: B
|
||||
verification_status: conceptual
|
||||
created_at: 2026-05-09
|
||||
updated_at: 2026-05-09
|
||||
tags: [ios, swiftui, state, property-wrapper, vibe-coding]
|
||||
tech_stack: { language: "Swift 5.7+ / SwiftUI 4+", applicable_to: ["iOS 16+", "macOS 13+"] }
|
||||
applied_in: []
|
||||
aliases: [@State, @Binding, @StateObject, @ObservedObject, @EnvironmentObject, @Observable]
|
||||
---
|
||||
|
||||
# SwiftUI State Property Wrappers
|
||||
|
||||
> 잘못 고르면 view identity 문제 + 의도치 않은 재생성. 핵심: **소유자 = `@State` / `@StateObject` / `@Observable`**, **차용자 = `@Binding` / `@ObservedObject` / `Bindable`**, **트리 전역 = `@Environment(Object)`**.
|
||||
|
||||
## 📖 핵심 개념
|
||||
- iOS 17+ 권장: `@Observable` 매크로 + `let`/`var` 일반 프로퍼티. 정밀 추적.
|
||||
- iOS 16-: `ObservableObject` + `@Published` + `@StateObject` / `@ObservedObject`.
|
||||
|
||||
## 💻 코드 패턴
|
||||
|
||||
### iOS 17+ — @Observable 권장
|
||||
```swift
|
||||
@Observable
|
||||
final class CartModel {
|
||||
var items: [Item] = []
|
||||
var total: Decimal = 0
|
||||
}
|
||||
|
||||
struct CartView: View {
|
||||
@State private var cart = CartModel() // 소유자
|
||||
var body: some View {
|
||||
Text("\(cart.total)")
|
||||
ChildView(cart: cart) // 그냥 전달
|
||||
}
|
||||
}
|
||||
|
||||
struct ChildView: View {
|
||||
let cart: CartModel // 차용자 — 별도 wrapper 불필요 (Observation 자동)
|
||||
@Bindable var cartBindable: CartModel // 양방향 binding 필요시
|
||||
var body: some View {
|
||||
TextField("name", text: $cartBindable.note)
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### iOS 16- — ObservableObject
|
||||
```swift
|
||||
final class CartModel: ObservableObject {
|
||||
@Published var items: [Item] = []
|
||||
}
|
||||
|
||||
struct CartView: View {
|
||||
@StateObject private var cart = CartModel() // 소유자
|
||||
var body: some View { ChildView(cart: cart) }
|
||||
}
|
||||
|
||||
struct ChildView: View {
|
||||
@ObservedObject var cart: CartModel // 차용자
|
||||
}
|
||||
```
|
||||
|
||||
### Environment 주입
|
||||
```swift
|
||||
struct AppView: View {
|
||||
@State private var user = UserModel()
|
||||
var body: some View {
|
||||
ContentView()
|
||||
.environment(user) // iOS 17+
|
||||
// .environmentObject(user) // iOS 16-
|
||||
}
|
||||
}
|
||||
|
||||
struct DeepChild: View {
|
||||
@Environment(UserModel.self) var user // iOS 17+
|
||||
// @EnvironmentObject var user: UserModel // iOS 16-
|
||||
}
|
||||
```
|
||||
|
||||
### Binding 양방향
|
||||
```swift
|
||||
struct Toggle: View {
|
||||
@Binding var isOn: Bool
|
||||
var body: some View { Button(isOn ? "on" : "off") { isOn.toggle() } }
|
||||
}
|
||||
|
||||
// 호출
|
||||
@State var on = false
|
||||
Toggle(isOn: $on)
|
||||
```
|
||||
|
||||
## 🤔 의사결정 기준
|
||||
| 역할 | iOS 17+ | iOS 16- |
|
||||
|---|---|---|
|
||||
| 값 타입 (Bool, String, struct) 소유 | `@State` | `@State` |
|
||||
| 클래스 모델 소유 | `@State` (Observable) | `@StateObject` |
|
||||
| 클래스 모델 차용 | 일반 let | `@ObservedObject` |
|
||||
| 양방향 binding 차용 | `@Bindable` | `@Binding` (값 타입만) |
|
||||
| 트리 전역 | `@Environment(Type.self)` | `@EnvironmentObject` |
|
||||
|
||||
## ❌ 안티패턴
|
||||
- **부모-자식 모두 `@StateObject`**: 자식 view 가 재생성되면 새 instance. 자식은 `@ObservedObject` 또는 받기.
|
||||
- **`@StateObject var x = makeBigThing()`**: init 매번 호출 (실제 사용 안 됨). 가벼운 init 권장.
|
||||
- **`@ObservedObject` 를 owner 처럼 사용**: parent rebuild 시 자식 instance 새로 생성 → state 잃음.
|
||||
- **iOS 17+ 코드에 `@Published`, `ObservableObject` 혼재**: 일관성. 한 모델은 한 패턴.
|
||||
- **EnvironmentObject 주입 안 하고 사용**: 런타임 crash. 모델 매크로 / preview 에서도 주입.
|
||||
- **값 타입 큰 struct 를 @State 로 자주 mutate**: copy 비용. 클래스 + Observable.
|
||||
|
||||
## 🤖 LLM 활용 힌트
|
||||
- iOS 17+ vs 16- 명시 후 코드 작성.
|
||||
- Preview 에서도 EnvironmentObject 주입 코드 같이.
|
||||
|
||||
## 🔗 관련 문서
|
||||
- [[iOS_SwiftUI_Lifecycle_View_Identity]]
|
||||
- [[iOS_Swift_Concurrency_async_await]]
|
||||
Reference in New Issue
Block a user