What exactly happens when you assign self to delegate?

Viewed 548

I'm new to Swift and I'm having a hard time understanding the purpose of assigning self to a delegate. Part of the difficulty stems from the fact that delegate seems to be used in two different ways.

First is as means to send messages from one class to another when a specific event happens, almost like state management. Second is to enable "a class or structure to hand off (or delegate) some of its responsibilities to an instance of another type," as stated in documentation. I have a feeling that these two are fundamentally the same and I'm just not getting it.

protocol PersonProtocol {
    func getName() -> String
    func getAge() -> Int
}

class Person {
    var delegate: PersonProtocol?
    
    func printName() {
        if let del = delegate {
            print(del.getName())
        } else {
            print("The delegate property is not set")
        }
    }
    
    func printAge() {
        if let del = delegate {
            print(del.getAge())
        } else {
            print("The delegate property is not set")
        }
    }
}

class ViewController: UIViewController, PersonProtocol {
    var person: Person!
    
    override func viewDidLoad() {
        person.delegate = self
        
        person.printAge()
        person.printName()
    }
    
    func getAge() -> Int {
        print("view controller")
       return 99
    }
    
    func getName() -> String {
        return "Some name"
    }
}

What is the purpose of person.delegate = self in this case? Isn't ViewController already required to conform to PersonProtocol without it?

3 Answers

If you notice inside your Person class, delegate is nil. If you don't execute person.delegate = self, delegate will remain nil.

In other words, assigning ViewController to person.delegate allows Person to identify who the delegate is (i.e., have a reference to ViewController), and that way you can successfully execute statements like delegate?.getName() or delegate?.getAge() from the Person class.

I have a feeling that these two are fundamentally the same

The first is a special case of the second. "send messages from one class to another" is just a specific way of "handing off some of its responsibilities". The "messages" are the "responsibilities"

What is the purpose of person.delegate = self in this case?

Here, person delegates (i.e. hands off) some of its responsibilities to another object. It does this by sending messages to another object. First, it needs to identify which objects it can delegate these responsibilities to. This is achieved by requiring that its delegate conform to PersonProtocol, as PersonProtocol defines the messages that Person is going to send.

Next, person needs to know exactly which object it should send these messages to. This is what person.delegate = self does. Remember that person doesn't know anything about your ViewController until this point. Instead of = self, you could say:

person.delegate = SomeOtherClassThatConformsToPersonProtocol()

and person will send its messages to that object instead, and the methods in your ViewController won't be called.

Isn't ViewController already required to conform to PersonProtocol without it?

Correct, but without it, person doesn't know which object it should send its messages to, and as a result, the methods in your ViewController won't be called.


Note that the delegate property should be declared as weak to avoid retain cycles. When you do person.delegate = self, you get a retain cycle: self has a strong reference to person, person also has a strong reference to self via the delegate property.

that means Person is not able to getName() and getAge() so Person class delegate that to other DataSource. Lets say the your view controller has a data source class PersonDataSource which deal with API to get this information So

class PersonDataSource: PersonProtocol { 
    func getAge() -> Int {
        print("view controller")
       return 99
    }
    
    func getName() -> String {
        return "Some name"
    }
 } 

so the view controller will looks like this

class ViewController: UIViewController {
    var person: Person!
    var personDataSource = PersonDataSource()
    override func viewDidLoad() {
        person.delegate = personDataSource
        
        person.printAge()
        person.printName()
    }
}
Related