Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

SwiftData: How to model inheritance?

My model class includes an array of objects that have some properties in common and some specific to only a subset. That sounds like a use case for a protocol and inheritance. However, I can't get SwiftData to compile this.

Minimal example:

import Foundation
import SwiftData

@Model
class Workout {
    var startTime: Date
    var endTime: Date?
    var activities: [Activity]
    
    init(startTime: Date, endTime: Date? = nil, activities: [Activity]) {
        self.startTime = startTime
        self.endTime = endTime
        self.activities = activities
    }
}

protocol Activity {
    var calories: Int {get}
}

@Model
class Run: Activity {
    var calories: Int
    var steps: Int
    
    init(calories: Int, steps: Int) {
        self.calories = calories
        self.steps = steps
    }
}

@Model
class Swim: Activity {
    var calories: Int
    var lanes: Int
    
    init(calories: Int, lanes: Int) {
        self.calories = calories
        self.lanes = lanes
    }
}

The error Type 'any Activity' cannot conform to 'PersistentModel' occurs in the generated code for the array:

enter image description here

I watched the WWDC videos, read through the docs and looked at all example projects I could find — nowhere is this problem tackled.

Does anyone have an idea either how to make SwiftData to support this or how to model the data in a compatible way?

like image 290
Jannik Arndt Avatar asked Apr 11 '26 06:04

Jannik Arndt


1 Answers

This is how I'm tackling this in my application: anything that needs to be "variant" (different depending on the type) is put into a struct that is enum'd upon. This isn't great because it means you can't filter/search on the variant properties, but it works:

@Model class Workout {
    var startTime: Date
    var endTime: Date?
    var activities: [Activity]
    
    init(startTime: Date, endTime: Date? = nil, activities: [Activity]) {
        // ...
    }
}

@Model class Activity {
    var calories: Int
    var activityType: ActivityType

    init(calories: Int, activityType: ActivityType) {
        // ...
    }
}

enum ActivityType: Codable {
    case swim(lanes: Int)
    case run(steps: Int)
}

There are a few things to note:

  • You won't be able to query on a specific ActivityType
  • If you use iCloud container sync, Workout.activities will be unsorted. You'll need some kind of order property on activity to ensure ordering.
like image 167
Downgoat Avatar answered Apr 12 '26 18:04

Downgoat



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!