@ObservedObject — یک property wrapper در SwiftUI است که به View اجازه میدهد تغییرات ObservableObject ایجاد شده در جای دیگری از سلسلهمراتب را مشاهده کند. بر خلاف @StateObject، @ObservedObject شیء ایجاد نمیکند — فقط در publisher objectWillChange آن مشترک میشود و هنگام بهروزرسانی ویژگیهای @Published، View را دوباره ترسیم میکند. این @ObservedObject را انتخاب درستی برای Viewهای فرزندی میکند که دادهها را از والد از طریق مقداردهنده اولیه دریافت میکنند. به گفته مقاله Paul Hudson — Hacking with Swift (2025)، معماری معمول یک برنامه SwiftUI به این صورت ساخته میشود: View ریشه از @StateObject برای ایجاد view model استفاده میکند و همه Viewهای فرزند آن را از طریق @ObservedObject دریافت میکنند که یک منبع حقیقت واحد بدون تکراری دادهها را تضمین میکند.
نکات اصلی
@ObservedObject — یک property wrapper است که View را در تغییرات ObservableObject مشترک میکند. وقتی شیء مشخص شده با @ObservedObject هر یک از ویژگیهای خود که با @Published اعلام شدهاند را تغییر میدهد، SwiftUI به طور خودکار View را دوباره ترسیم میکند. @ObservedObject شیء ایجاد نمیکند — فقط بین یک نمونه موجود ObservableObject و View که باید به تغییرات آن واکنش نشان دهد، ارتباط برقرار میکند.
تفاوت کلیدی بین @ObservedObject و @StateObject — مالکیت است. @ObservedObject فرض میکند که شیء در جایی بالاتر در سلسلهمراتب View ایجاد و ذخیره شده است. View فرزند یک ارجاع به این شیء را از طریق مقداردهنده اولیه دریافت میکند و صرفاً آن را مشاهده میکند. اگر View فرزند دوباره ایجاد شود، همان ارجاع را از والد دریافت میکند — دادهها از دست نخواهند رفت.
به گفته Apple Developer Documentation — SwiftUI (2025)، @ObservedObject از iOS 13 در دسترس است، که آن را تنها گزینه برای مشاهده ObservableObject در پروژههایی میکند که از نسخههای قدیمی iOS پشتیبانی میکنند. در iOS 14+ برای ایجاد اشیاء @StateObject ترجیح داده میشود، اما @ObservedObject برای انتقال اشیاء موجود همچنان مرتبط است.
مکانیزم @ObservedObject بر اساس پروتکل ObservableObject از framework Combine است. هر کلاسی که با ObservableObject مطابقت دارد، به طور خودکار یک publisher objectWillChange دریافت میکند که قبل از تغییر هر ویژگی @Published سیگنال ارسال میکند. SwiftUI از طریق @ObservedObject در این publisher مشترک میشود و پس از دریافت سیگنال، View را به عنوان نیازمند ترسیم مجدد علامتگذاری میکند.
class TaskViewModel: ObservableObject {
@Published var tasks: [Task] = []
@Published var isLoading = false
func loadTasks() async {
isLoading = true
// واکشی دادهها
isLoading = false
}
}
struct TaskListView: View {
@ObservedObject var viewModel: TaskViewModel
var body: some View {
List(viewModel.tasks) { task in
Text(task.title)
}
.task { await viewModel.loadTasks() }
}
}
وقتی View والد viewModel را از طریق مقداردهنده اولیه به TaskListView منتقل میکند، SwiftUI بین شیء و View ارتباط برقرار میکند. با تغییر آرایه tasks یا پرچم isLoading، SwiftUI TaskListView را دوباره ترسیم میکند. شیء در این میان بدون تغییر میماند — در View والد از طریق @StateObject ذخیره میشود.
تفاوت بین @ObservedObject و @StateObject — تفاوت بین ناظر و مالک است. @StateObject شیء ایجاد میکند و چرخه حیات آن را مدیریت میکند. @ObservedObject فقط شیئی را مشاهده میکند که در جای دیگری ایجاد و ذخیره شده است. انتخاب بین آنها با مسئولیت View نسبت به دادهها تعیین میشود.
| سناریو | توصیه | دلیل |
|---|---|---|
| View داده ایجاد میکند | @StateObject | View مالک شیء است و مسئول چرخه حیات آن است |
| View داده دریافت میکند | @ObservedObject | View فقط مشاهده میکند، شیء در والد زندگی میکند |
| پشتیبانی iOS 13 | @ObservedObject | @StateObject در دسترس نیست، از @ObservedObject با مدیریت دستی استفاده کنید |
| کامپوننت قابل استفاده مجدد | @ObservedObject | کامپوننت نباید داده ایجاد کند — آن را از خارج دریافت میکند |
قانون اصلی: اگر View شیء ایجاد میکند — @StateObject. اگر View شیء دریافت میکند — @ObservedObject. نقض این قانون به سمت @ObservedObject برای ایجاد شیء منجر به از دست رفتن داده در بازسازی View میشود. نقض به سمت @StateObject برای دریافت شیء یک نمونه تکراری مستقل از والد ایجاد میکند.
سناریوی معمول استفاده از @ObservedObject — یک لیست وظایف، که در آن View ریشه یک view model ایجاد میکند و سلول لیست آن را از طریق @ObservedObject دریافت میکند. هر سلول میتواند متدهای view model را فراخوانی کند و تغییرات به طور خودکار در کل لیست نمایش داده میشوند، زیرا همه سلولها یک شیء را مشاهده میکنند.
struct TaskRow: View {
@ObservedObject var viewModel: TaskViewModel
let task: Task
var body: some View {
HStack {
Text(task.title)
Spacer()
Button("انجام شد") {
viewModel.completeTask(task)
}
}
}
}
struct TaskListContainer: View {
@StateObject var viewModel = TaskViewModel()
var body: some View {
List(viewModel.tasks) { task in
TaskRow(viewModel: viewModel, task: task)
}
}
}
در این مثال TaskListContainer viewModel را از طریق @StateObject ایجاد میکند و هر TaskRow آن را از طریق @ObservedObject دریافت میکند. وقتی کاربر “Done” را در هر سطری کلیک میکند، viewModel.completeTask ویژگی published را تغییر میدهد و همه Viewهایی که این شیء را مشاهده میکنند به طور خودکار بهروز میشوند.
رایجترین خطا — استفاده از @ObservedObject برای ایجاد شیء در داخل View است. وقتی View بازسازی میشود (مثلاً با تغییر state)، SwiftUI یک نمونه جدید ObservableObject ایجاد میکند که منجر به از دست رفتن تمام دادههای جمعآوری شده میشود. این خطا به خصوص در NavigationStack دردناک است، جایی که کاربر میتواند فرمی را پر کند و هنگام بازگشت دادهها را از دست بدهد.
// ❌ از دست رفتن داده: @ObservedObject شیء را نگه نمیدارد
struct FormView: View {
@ObservedObject var formVM = FormViewModel()
// فرم VM جدید در هر بازسازی View ایجاد میشود!
}
// ✅ درست: @StateObject شیء را نگه میدارد
struct FormView: View {
@StateObject var formVM = FormViewModel()
// شیء یک بار در طول عمر View ایجاد شد
}
اگر View فرزند همان ObservableObject را از طریق @StateObject اعلام کند، یک کپی مستقل ایجاد میکند. تغییرات در شیء والد در فرزند دیده نخواهد شد و بالعکس. همیشه برای Viewهای فرزندی که شیء را از خارج دریافت میکنند از @ObservedObject استفاده کنید.
در SwiftUI مدرن چندین جایگزین برای @ObservedObject وجود دارد، هر کدام با مزایای خود. انتخاب به معماری برنامه، نسخه iOS و سناریوی استفاده خاص بستگی دارد.
انتخاب بین @ObservedObject و @EnvironmentObject — یک مسئله سبک و معماری است. @ObservedObject وابستگیهای View را از طریق مقداردهنده اولیه به وضوح نشان میدهد که کد را قابل پیشبینیتر میکند. @EnvironmentObject برای سلسلهمراتب عمیق مناسب است، اما وابستگیها را پنهان میکند که میتواند اشکالزدایی را دشوار کند.
سوالات متداول
بله، اگر شیء در خارج از SwiftUI ایجاد و ذخیره شده باشد — مثلاً در AppDelegate یا singleton. در این حالت @ObservedObject صرفاً در تغییرات شیء موجود مشترک میشود. با این حال برای اشیاء ایجاد شده در داخل سلسلهمراتب SwiftUI همیشه به @StateObject در جایی بالاتر نیاز است.
محتملترین دلیل — ویژگی از طریق @Published تغییر نمیکند یا خود شیء تغییر نمیکند، بلکه ساختار داخلی آن بدون فراخوانی objectWillChange تغییر میکند. برای مجموعهها از تخصیص یک کپی جدید استفاده کنید: array.append() کافی نیست — باید خود آرایه را دوباره از طریق array = array + [element] تخصیص دهید.
@ObservedObject به خودی خود بار قابل توجهی ایجاد نمیکند. مشکلات زمانی ایجاد میشوند که تغییرات مکرر ویژگیهای @Published رخ دهد — هر تغییر باعث ترسیم مجدد همه Viewهای ناظر میشود. برای بهینهسازی از EquatableView استفاده کنید، تعداد ویژگیهای published را کاهش دهید و از بهروزرسانیهای غیرضروری خودداری کنید.
@ObservedObject کل یک کلاس ObservableObject را مشاهده میکند و View را با هر تغییری در ویژگیهای published آن دوباره ترسیم میکند. @Binding یک ارتباط دوطرفه با یک مقدار خاص (String، Int، Bool) ایجاد میکند و اجازه خواندن و نوشتن آن را میدهد. @Binding سبکتر است و به ObservableObject نیاز ندارد.
بله، این یک الگوی استاندارد است. @Published در داخل ObservableObject به طور خودکار با @ObservedObject یکپارچه میشود. هر ویژگی @Published یک ناظر به publisher objectWillChange اضافه میکند. با تغییر هر یک از آنها، همه Viewهای دارای @ObservedObject برای این شیء دوباره ترسیم میشوند.
خلاصه
ما یک اپلیکیشن موبایل به صورت کلید در دست توسعه خواهیم داد
IT Sectr از سال 2017 برنامههای iOS و Android را برای استارتاپها و کسبوکارها ایجاد میکند. ما به شما مشاوره میدهیم و بهترین راهحل را پیشنهاد خواهیم کرد.
همچنین بخوانید