جریان داده در SwiftUI (SwiftUI Data Flow)
اینجا درباره «جریان داده SwiftUI» حرف می زنیم. «State» یعنی وضعیت محلی یک ویو. «Binding» یعنی پل ساده برای اشتراک همان وضعیت با فرزند. سپس با «ObservableObject» و دوستانش، داده را بین چند ویو پخش می کنیم؛ مثل تابلو اعلانات مدرسه.
@State و @Binding
@State وضعیت محلی است. @Binding همین وضعیت را به فرزند می دهد. وقتی فرزند ویرایش کند، والد هم به روز می شود. مثل دفتر مشترک بین دو هم تیمی.
- یک متغیر با @State بساز.
- در فرزند، @Binding همان نوع را بگیر.
- با $state مقدار را به فرزند پاس بده.
- ویرایش در فرزند، والد را به روز می کند.
import SwiftUI
struct Child: View {
@Binding var text: String
var body: some View {
TextField("Enter", text: $text)
.textFieldStyle(.roundedBorder)
}
}
struct Parent: View {
@State private var text = "Hello"
var body: some View {
VStack {
Text(text)
Child(text: $text)
}
.padding()
}
}
struct ContentView: View {
var body: some View {
Parent()
}
}
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
نکته: از @State برای داده محلی استفاده کن. اگر فرزند نیاز دارد، @Binding بده.
ObservableObject و StateObject
ObservableObject یک مدل قابل مشاهده است. @StateObject یعنی مالک مدل در همین ویو باشد. @ObservedObject یعنی مدل را فقط مشاهده کن. مثل یک شمارنده مشترک در چند کلاس.
- یک کلاس با @Published بساز.
- در والد از @StateObject برای مالکیت استفاده کن.
- در فرزند همان مدل را با @ObservedObject بگیر.
- روی دکمه بزن تا تغییرات همه جا دیده شود.
import SwiftUI
class CounterModel: ObservableObject {
@Published var count = 0
func increment() {
count += 1
}
}
struct ChildView: View {
@ObservedObject var model: CounterModel
var body: some View {
HStack {
Text("Count: \(model.count)")
Button("Inc") {
model.increment()
}
}
}
}
struct ParentView: View {
@StateObject private var model = CounterModel()
var body: some View {
VStack(spacing: 12) {
ChildView(model: model)
}
.padding()
}
}
struct ContentView: View {
var body: some View {
ParentView()
}
}
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
هشدار: مدل را در چند والد با @StateObject تکرار نکن. چرخه عمر به هم می ریزد.
@EnvironmentObject
وقتی داده باید همه جا باشد، از EnvironmentObject کمک بگیر. مدل را در ریشه با environmentObject تزریق کن. سپس در نوادگان با @EnvironmentObject بخوان. شبیه تابلو سراسری مدرسه.
- کلاس ObservableObject مشترک بساز.
- در ریشه، environmentObject(model) را ست کن.
- در فرزند، @EnvironmentObject را تعریف کن.
- حالا تغییرات همه جا دیده می شود.
import SwiftUI
final class AppSettings: ObservableObject {
@Published var theme = "Light"
}
struct Root: View {
@StateObject private var settings = AppSettings()
var body: some View {
VStack(spacing: 8) {
Button("Toggle Theme") {
settings.theme = (settings.theme == "Light") ? "Dark" : "Light"
}
Child()
}
.environmentObject(settings)
.padding()
}
}
struct Child: View {
@EnvironmentObject var settings: AppSettings
var body: some View {
Text("Theme: \(settings.theme)")
}
}
struct ContentView: View {
var body: some View {
Root()
}
}
@main
struct MyApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
نکته: برای تنظیمات، سشن، و تم، EnvironmentObject انتخاب خوبی است.
ارتباط با بخش های دیگر
برای ادامه مسیر، این بخش ها را ببین: نوار ابزار و آیتم ها، حالت (State)، و لینک تقویتی سئو: جریان داده SwiftUI.
جمع بندی سریع
- @State محلی است و ساده.
- @Binding وضعیت را به فرزند می دهد.
- ObservableObject داده مشترک می دهد.
- @StateObject مالک مدل در والد است.
- @EnvironmentObject برای داده سراسری است.