I'm trying to learn protocols and protocol extensions and decided to make an app where user can track points in a match. For example in tennis and badminton. Tennis match contains sets, games and points and badminton match contains games and points. All matches contains participants(players).
Point stores a single point in a match.
struct Point {
let value: Int
init(_ value: Int) {
self.value = value
}
}Match contains participants for example Players.
protocol Participant {
var id: String { get }
var name: String { get }
}
struct Player: Participant {
let id: String
let name: String
}The match controller (or view model) only needs to know about the match items and it should be able add add points to a certain player.
let player1 = Player(id: "0", name: "John")
let player2 = Player(id: "1", name: "Jane")
var match = TennisMatch(participants: [player1, player2)
match.addChild() // Adds set in a match
let currentPointCollection = match.currentPointCollection() // Gets current point collection (set 1, game 1)
currentPointCollection.addPoint(Point(1), toParticipant: player1)I started by creating two protocols: PointCollection and ItemCollection. PointCollection is a collection for points e.g. a game contains points. ItemCollection is a collection for match's child items e.g. match contains games or sets and set contains games etc.
protocol PointCollection {
mutating func addPoint(point: Point, toParticipant participant: Participant)
}
protocol ItemCollection {
typealias ItemType
var count: Int { get }
mutating func addChild()
subscript(i: Int) -> ItemType { get }
func currentPointCollection() -> PointCollection
}Match is now basically an ItemCollection.
protocol Match: ItemCollection {}This is where I'm struggling. ItemCollection can have both ItemCollections and PointCollections as a child because a match can contain both. For example if I define a tennis match. This fails in “Protocol 'ItemCollection' can only be used as a generic constraint because it has Self or associated type requirements”
struct TennisMatch {
private var sets: [ItemCollection] = []
}
extension TennisMatch: ItemCollection {
typealias ItemType = ItemCollection
var count: Int { return sets.count }
mutating func addChild() {
sets.append(MatchGame())
}
subscript(i: Int) -> ItemCollection {
return sets[i]
}
}
struct MatchGame {
private var participantPoints: [String: [Point]] = [:]
}
extension MatchGame: PointCollection {
mutating func addPoint(point: Point, toParticipant participant: Participant) {
var points = participantPoints[participant.id] ?? []
points.append(point)
participantPoints[participant.id] = points
}
}I know what the error means, but I'm not sure how to fix it. If you have a better idea how to define this kind of architecture feel free to post.