swiftdata model polymorphism?

I have a SwiftData model where I need to customize behavior based on the value of a property (connectorType). Here’s a simplified version of my model:

@Model
public final class ConnectorModel {
    public var connectorType: String
    ...
    func doSomethingDifferentForEveryConnectorType() {
       ...
    }
}

I’d like to implement doSomethingDifferentForEveryConnectorType in a way that allows the behavior to vary depending on connectorType, and I want to follow best practices for scalability and maintainability. I’ve come up with three potential solutions, each with pros and cons, and I’d love to hear your thoughts on which one makes the most sense or if there’s a better approach:

**Option 1: Use switch Statements **

func doSomethingDifferentForEveryConnectorType() {  
    switch connectorType {  
    case "HTTP":  
        // HTTP-specific logic  
    case "WebSocket":  
        // WebSocket-specific logic  
    default:  
        // Fallback logic  
    }  
}

Pros: Simple to implement and keeps the SwiftData model observable by SwiftUI without any additional wrapping.

Cons: If more behaviors or methods are added, the code could become messy and harder to maintain.

**Option 2: Use a Wrapper with Inheritance around swiftdata model **


@Observable  
class ParentConnector {  
    var connectorModel: ConnectorModel  

    init(connectorModel: ConnectorModel) {  
        self.connectorModel = connectorModel  
    }  

    func doSomethingDifferentForEveryConnectorType() {  
        fatalError("Not implemented")  
    }  
}  

@Observable  
class HTTPConnector: ParentConnector {  
    override func doSomethingDifferentForEveryConnectorType() {  
        // HTTP-specific logic  
    }  
}  

Pros: Logic for each connector type is cleanly organized in subclasses, making it easy to extend and maintain.

Cons: Requires introducing additional observable classes, which could add unnecessary complexity.

**Option 3: Use a @Transient class that customizes behavior **


protocol ConnectorProtocol {  
    func doSomethingDifferentForEveryConnectorType(connectorModel: ConnectorModel)  
}  

class HTTPConnectorImplementation: ConnectorProtocol {  
    func doSomethingDifferentForEveryConnectorType(connectorModel: ConnectorModel) {  
        // HTTP-specific logic  
    }  
}  

Then add this to the model:


@Model  
public final class ConnectorModel {  
    public var connectorType: String  

    @Transient  
    public var connectorImplementation: ConnectorProtocol?  

    // Or alternatively from swiftui I could call myModel.connectorImplementation.doSomethingDifferentForEveryConnectorType() to avoid this wrapper
    func doSomethingDifferentForEveryConnectorType() {  
        connectorImplementation?.doSomethingDifferentForEveryConnectorType(connectorModel: self)  
    }  
}  

Pros: Decouples model logic from connector-specific behavior. Avoids creating additional observable classes and allows for easy extension.

Cons: Requires explicitly passing the model to the protocol implementation, and setup for determining the correct implementation needs to be handled elsewhere.

My Questions

  1. Which approach aligns best with SwiftData and SwiftUI best practices, especially for scalable and maintainable apps?
  2. Are there better alternatives that I haven’t considered?
  3. If Option 3 (protocol with dependency injection) is preferred, what’s the best way to a)manage the transient property 2) set the correct implementation and 3) pass reference to swiftdata model?

Thanks in advance for your advice!

swiftdata model polymorphism?
 
 
Q