Delegation is a powerful design pattern in Swift that allows one object to communicate with another object in a loosely-coupled way. This means that the object sending the message (the delegator) doesn’t need to know anything about the object receiving the message (the delegate), other than that it conforms to a certain protocol.
In this example, we’ll use the delegation pattern to control whether a Pokemon can use its attack. We’ll create a Pokemon
struct with a name
and a power
property, and a Trainer
class that has a delegate
property and a pokemon
property. The delegate
property will be of type PokemonAttackDelegate
, a protocol we’ll define later.
struct Pokemon {
let name: String
let power: Int
}
protocol PokemonAttackDelegate {
func canUseAttack(_ pokemon: Pokemon) -> Bool
}
class Trainer {
var delegate: PokemonAttackDelegate?
var pokemon: Pokemon
init(pokemon: Pokemon) {
self.pokemon = pokemon
}
func useAttack() {
if let delegate = delegate {
if delegate.canUseAttack(pokemon) {
print("\(pokemon.name) used its attack with power \(pokemon.power)!")
} else {
print("\(pokemon.name) can't use its attack.")
}
} else {
print("No delegate set.")
}
}
}
In the useAttack
method of the Trainer
class, we check if the delegate
property is not nil
, and if it’s not, we call its canUseAttack
method, passing in the pokemon
property. If the delegate says that the attack can be used, we print a message to the console. Otherwise, we print a different message.
Now let’s define the PokemonAttackDelegate
protocol:
protocol PokemonAttackDelegate {
func canUseAttack(_ pokemon: Pokemon) -> Bool
}
The only requirement of this protocol is a single method called canUseAttack
, which takes a Pokemon
parameter and returns a boolean indicating whether the attack can be used or not. We’ll implement this protocol in our ViewController
class, which we’ll set as the delegate
of the Trainer
instance.
class ViewController: UIViewController, PokemonAttackDelegate {
override func viewDidLoad() {
super.viewDidLoad()
let pikachu = Pokemon(name: "Pikachu", power: 50)
let trainer = Trainer(pokemon: pikachu)
trainer.delegate = self
trainer.useAttack()
}
func canUseAttack(_ pokemon: Pokemon) -> Bool {
// Let's say that Pikachu can only use its attack if its power is greater than 40
return pokemon.power > 40
}
}
In our ViewController
class, we conform to the PokemonAttackDelegate
protocol and implement the canUseAttack
method. In this example, we say that Pikachu can only use its attack if its power
property is greater than 40. We then create a Trainer
instance with a Pikachu
Pokemon
instance and set the delegate
property to self
, which is an instance of ViewController
. Finally, we call the useAttack
method on the Trainer
instance, which will call the canUseAttack
method on the delegate (ViewController
) to determine if the attack can be used.
In conclusion, delegates are a powerful tool in Swift that allow for flexible communication between objects. By implementing the delegate pattern, we can create reusable code that can be adapted to various use cases. In this example, we saw how delegates can be used in a Pokemon-themed scenario, where a Trainer object can use its Pokemon’s attack only if certain conditions are met. By understanding how to implement and use delegates in our own projects, we can make our code more modular and easier to maintain.