اپل در انتشار SwiftData ۲۰۲۷ (در کنار Xcode 27 beta) تغییرات بنیادینی را معرفی کرده که تجربه‌ی توسعه‌ی اپلیکیشن‌های iOS و macOS را متحول می‌کند. این بروزرسانی بر سه محور اصلی تمرکز دارد: استفاده از انواع داده‌ی شخص ثالث بدون نیاز به @Model، بخش‌بندی (Sectioning) نتایج پرس‌وجو در SwiftUI و مکانیزم‌های نظارت بر تغییرات دیتاستور خارج از محیط SwiftUI.

پشتیبانی از انواع خارجی با ویژگی @Attribute(.codable)

یکی از چالش‌های قدیمی SwiftData، عدم قابلیت نگهداری مستقیم انواع داده‌ای بود که کنترل سورس‌کد آن‌ها در دست توسعه‌دهنده نبود (مانند SDKهای شخص ثالث). از آنجا که نمی‌توان بر روی این انواع @Model قرار داد، SwiftData قادر به تولید اسکما (Schema) برایشان نبود.

راه‌حل نسخه ۲۰۲۷، استفاده از پروتکل Codable به عنوان یک ویژگی (Attribute) است. حالا هر نوع مرجع یا مقداری که از Codable پیروی کند، می‌تواند به عنوان یک فیلد در مدل SwiftData ظاهر شود:

struct ExLocation: Codable { // نوع شخص ثالث یا SDK
    var latitude: Double
    var longitude: Double
}

@Model
final class Destination {
    var title: String
    @Attribute(.codable) var locationInfo: ExLocation
    
    init(title: String, locationInfo: ExLocation) {
        self.title = title
        self.locationInfo = locationInfo
    }
}

محدودیت‌های مهم ویژگی .codable

  • بدون فیلترینگ و سورتینگ: این فیلدها به‌عنوان مقادیر مبهم (Opaque) ذخیره می‌شوند و در پرس‌وجوهای #Predicate قابل استفاده نیستند.
  • بدون مهاجرت خودکار: تغییر در ساختار نوع پایه (افزودن/حذف پراپرتی) باعث فعال‌سازی Migration خودکار نمی‌شود.
  • پیشنهاد: برای انواعی که مالکیت کامل کدشان را دارید، همچنان استفاده از @Model و روابط معمولی SwiftData بهترین شیوه (Best Practice) است.

بخش‌بندی نتایج در SwiftUI با پارامتر sectionBy

پراپرتی رپر @Query اکنون پارامتر جدید sectionBy را می‌پذیرد که امکان گروه‌بندی نتایج بر اساس یک کی‑پث (KeyPath) را فراهم می‌کند. این قابلیت مستقیماً با List و Section در SwiftUI یکپارچه شده است:

@Query(sort: \Trip.startDate, sectionBy: \.destination)
var trips: [Trip]

// در View:
List {
    ForEach(_trips.sections) { section in
        Section(section.id) {
            ForEach(section) { trip in
                TripListItem(trip: trip)
            }
        }
    }
}

این ویژگی کد boilerplate را برای نمایش لیست‌های گروه‌بندی شده (مانند تقویم، دسته‌بندی مقصدها یا فیلترهای کاربر) به طور چشمگیری کاهش می‌دهد.

ResultsObserver: نظارت بر داده در هر لایه‌ی اپلیکیشن

@Query انحصاراً در Viewهای SwiftUI کار می‌کرد. ResultsObserver این محدودیت را برطرف می‌کند و بر پایه فریم‌ورک Observation ساخته شده است. این مراقب (Observer) می‌تواند در کلاس‌های کنترلر، ViewModelها، رندررهای SceneKit یا هر کد غیر-SwiftUI دیگر استفاده شود.

ویژگی‌های کلیدی:

  • پشتیبانی کامل از مرتب‌سازی، فیلترینگ (Predicate) و بخش‌بندی (Sectioning)
  • تعیین مسیر یادگیری (Token) با withContinuousObservation برای واکنش به رویدادهای .didSet
  • امنیت ترد (Thread Safety) با انوتیشن @MainActor
@Observable @MainActor
final class MapCameraController {
    private let resultObserver: ResultsObserver<Trip>
    private var token: ObservationTracking.Token?
    
    init(modelContext: ModelContext) throws {
        resultObserver = try ResultsObserver<Trip>(modelContext: modelContext)
        token = withContinuousObservation(options: [.didSet]) { [weak self] event in
            // واکنش به تغییرات: به‌روزرسانی دوربین نقشه، محاسبه مجدد کلasterها و ...
        }
    }
}

HistoryObserver: همگام‌سازی با سیستم‌های خارجی

برای سناریوهایی که نیاز به ردگیری تراکنش‌های دیتابیس دارند (مانند Sync با سرور، Backup یا Audit Log)، HistoryObserver معرفی شده است. این مراقب تاریخچه ماندگار (Persistent History) دیتاستور را رصد می‌کند.

  • پراپرتی eventCounter قابل مشاهده (Observable) است و با هر تراکنش جدید افزایش می‌یابد.
  • با مشاهده این کاونتر، می‌توان از ModelContext.fetchHistory برای دریافت جزئیات تغییرات استفاده کرد.
  • مناسب برای پیاده‌سازی Offline-first sync، Conflict Resolution و Real-time Collaboration.

تزامنی با Xcode 27 Beta

تمام قابلیت‌های بالا در Xcode 27 beta برای پلتفرم‌های ۲۰۲۷ اپل (iOS 18، macOS 15، watchOS 11، tvOS 18، visionOS 2) در دسترس هستند. توسعه‌دهندگان می‌توانند از امروز با این APIها آزمایش کرده و بازخورد خود را به تیم SwiftData ارسال کنند.


نکات کلیدی برای توسعه‌دهندگان

  • از @Attribute(.codable) فقط برای انواع غیرقابل‌تغییر یا شخص ثالث استفاده کنید.
  • برای لیست‌های پیچیده SwiftUI، sectionBy جایگزین منطق دستی گروه‌بندی شود.
  • ResultsObserver را در لایه‌های غیر-UI (Domain/Logic) برای حذف وابستگی به View ادغام کنید.
  • HistoryObserver پایه‌ی معماری‌های Sync مدرن را فراهم می‌کند—از الان برای نقشه‌برداری سناریوهای همگام‌سازی برنامه‌ریزی کنید.