0

Sorry for the basic question. I am working through the Head First Swift book and have hit a snag. I am trying to iterate over an array of custom types and access the functions of the Dog and Cat types in a for in loop but I can't figure out the syntax. How do I call either bark() or meow() depending on whether the item in the array is a Dog or Cat?

protocol Animal {
    var type: String { get }
}

struct Dog: Animal {
    var name: String
    var type: String
    
    func bark() {
        print("Woof woof")
    }
}

struct Cat: Animal {
    var name: String
    var type: String
    
    func meow() {
        print("Meow")
    }
}

var bunty = Cat(name: "Bunty", type: "British Shorthair")
var nigel = Cat(name: "Nigel", type: "Russian Blue")
var percy = Cat(name: "Percy", type: "Manx")
var argos = Dog(name: "Argos", type: "Whippet")
var barny = Dog(name: "Barny", type: "Bulldog")

var animals: [Animal] = [bunty, nigel, percy, argos, barny]
print(animals.count)


for animal in animals {
    
}

I have tried an if statement in the loop:

for animal in animals {
    if animal == Cat {
        meow()
    } else {
        bark()
    }
}

But Swift says "Binary operator == cannot be applied to operands Animal and Cat.type. Thanks for your help, I'm trying to learn.

2
  • Does this answer your question? whats the equivalent of java's instanceof in Swift? Commented Aug 22, 2022 at 8:11
  • @jonrsharpe, identifying the class is only half of the problem. This answer doesn’t answer the question of how you call the methods on the derived classes. Commented Aug 22, 2022 at 11:53

2 Answers 2

2

Since protocol Animal declare only property var type: String { get } and you want to call functions bark() and meow() which are only visible to their specific types you will have to downcast with as? operator.

for animal in animals {
    if let dog = animal as? Dog {
        dog.bark()
    }

    if let cat = animal as? Cat {
        cat.meow()
    }
}
Sign up to request clarification or add additional context in comments.

Comments

1

Michal Olechowski is right that you need to downcast in order to call those methods. Here are two additional ways that can be done:

Use switch to identify the class

for animal in animals {
   switch animal {
      case let dog as Dog:
         dog.bark()
      case let cat as Cat:
         cat.meow()
      default: break
   }
}

Use optional chaining with downcasting

for animal in animals {
   (animal as? Dog)?.bark()
   (animal as? Cat)?.meow()
}

In this case, if the conditional downcast works then the optional value is unwrapped and the method is called. If the conditional downcast fails and returns nil, then nothing happens.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.