Search code examples
swiftprotocols

Swift protocol using another protocol causes error


I'm working in Swift 4.

I want to define a Problem.

A Problem consists of a Question and an Answer.

The Question might be any of: String, Int, [Int], Image, [Image] or some new Type not defined or could be some combination of the above.

The Answer might also be any of the above but a given Question can have an Answer of a different type.

(e.g. question = (Image, "What type of animal is this?), answer = ("A Cat") )

So I thought protocols would be the way to go:

protocol Posable {
  var pose: String { get }
}

protocol Answerable: Hashable {
  var answer: String { get }
}

protocol Puzzle {
  var problem: Posable { get }
  var solution: Answerable { get }
}

I made Answerable Hashable because I want to be able to compare Answers and create sets of Answers.

But I get on the solution: Answerable line:

'Protocol 'Answerable' can only be used as a generic constraint because it has Self or associated type requirements.

I understand why that is but...

Can anyone make any suggestions about how to implement this so that I don't run into that problem?

I'm keen to implement with protocols if possible, partly so that I learn about them.


Solution

  • I would solve it with generics :

    import UIKit
    
    struct Posable<T> {
        var pose: T
    }
    
    struct Answerable<T> {
        var answer: T
    }
    
    extension Answerable: Equatable where T: Equatable {
        static func ==(lhs: Answerable<T>, rhs: Answerable<T>) -> Bool {
            return lhs.answer == rhs.answer
        }
    }
    
    extension Answerable: Hashable where T: Hashable {
        var hashValue: Int {
            return answer.hashValue
        }
    }
    
    struct Puzzle<T, U> {
        var problem: Posable<T>
        var solution: Answerable<U>
    }
    
    let image = UIImage() // Image of a cat
    let pose = Posable<(UIImage, String)>(pose: (image, "What type of animal is this?"))
    let solution = Answerable<String>(answer: "A cat")
    
    let myPuzzle = Puzzle<(UIImage, String), String>(problem: pose, solution: solution)
    

    Generics allow you to make very reusable code! Here you can use any class as a question or as an anwser.

    With Swift type inherence : you also get to simplify the initialization :

    let image = UIImage() // Image of a cat
    let pose = Posable(pose: (image, "What type of animal is this?"))
    let solution = Answerable(answer: "A cat")
    
    let myPuzzle = Puzzle(problem: pose, solution: solution)