[G1-Sync] Manual knowledge update

This commit is contained in:
Antigravity Agent
2026-05-09 21:08:02 +09:00
parent f0befc887a
commit 93ec7e9056
363 changed files with 68333 additions and 64 deletions
@@ -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]]