The 2027 release of SwiftData introduces support for persisting custom and third-party types via Codable, along with the ability to organize data into SwiftUI list sections. It also adds new capabilities for observing data store changes through ResultsObserver and HistoryObserver.
Codable attributes solve a limitation in SwiftData when integrating custom or third-party types into a model. Since you do not control a third-party reference type, you cannot mark it with @Model. As a result, it cannot be included directly into a model, because SwiftData is unable to generate a schema for it.
With the new release, though, a third-party type can work seamlessly with SwiftData as long as it conforms to the Codable protocol. In the following example, ExLocation is a value type that we do not control, originating either from the iOS SDK or a third-party library:
struct ExLocation: Codable {
...
}
@Model
final class Destination {
var title: String
@Attribute(.codable) var locationInfo: ExLocation
init(title: String, locationInfo: ExLocation) {
self.title = title
self.locationInfo = locationInfo
}
}
Using a Codable property in a SwiftData model comes with a few limitations. Most notably, such properties cannot be used for filtering or sorting, as they are treated as opaque values. Additionally, changes to the underlying type, such as adding a new property, do not trigger automatic migrations. Due to these constraints, using .codable for types you control is not considered a good practice. In those cases, it is so far more practical to define them as @Model types instead.
To support sectioning fetches, SwiftData extends the @Query property wrapper with the new sectionBy parameter, allowing to specify a key path on the fetched type to use for grouping:
@Query(sort: \Trip.startDate, sectionBy: \.destination)
var trips: [Trip]
Passing a sectionBy argument will make the wrapped object, _trips, expose a sections property that can be used to iterate over them:
List {
ForEach (_trips.sections) { section in
Section(section.id) {
ForEach(section) { trip in
TripListItem(trip: trip)
}
}
}
}
SwiftData 2027 also introduces ResultObserver, which provides a mechanism similar to @Query but can be used outside of SwiftUI. This makes it suitable for contexts such as object classes that need to recompute values in response to data changes, or for environments like SceneKit-based apps that do not use SwiftUI at all. ResultObserver is built on the Observation framework and supports the same features as @Query, including sorting, filtering, and sectioning.
The following snippets shows how you define a controller object that uses ResultObserver to observe changes to Trip entities:
@Observable @MainActor final class MapCameraController {
private let ResultObserver: ResultObserver<Trip>
...
private var token: ObservationTracking.Token?
init(modelContext: ModelContext) throws {
resultObserver = try ResultObserver<Trip>(modelContext: modelContext)
token = withContinuosObservation(option: [.didSet]) { [weak self] event in
...
}
}
}
Similarly to ResultsObserver, HistoryObserver monitors the persistent history of your data store and allows your code to react when new transactions are added. This is particularly useful for keeping parts of the data store in sync with other systems, like remote servers. HistoryObserver exposes an observable eventCounter property that increments whenever new transactions become available in the persistent history. By observing this property, an app can use ModelContext.fetchHistory API to retrieve the latest changes.
SwiftData for Apple's 2027 platform releases is available in Xcode 27 beta.