Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Swift protocols: Why does the compiler complain that my class doesn't conform to a protocol?

I've been playing around with Swift protocols and I'm trying to figure out why this code isn't working...

protocol Animal {
  var name: String {get}
  var breed: String {get}
}

struct Bird: Animal {
  var name: String
  var breed: String
  var wingspan: Double
}

protocol AnimalHouse {
  var myAnimal: Animal! {get set}
}

class Birdhouse: AnimalHouse {
  var myAnimal: Bird!

  func isOpeningBigEnough() -> Bool {
    return myAnimal.wingspan <= 5.0
  }
}

The problem the compiler keeps giving me is that that BirdHouse doesn't conform to protocol AnimalHouse. If you follow up, it'll tell you that myAnimal requires type Animal, and I'm supplying type Bird. Obviously, Bird does conform to the Animal protocol, but that's not enough to make the compiler happy.

I'm assuming this is one of those one-line fixes where the trick is knowing where the one line is. Anybody have any suggestions?

(And, yes, I could make myAnimal an Animal and then cast it as a Bird later in the function, but that seems unnecessarily messy.)

like image 420
Toddarooski Avatar asked Nov 30 '25 03:11

Toddarooski


2 Answers

The compiler is right.

When you write

protocol AnimalHouse {
    var myAnimal: Animal! {get set}
}

you are making (among the others) the following statement:

If a type does conform to AnimalHouse, then it is possible to put an Animal! inside the myAnimal property.

Now let's look at how Birdhouse is defined

class Birdhouse: AnimalHouse {
    var myAnimal: Bird!

    ...
}

The type on myAnimal is Bird!. And you cannot put an Animal! inside a property of type Bird!.

So Birdhouse does not respect what promised in the AnimalHouse protocol.

like image 131
Luca Angeletti Avatar answered Dec 05 '25 07:12

Luca Angeletti


As you said yourself in the question, you can't just downcast to Bird from Animal. I propose changing the var to be optional, as an AnimalHouse is likely to be without inhabitant some of the time.

In my implementation below non Bird animals can't enter the birdhouse.

protocol AnimalHouse {
    var myAnimal: Animal? {get set}
}

class Birdhouse: AnimalHouse {
    var myAnimal: Animal? {
        get{
            return myBird
        }
        set(newanimal){
            if let bird = newanimal as? Bird {
                myBird = bird
            }
        }
    }

    private var myBird: Bird?

    func isOpeningBigEnough() -> Bool {
        return myBird?.wingspan <= 5.0
    }
}

A further development of the AnimalHouse protocol might be to add throws to the setter (not possible as of Swift 2.0) or that an AnimalHouse returns the type of animal it can house.

protocol AnimalHouse {
    var myAnimal: Animal? {get set}
    func houses() -> Any
}

class Birdhouse: AnimalHouse {
    func houses() -> Any {
        return Bird.self
    }
}
like image 29
orkoden Avatar answered Dec 05 '25 09:12

orkoden



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!