.agents/skills/swiftdata-pro/references/class-inheritance.md
When supporting iOS 26 and other coordinated releases (macOS 26, etc), SwiftData supports class inheritance for models.
Important: This is not a common feature; only add model subclassing if it actually has a benefit. Alternatives such as protocols are often simpler and better.
This works the same as regular class inheritance in Swift, however, child classes must be explicitly marked @available for a 26 release or later, e.g. iOS 26. This is required even if iOS 26 is set as the minimum deployment target.
For example:
@Model class Article {
var type: String
init(type: String) {
self.type = type
}
}
@available(iOS 26, *)
@Model class Tutorial: Article {
var difficulty: Int
init(difficulty: Int) {
self.difficulty = difficulty
super.init(type: "Tutorial")
}
}
@available(iOS 26, *)
@Model class News: Article {
var topic: String
init(topic: String) {
self.topic = topic
super.init(type: "News")
}
}
Notice how both the parent and child classes must use the @Model macro.
Important: When using a 26 release or later as minimum deployment target, we must still mark subclassed models with @available. However, we do not need to do the same with code using that model, because Xcode can match the deployment target and the model availability.
When providing the schemas as part of model container creation, make sure to list both the parent class and its child classes – SwiftData is not able to infer the connection by itself.
If you create a relationship to a model that has subclasses, the relationship might contain the parent class or any of its subclasses.
For example, the articles array here might contain Article, Tutorial, or News instances:
@Model class Magazine {
@Relationship(deleteRule: .cascade) var articles: [Article]
init(articles: [Article]) {
self.articles = articles
}
}
If only one subclass is supported, it should be written specifically. If several subclasses but not all should be in the relationship, you might have no choice but to add another level of subclasses: BaseClass -> Subclass -> Subsubclass. However, this is not a good idea – deep subclassing is generally frowned upon, and will increase complexity in migrations.
One important benefit of model subclassing is that we can use @Query to look for specific subclasses, or to look for the base class, which will automatically return all child classes too.
For example, we could load only tutorials like this:
@Query private var tutorials: [Tutorial]
Or load all articles, including tutorials, like this:
@Query private var articles: [Article]
If you want to load specific child classes but not the parent class, use is with the #Predicate macro to perform filtering:
@Query(filter: #Predicate<Article> {
$0 is Tutorial || $0 is News
}) private var tutorialsAndNews: [Article]
Important: The type of the resulting array elements is Article, the parent class, so typecasting must be used to access child-class properties and methods.
It's possible to do typecasting inside predicates to filter based on child-class properties. For example, this looks for easier tutorials and general news to create a list of articles suitable for the front page:
@Query(filter: #Predicate<Article> { article in
if let tutorial = article as? Tutorial {
tutorial.difficulty < 3
} else if let news = article as? News {
news.topic == "General"
} else {
false
}
}) private var frontPageArticles: [Article]
When working with the resulting data, regular Swift typecasting using as works fine.