2

I'm experimenting with a server-side-Swift project using Vapor and Fluent. The idea for this project is to track athletes and their training plans and workouts. Trying to be protocol-oriented, I have a protocol named "Workout," and then various classes that conform to "Workout" such as "run," "swim," etc.

Workout.swift:

import Vapor
import FluentPostgreSQL

protocol Workout {
    var title: String { get set }
    var duration: Int { get set }
}

And then, for example, Run.swift:

import Vapor
import FluentPostgreSQL

final class Run: Workout, Codable {
    //Conform to Workout
    var title: String
    var duration: Int
    
    //New Properties
    var distance: Double
    var id: Int?
    
    init(title: String, duration: Int, distance: Double) {
        self.title = title
        self.duration = duration
        self.distance = distance
    }
}

extension Run: PostgreSQLModel {}
extension Run: Content {}

When I create the model for "Training Plan," I would like to have the parameter:

var workouts = [Workout]?

to allow for any type which conforms to Workout.

TrainingPlan.swift:

import Vapor
import FluentPostgreSQL

final class TrainingPlan: Codable {
    var id: Int?
    var title: String
    var workouts: [Workout]?

    init(title: String) {
        self.title = title
    }
}

extension TrainingPlan: PostgreSQLModel {}
extension TrainingPlan: Content {}

I get the following errors on TrainingPlan.swift:

Type 'TrainingPlan' does not conform to protocol 'Decodable'

Type 'TrainingPlan' does not conform to protocol 'Encodable'

Changing Workout.swift to:

protocol Workout: Codable {
    var title: String { get set }
    var duration: Int { get set }
}

does not resolve the errors.

What would be the correct way to handle this with Fluent while still staying protocol-oriented in my data modeling? Thank you!

UPDATE (6/20/18):

Setting a generic type in TrainingPlan.swift (for types that conform to Workout) resolves the errors about conforming to Encodable / Decodable:

import Vapor
import FluentPostgreSQL

final class TrainingPlan<W: Workout>: Codable {
    var id: Int?
    var title: String
    var workouts: [W]?
    
    init(title: String) {
        self.title = title
    }
}

extension TrainingPlan: PostgreSQLModel {}
extension TrainingPlan: Content {}
extension TrainingPlan: Parameter {}
extension TrainingPlan: Migration {}

However, this implementation causes another error with adding TrainingPlan to the database migration in configure.swift:

migrations.add(model: TrainingPlan.self, database: .psql)

Produces the error:

Generic parameter 'W' could not be inferred

Community
  • 1
  • 1
seanbarney
  • 21
  • 3
  • Possible duplicate of [Protocol doesn't conform to itself?](https://stackoverflow.com/questions/33112559/protocol-doesnt-conform-to-itself) – Mukesh Jun 19 '18 at 07:28

1 Answers1

0

You need to do

swift migrations.add(model: TrainingPlan<ConcreteWorkoutType>.self, database: .psql)

So the compiler knows what the generic type is when compiling your code

0xTim
  • 5,146
  • 11
  • 23