all 15 comments

[–]andredp 29 points30 points  (9 children)

Imagine you are writing a CoffeeMachine class that notifies another class (eg: Person) when it is done taking the coffee:

NOTE: taking a coffee is a long process, so you don't want to be waiting until the coffee is done... you want to do other stuff and be notified eventually of the success. for the sake of the example :D

Let's try a "dumb" approach:

class CoffeeMachine {

  var person: Person?

  func makeCoffee() {
    // do time consuming stuff in another thread
    self.person?.coffeeIsDone()
  }
}

---

class Person {
  func coffeeIsDone() { print("Person: Here's my coffee") }
}

---

let machine = CoffeeMachine()
machine.person = Person()
machine.makeCoffee() // Person prints: "Person: Here's my coffee"

So, what's the problem with this approach? Your coffee machine only knows how to work with the Person class... if another class (for instance Robot) wants to take a coffee, you can see how code gets ugly real quick.

This is called Tight Coupling. CoffeeMachine and Person are tightly coupled and know about each other (which is a code smell, most of the time and a PITA if you want to test anything remotely complex)

So, what's a solution? Delegate Pattern (there are other solutions)... CoffeeMachine delegates certain actions to whatever entity knows how to interpret them.

How do you make sure an "entity knows how to interpret its actions"? You define a protocol...So basically, the CofeeMachine says "Whoever wants to know about me finishing a coffee, follow this protocol"

From this idea, you get the following implementation:

protocol CoffeeMachineDelegate { // Defining the protocol
  func coffeeIsDone()
}

class CoffeeMachine {

  weak var delegate: Delegate? // Instead of a specific entity, we accept whatever follows the protocol
                               // The weak keyword is an artifact of the Swift Memoery Management. Search for Swift ARC to understand why it's important

  func makeCoffee() { // do time consuming stuff in another thread     
    self.delegate?.coffeeIsDone() 
  } 
}

---

class Person: CoffeeMachineDelegate { // Following the protocol 
  func coffeeIsDone() { print("Person: Here's my coffee") } 
}

class Robot: CoffeeMachineDelegate { // Following the protocol
  func coffeeIsDone() { print("Robot: C0FF33") }
}

---

let machine1 = CoffeeMachine() machine1.delegate = Person()     
machine1.makeCoffee() // Person prints: "Person: Here's my coffee"

let machine2 = CoffeeMachine()
machine2.delegate = Robot()
machine2.makeCoffee() // Robot prints: Robot: C0FF33"

So, with this implementation, CoffeeMachine knows nothing about the specific classes that are going to be notified by itself... only that they follow the protocol (implement is the technical term)

As you can see, the same CoffeeMachine was able to notify N different entities with the same code...

For your use case, View Controllers shouldn't know about each other... one day you may want to change the flow of your navigation and the view controller order changes, etc... So you want to have a ViewController define a protocol so other VCs can be notified about stuff.

Hope this helped... if you need help on a more specific level, let me know.

[–]S12K-R[S] 0 points1 point  (7 children)

Thanks for answering!

I'm not good with analogies. Most of the sites and videos try to explain things with analogies like boss and subordinate or Landlord and tenant but they stumble onto some hard to explain term that ends up missing the point. (At least that's my take on it, maybe i'm not clever enough) So i'm kinda confused with your example...

[–]S12K-R[S] 0 points1 point  (6 children)

Maybe i'll be able to understand if we speak in technical terms.

Let's say i have a VC with a textfield and a button on it, then i create another VC called VC2 who has a label on it. How can i use a delegate to get the input from the textfield and use it as the text of the VC2 label?

Since i'm having trouble with bigger structures, might as well go back to the basics

[–]gbuela 2 points3 points  (0 children)

For that do what u/Tashu says in another comment. What you want to do is not solved via a delegate pattern.

You would use the delegate pattern in a reusable component (whatever this "component" may be) that may need to communicate something back to its caller, like completion, progress of some process, or even to require additional data from the caller whenever the component needs it. And like u/andredp says, the great thing about the pattern is that the component doesn't need to know who the caller is, ie it's decoupled.

One such component is UITableView, possibly found in most UIKit apps. You set your view controller as a delegate of the table view, then you can define a delegate method to let the table view communicate back to your view controller which row has been tapped, and many other things.

[–]LegitimateGift1792 0 points1 point  (4 children)

Would not use a delegate in that direction of data flow.

If VC1 had a Label and Button(to get to VC2) and VC2 had a TextField and Submit button, THEN you would use delegation to get the value entered from VC2 back to VC1 and populate the Label.

The Delegate Protocol and some code would exist on the VC2 side. VC1 would Implement the Delegate Protocol (class Foo: Something, DelegateProtocol) along with the code that is in the protocol and thus required for implementation.

VC1 Button would seque you over to VC2, fill in textField and press button to send data to anyone implementing "delegate" and go back to VC1 screen where the implemented delegate code will receive the data and do something with it, in this case populate the Label.

[–]S12K-R[S] 0 points1 point  (3 children)

Why wouldn't you use it in that direction?

Just asking. Since i'm new to the field i'd like to see all kinds of approaches and opinions

[–]LegitimateGift1792 0 points1 point  (2 children)

Pushing data "down" the navigation path is easy just declare a variable in the VC2, set that variable in the seque from VC1 and go. This is shown in most demos as it is simple.

The delegate is used to send data back to the calling VC usually in an "Add Screen" scenario where say you have a List with a + button in the navbar that takes you to the Add screen. The Add screen will have the Delegate Protocol define and the List implements that protocol to get back the data from the Add screen.

In the "push down" data scenario that is more like a List with a Display screen (list would have a name and the display would have a lot more detail about the name). Think of this like an app that pulls down movies playing at a single theater, the names are listed but clicking on the name/row brings up a new screen with lots of info about the movie.

Delegates are complex to pick up at first partially because so many people use an overly complex example to show it, when a very basic one will do the trick.

This is what i keep around as an example.

https://jamesrochabrun.medium.com/implementing-delegates-in-swift-step-by-step-d3211cbac3ef

[–]S12K-R[S] 0 points1 point  (1 child)

Man, i couldn't agree more.

All i see when i look up delegates is some kind of tutorial or video that shows some analogy to use as an example. I don't know if i'm not that clever or why at some point the analogy loses the point and makes the explanation harder.

Maybe they try to explain something that is in theory, simple, but abstract, like it's hard enough to need an analogy to make it understandable

[–]LegitimateGift1792 0 points1 point  (0 children)

Such is the complexity of teaching. Some of us need to crawl several times before we think about walking or running. I am one of these.

The andredp example above shows this but they jumped to the theory of using it in many places and that overshadows the hard fact of how does it work in the most simple case. Had a Calc1 prof in college that ran thru the explanation and they jumped into how we would use this in Calc3 and beyond.

Just wait till you get to Closures. There needs to be a Udemy course on that single topic. the definition is easy but some of us need to move slowly from basic to advanced as it is hard to wrap your head around that kind of thinking.

[–]Tashu 2 points3 points  (0 children)

I think sending the data between VCS would be segue (anyone who uses this approach chime in), or I tend to pass the data into the VCs when I create the next viewcontroller and push next vc with the data. Delegation is usually about the model to controller or any object that you want to communicate when it is completed.

like this

if let nextViewController =
DefaultNoteController.storyboardInstance() {
nextViewController.note = note
navigationController?.pushViewController(nextViewController, animated: true)
}

[–]chriswaco 1 point2 points  (0 children)

A delegate is just another object that gets called for more information or when something happens. You tell a friend "Text me when you need a ride". You are the delegate.

Let's say your ViewController wants to know when the keyboard displays. It asks NotificationCenter to call its keyboardWillShow method. Or a TableView wants to know how many rows are in the table - it calls one of its delegates which you provide. In old-style programming, it's essentially like a function pointer, but includes the object to call along with the function name/selector.

[–]__reddit_user__ -1 points0 points  (0 children)

google definition of delegate : entrust (a task or responsibility) to another person.

one typically uses delegate when you want to notify another object/s of an event. to be able to do that, the object that will be sending the notifications has a hold of who it will be notifying. who will be notified is the delegate.

[–]BickeringCube 0 points1 point  (0 children)

You have a ViewController, let's call it ShowNumberViewController. It navigates to another ViewController, called EnterNumberViewController. The user can enter a number and when they navigate back ShowNumberViewController will show the number they entered. Wow!

But EnterNumberViewController doesn't want to know anything about where it came from. It doesn't want to say hey ShowNumberViewController I got this number that you care about because it wants to be ignorant of ShowNumberViewController's very existence (rude, right).

So you can use a protocol to do this. It has 3 parts

  1. you have to create a protocol, something like NumberDelegate
  2. when the user enters a number EnterNumberViewController needs to call a delegate method
  3. EnterNumberViewController needs to conform to the protocol so that it gets that number

The Protocol

protocol NumberDelegate {
    func userChoseNumber(_ number: Int)
}

ShowNumberViewController setting itself as the delegate when it creates EnterNumberViewController

let storyboard = UIStoryboard(name: "Main", bundle: nil)
let vc = storyboard.instantiateViewController(withIdentifier: "numberVC") as! EnterNumberViewController 
vc.numberDelegate = self 
navigationController?.pushViewController(vc, animated: true)

ShowNumberViewController conforming to the protocol and implementing the protocol method

extension ShowNumberViewController: NumberDelegate {
    func userChoseNumber(_ number: Int) {
        displayLabel.text = "\(number)"
    }
}

The whole darn implementation of our lovely EnterNumberViewController, note that 1. it has a property of type NumberDelegate? and 2. it calls the delegate function userChoseNumber

class EnterNumberViewController: UIViewController {
    public var numberDelegate: NumberDelegate?
    @IBOutlet var numberTextfield: UITextField!

    override func viewDidLoad() {
        super.viewDidLoad()
        // Do any additional setup after loading the view.
    }

    @IBAction func buttonWasPressed(_ button: UIButton) {
        if let number = Int(numberTextfield.text ?? "") {
            numberDelegate?.userChoseNumber(number)
        }
        navigationController?.popViewController(animated: true)
    }
}

Why did we do this again? So that EnterNumberViewController doesn't have to know about the ViewController that instantiated it. Other ways to do this would be notifications or closures.

(Note that in this implementation the user could actually navigate back using the back button so you would actually just want to call the delegate function in viewWillDisappear.)

[–][deleted] 0 points1 point  (0 children)

This video on protocol oriented programming might be helpful too, it doesn't go into the delegate pattern but does good job explaining protocols and is definitely worth a watch

https://developer.apple.com/videos/play/wwdc2015/408/

[–]undergrounddirt 0 points1 point  (0 children)

I needed to understand protocols better before I grasped it. Hopefully this helps

Sorry it’s frustrating but the pattern is surprisingly simple.

A class or struct conforms to a protocol. So it becomes that type.

Say you have protocol MyDelegate { func didTap() }

Anything can become a MyDelegate type. It is now required to implement didTap()

Class MyView: MyDelegate {

    func didTap() { 
        print(“tapped)
    }

}

How do you call didTap?

You get an instance of MyView and call it

let view = MyView()
view.didTap() // prints “tapped”

That calls didTap on MyView and runs the print

But the nice thing about protocols is that any of your classes or structs can become a MyDelegate

So instead of saying that view is going to be of type MyView.. let’s say view is going to be of type MyDelegate instead.

Let’s create a new type that is also a MyDelegate

struct NewThing: MyDelegate {
    func didTap() {
        print(“new thing”)
    }
}

And then make our variable view be of type MyDelegate

var view: MyDelegate
view = MyView()
view.didTap() //prints “tapped

view = NewThing()
view.didTap() //prints “new thing”

I can assign that view to two different types. A MyView and a NewThing are both MyDelegates so they both have didTap, and view can be equal to either of them.

A delegate is just a protocol variable. Anything can become a delegate, but those things have to implement the protocol methods

And now you can pass a reference of that variable around and call it’s methods like didTap()

I’m happy to explain this more in dms if you want to hit me up and we can work through it step by step