In Swift, when learning UI (User Interface), sooner or later, everyone comes to the need to use a delegate. All the guides write about them, and you seem to be doing as it is written there, and it seems to work, but why and how it works, not everyone in the head fits in to the end. Personally, I even got the feeling for a while that delegate is some kind of magic word, and that it is directly built into the programming language (that's how confusing my thoughts were from these guides). Let's try to explain in simple terms what it is. And once you understand the delegate, it will be much easier to understand what a callback is and how it works.
Waiter and chef
So, before moving on to the code, let's imagine a certain waiter and some chef. The waiter received an order from the client at the table, but he himself does not know how to cook, and he needs to ask the cook about it. He can go to the kitchen and tell the cook, "Cook the chicken." The cook has the appropriate tools (frying pan, oil, fire ...) and cooking skill. The cook prepares and gives the dish to the waiter. The waiter takes what was made by the chef and brings it to the client.
Now let's imagine a situation that a waiter cannot come running to the kitchen and tell the cook directly which dish was ordered from him. They do not let him into the kitchen (for example, such rules) or the kitchen is on another floor (you get tired of running). And the only way to communicate is through the mini-elevator window. The waiter puts a note there, presses the button, the elevator goes to the kitchen. Comes back with a prepared dish. Do you remember? Now let's fix the situation in our head, try to recreate it through the code and understand how this relates to our theme.
Let's transfer to the code
We create the waiter and cook classes. For simplicity, let's do this in the playground:
import UIKit
//
class Waiter {
/// "" - . , "private".
private var order: String?
/// " ".
func takeOrder(_ food: String) {
print("What would you like?")
print("Yes, of course!")
order = food
sendOrderToCook()
}
/// " ". . ?
private func sendOrderToCook() {
// ??? ?
}
/// " ". .
private func serveFood() {
print("Your \(order!). Enjoy your meal!")
}
}
//
class Cook {
/// "". .
private let pan: Int = 1
/// "". .
private let stove: Int = 1
/// "". .
private func cookFood(_ food: String) -> Bool {
print("Let's take a pan")
print("Let's put \(food) on the pan")
print("Let's put the pan on the stove")
print("Wait a few minutes")
print("\(food) is ready!")
return true
}
}
Now we create copies of them (we hire them to work), and ask the waiter to receive the order (chicken):
// ( ):
let waiter = Waiter()
let cook = Cook()
// . , :
waiter.takeOrder("Chiken")
How can the waiter tell the cook what to cook now?
, , private. private, :
cook.cookFood(waiter.order!)
// 'cookFood' is inaccessible due to 'private' protection level
// 'order' is inaccessible due to 'private' protection level
«» , private . "" , ? : " , , ?"
"". . . "" " ":
protocol InterchangeViaElevatorProtocol {
func cookOrder(order: String) -> Bool
}
, "", , . . , . : , .
, . ().
. , , " ". Xcode . Bool .
cookFood, .
extension Cook: InterchangeViaElevatorProtocol {
func cookOrder(order: String) -> Bool {
cookFood(order)
}
}
" ". , , .
extension Waiter {
var receiverOfOrderViaElevator: InterchangeViaElevatorProtocol? { return cook }
}
, . return cook.
-: , . .
, . , .
:
import UIKit
protocol InterchangeViaElevatorProtocol {
func cookOrder(order: String) -> Bool
}
class Waiter {
// " ". , , .
var receiverOfOrderViaElevator: InterchangeViaElevatorProtocol?
var order: String?
func takeOrder(_ food: String) {
print("What would you like?")
print("Yes, of course!")
order = food
sendOrderToCook()
}
private func sendOrderToCook() {
// ??? ?
}
private func serveFood() {
print("Your \(order!). Enjoy your meal!")
}
}
//
class Cook: InterchangeViaElevatorProtocol {
private let pan: Int = 1
private let stove: Int = 1
private func cookFood(_ food: String) -> Bool {
print("Let's take a pan")
print("Let's put \(food) on the pan")
print("Let's put the pan on the stove")
print("Wait a few minutes")
print("\(food) is ready!")
return true
}
// , ():
func cookOrder(order: String) -> Bool {
cookFood(order)
}
}
private order ( ).
:
- :
// :
let waiter = Waiter()
let cook = Cook()
// :
waiter.takeOrder("Chiken")
, " " – .
// , " " - :
waiter.receiverOfOrderViaElevator = cook
, , , .
" " :
// " " :
waiter.receiverOfOrderViaElevator?.cookOrder(order: waiter.order!)
, !
/*
What would you like?
Yes, of course!
Let's take a pan
Let's put Chiken on the pan
Let's put the pan on the stove
Wait a few minutes
Chiken is ready!
*/
« », .
«» , « », , .
private func sendOrderToCook() {
// cookOrder " ":
receiverOfOrderViaElevator?.cookOrder(order: order!)
}
! receiverOfOrderViaElevator, . . delegate, . , , .
? «, – . – UI?»
delegate UI?
UI , «» «». , table view collection view. table view collection view : . . () «» («Delegate»).
, Delegable «». , , !
, – . . Waiter. () hireWaiter. (, -):
// -
class Chief: InterchangeViaElevatorProtocol {
private let pan: Int = 1
private let stove: Int = 1
private func cookFood(_ food: String) -> Bool {
print("Let's take a pan")
print("Let's put \(food) on the pan")
print("Let's put the pan on the stove")
print("Wait a few minutes")
print("\(food) is ready!")
return true
}
// , ():
func cookOrder(order: String) -> Bool {
cookFood(order)
}
// - :
func hireWaiter() -> Waiter {
return Waiter()
}
}
- ( - hireWaiter):
// - (- ):
let chief = Chief()
// - :
let waiter = chief.hireWaiter()
// :
waiter.takeOrder("Chiken")
. , " " – -. .
// , " " - -:
waiter.receiverOfOrderViaElevator = chief
// " " :
waiter.receiverOfOrderViaElevator?.cookOrder(order: waiter.order!)
, .
. , -, , « » -.
class SmartChief: Chief {
override func hireWaiter() -> Waiter {
let waiter = Waiter()
waiter.receiverOfOrderViaElevator = self //
return waiter
}
}
SmartChief Chief .
, - (), . !
let smartChief = SmartChief()
let smartWaiter = smartChief.hireWaiter()
smartWaiter.takeOrder("Fish")
/*
What would you like?
Yes, of course we have Fish!
Let's take a pan
Let's put Fish on the pan
Let's put the pan on the stove
Wait a few minutes
Fish is ready!
*/
:
- (), , , , - .
- «» .
- (, ) , ( )
- , self «» .
, «» , , .
. , ! .
(, callback). ? ,
, , «» . , . ? ?
(callback) – , . . .
-,
, - ( , ). , , : «- ! , !» , , . .
.
. . , String Bool. cookFood ! - - .
///
class TalentedWaiter {
var order: String?
// . , String Bool.
var doEverything: ((String) -> Bool)?
func takeOrder(_ food: String) {
print("What would you like?")
print("Yes, of course we have \(food)!")
order = food
// - :
doOwnself()
}
private func doOwnself() -> Bool {
// , :
if let doEverything = doEverything {
let doOwnself = doEverything(order!)
return doOwnself
} else {
return false
}
}
}
-. , , . , . , :
// -
class LazyChief {
private let pan: Int = 1
private let stove: Int = 1
private func cookFood(_ food: String) -> Bool {
print("I have \(pan) pan")
print("Let's put \(food) on the pan!")
print("I have \(stove) stove. Let's put the pan on the stove!")
print("Wait a few minutes...")
print("\(food) is ready!")
return true
}
// :
func hireWaiter() -> TalentedWaiter {
let talentedWaiter = TalentedWaiter()
// . , cookFood:
talentedWaiter.doEverything = { order in
self.cookFood(order)
}
return talentedWaiter
}
}
-, , , :
let lazyChief = LazyChief()
let talentedWaiter = lazyChief.hireWaiter()
talentedWaiter.takeOrder("Meat")
/*
What would you like?
Yes, of course we have Meat!
I have 1 pan
Let's put Meat on the pan!
I have 1 stove. Let's put the pan on the stove!
Wait a few minutes...
Meat is ready!
*/
, , «» .
, , . , , ().
– . , self , - , .
[weak self] in . , !
talentedWaiter.doEverything = { [weak self] order in
self!.cookFood(order)
}
. , . !