Home > Software design >  How to access the methods of custom types in an array?
How to access the methods of custom types in an array?

Time:08-24

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.

CodePudding user response:

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()
    }
}

CodePudding user response:

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.

  • Related