Delegates in swift?
Asked Answered
A

15

145

How does one go about making a delegate, i.e. NSUserNotificationCenterDelegate in swift?

Abruzzi answered 7/6, 2014 at 16:23 Comment(1)
Do you mean implementing a delegate or defining your own delegate?Trollop
L
74

It is not that different from obj-c. First, you have to specify the protocol in your class declaration, like following:

class MyClass: NSUserNotificationCenterDelegate

The implementation will look like following:

// NSUserNotificationCenterDelegate implementation
func userNotificationCenter(center: NSUserNotificationCenter, didDeliverNotification notification: NSUserNotification) {
    //implementation
}

func userNotificationCenter(center: NSUserNotificationCenter, didActivateNotification notification: NSUserNotification) {
    //implementation
}

func userNotificationCenter(center: NSUserNotificationCenter, shouldPresentNotification notification: NSUserNotification) -> Bool {
    //implementation
    return true
}

Of course, you have to set the delegate. For example:

NSUserNotificationCenter.defaultUserNotificationCenter().delegate = self;
Landed answered 7/6, 2014 at 16:47 Comment(2)
What happens when you want to extend UIViewController, for example, in objective-c, you can have something lie this @interface MyCustomClass: UIViewController <ClassIWantToUseDelegate>, allowing you to init/configure the viewcontroller, as well as call delegate methods on the subviews? Something similar to this?Panier
Hi Adam, quick question, how can I set delegate = self, if I cannot instantiate an object because it is a generic class which I don't have access to in the other class, yet I want the generics class to call a function in the other class, hence the need for delegate?Antebellum
R
251

Here's a little help on delegates between two view controllers:

Step 1: Make a protocol in the UIViewController that you will be removing/will be sending the data.

protocol FooTwoViewControllerDelegate:class {
    func myVCDidFinish(_ controller: FooTwoViewController, text: String)
}

Step2: Declare the delegate in the sending class (i.e. UIViewcontroller)

class FooTwoViewController: UIViewController {
    weak var delegate: FooTwoViewControllerDelegate?
    [snip...]
}

Step3: Use the delegate in a class method to send the data to the receiving method, which is any method that adopts the protocol.

@IBAction func saveColor(_ sender: UIBarButtonItem) {
        delegate?.myVCDidFinish(self, text: colorLabel.text) //assuming the delegate is assigned otherwise error
}

Step 4: Adopt the protocol in the receiving class

class ViewController: UIViewController, FooTwoViewControllerDelegate {

Step 5: Implement the delegate method

func myVCDidFinish(_ controller: FooTwoViewController, text: String) {
    colorLabel.text = "The Color is " +  text
    controller.navigationController.popViewController(animated: true)
}

Step 6: Set the delegate in the prepareForSegue:

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    if segue.identifier == "mySegue" {
        let vc = segue.destination as! FooTwoViewController
        vc.colorString = colorLabel.text
        vc.delegate = self
    }
}

And that should work. This is of course just code fragments, but should give you the idea. For a long explanation of this code you can go over to my blog entry here:

segues and delegates

If you are interested in what's going on under the hood with a delegate I did write on that here:

under the hood with delegates

Rinee answered 11/9, 2014 at 16:5 Comment(5)
Step2 shouldyn't be there weak reference to delegate ? if I'm I right please edit it. Btw you can make it optional value. That would be more swift. weak var delegate:FooTwoViewControllerDelegate? PS: delegate should be weak cus of retain circle, child shouldynt keep strong reference to parentCreak
In my way when you will make delegate optional you will resolve you unwraping error. delegate?.myVCDidFinish Becouse if delegate is not set the cod wont execute now :) In your version it will try to execute and will fail to unwrap if delegate is nil and you it is.Creak
you need to declare protocol like this in order to make weak reference possible for delegate protocol FooTwoViewControllerDelegate:class{}Walloon
Could you please set by each step in which VC you are like VC1 and VC2. I am not really sure where to put them.Oatcake
@Creak - Actually it appears to be a little complicated. weak is only needed for classes not structs and enums. If the delegate is going to be a struct or enum then you don't need to worry about retain cycles. However, the delegate its a class (this is true for a lot of cases since quite often its a ViewController), then you need weak but you need to declare your protocol as a class. There is more info here https://mcmap.net/q/23591/-how-can-i-make-a-weak-protocol-reference-in-39-pure-39-swift-without-objcLyndell
F
124

Delegates always confused me until I realized that a delegate is just a class that does some work for another class. It's like having someone else there to do all the dirty work for you that you don't want to do yourself.

I wrote a little story to illustrate this. Read it in a Playground if you like.

Once upon a time...

// MARK: Background to the story

// A protocol is like a list of rules that need to be followed.
protocol OlderSiblingDelegate: class {
    // The following command (ie, method) must be obeyed by any 
    // underling (ie, delegate) of the older sibling.
    func getYourNiceOlderSiblingAGlassOfWater()
}

// MARK: Characters in the story

class BossyBigBrother {
    
    // I can make whichever little sibling is around at 
    // the time be my delegate (ie, slave)
    weak var delegate: OlderSiblingDelegate?
    
    func tellSomebodyToGetMeSomeWater() {
        // The delegate is optional because even though 
        // I'm thirsty, there might not be anyone nearby 
        // that I can boss around.
        delegate?.getYourNiceOlderSiblingAGlassOfWater()
    }
}

// Poor little sisters have to follow (or at least acknowledge) 
// their older sibling's rules (ie, protocol)
class PoorLittleSister: OlderSiblingDelegate {

    func getYourNiceOlderSiblingAGlassOfWater() {
        // Little sis follows the letter of the law (ie, protocol),
        // but no one said exactly how she had to respond.
        print("Go get it yourself!")
    }
}

// MARK: The Story

// Big bro is laying on the couch watching basketball on TV.
let bigBro = BossyBigBrother()

// He has a little sister named Sally.
let sally = PoorLittleSister()

// Sally walks into the room. How convenient! Now big bro 
// has someone there to boss around.
bigBro.delegate = sally

// So he tells her to get him some water.
bigBro.tellSomebodyToGetMeSomeWater()

// Unfortunately no one lived happily ever after...

// The end.

In review, there are three key parts to making and using the delegate pattern.

  1. the protocol that defines what the worker needs to do
  2. the boss class that has a delegate variable, which it uses to tell the worker class what to do
  3. the worker class that adopts the protocol and does what is required

Real life

In comparison to our Bossy Big Brother story above, delegates are often used for the following practical applications:

  1. Communication: one class needs to send some information to another class.
  2. Customization: one class wants to allow another class to customize it.

The great part is that these classes don't need to know anything about each other beforehand except that the delegate class conforms to the required protocol.

I highly recommend reading the following two articles. They helped me understand delegates even better than the documentation did.

One more note

Delegates that reference other classes that they do not own should use the weak keyword to avoid strong reference cycles. See this answer for more details.

Flapper answered 5/11, 2015 at 16:23 Comment(4)
Finally someone that can explain protocol and delegate with common sense! thanks man!Maeda
What happens when Bossy Big Brother doesn't know he's a brother (Generics) ?Antebellum
@Marin, I'm not really sure that I understand your question. The list of rules (protocol) doesn't care who it is that is calling for the rules to be followed or who is following the rules. They are just rules.Flapper
Basically I am referring to my question, slightly simplified over here. #41195703Antebellum
L
74

It is not that different from obj-c. First, you have to specify the protocol in your class declaration, like following:

class MyClass: NSUserNotificationCenterDelegate

The implementation will look like following:

// NSUserNotificationCenterDelegate implementation
func userNotificationCenter(center: NSUserNotificationCenter, didDeliverNotification notification: NSUserNotification) {
    //implementation
}

func userNotificationCenter(center: NSUserNotificationCenter, didActivateNotification notification: NSUserNotification) {
    //implementation
}

func userNotificationCenter(center: NSUserNotificationCenter, shouldPresentNotification notification: NSUserNotification) -> Bool {
    //implementation
    return true
}

Of course, you have to set the delegate. For example:

NSUserNotificationCenter.defaultUserNotificationCenter().delegate = self;
Landed answered 7/6, 2014 at 16:47 Comment(2)
What happens when you want to extend UIViewController, for example, in objective-c, you can have something lie this @interface MyCustomClass: UIViewController <ClassIWantToUseDelegate>, allowing you to init/configure the viewcontroller, as well as call delegate methods on the subviews? Something similar to this?Panier
Hi Adam, quick question, how can I set delegate = self, if I cannot instantiate an object because it is a generic class which I don't have access to in the other class, yet I want the generics class to call a function in the other class, hence the need for delegate?Antebellum
C
48

I got few corrections to post of @MakeAppPie

First at all when you are creating delegate protocol it should conform to Class protocol. Like in example below.

protocol ProtocolDelegate: class {
    func myMethod(controller:ViewController, text:String)
}

Second, your delegate should be weak to avoid retain cycle.

class ViewController: UIViewController {
    weak var delegate: ProtocolDelegate?
}

Last, you're safe because your protocol is an optional value. That means its "nil" message will be not send to this property. It's similar to conditional statement with respondToselector in objC but here you have everything in one line:

if ([self.delegate respondsToSelector:@selector(myMethod:text:)]) {
    [self.delegate myMethod:self text:@"you Text"];
}

Above you have an obj-C example and below you have Swift example of how it looks.

delegate?.myMethod(self, text:"your Text")
Creak answered 29/1, 2015 at 20:38 Comment(1)
you're safe because your protocol is an optional value..... because you you use optional chaining delegate?.myMethod won't crash because if delegate is nil then nothing would happen. However if you made mistake and wrote delegate!.myMethod you could crash if a delegate is not set, so its basically a way for you to be safe...Beekman
L
35

Here's a gist I put together. I was wondering the same and this helped improve my understanding. Open this up in an Xcode Playground to see what's going on.

protocol YelpRequestDelegate {
    func getYelpData() -> AnyObject
    func processYelpData(data: NSData) -> NSData
}

class YelpAPI {
    var delegate: YelpRequestDelegate?

    func getData() {
        println("data being retrieved...")
        let data: AnyObject? = delegate?.getYelpData()
    }

    func processYelpData(data: NSData) {
        println("data being processed...")
        let data = delegate?.processYelpData(data)
    }
}

class Controller: YelpRequestDelegate {
    init() {
        var yelpAPI = YelpAPI()
        yelpAPI.delegate = self
        yelpAPI.getData()
    }
    func getYelpData() -> AnyObject {
        println("getYelpData called")
        return NSData()
    }
    func processYelpData(data: NSData) -> NSData {
        println("processYelpData called")
        return NSData()
    }
}

var controller = Controller()
Lanner answered 18/4, 2015 at 23:59 Comment(5)
Love this. Very helpfulEichman
@Lanner Hi, It was good example firstly, but i still have an issue. How can i make my any UIViewController class to conform delegate we made? Are they have to be declared in one swift file? Any help will mean a lot.Duclos
@Duclos It's been awhile since I posted this, but I think what you're asking should be pretty simple (If I'm misunderstanding, I apologize). Just add the delegate to your UIViewController after the colon. So something like class ViewController : UIViewController NameOfDelegate.Lanner
@Lanner yes, you got my question well. I tried your suggestion btw, but when I create a delegate class in a.swift according to your answer above, it doesn't come up in b.swift. I cannot reach any class outside of my swift file. any toughts?Duclos
one thing i don't understand is why should I create a new instance of YelpApi just so that i call YelpApi's delegate ? What if the instance that's running is different from the 'new' one I just created... how does it know which delegate belongs to which instance of YelpApi ?Antebellum
U
18

DELEGATES IN SWIFT 2

I am explaining with example of Delegate with two viewControllers.In this case, SecondVC Object is sending data back to first View Controller.

Class with Protocol Declaration

protocol  getDataDelegate  {
    func getDataFromAnotherVC(temp: String)
}


import UIKit
class SecondVC: UIViewController {

    var delegateCustom : getDataDelegate?
    override func viewDidLoad() {
        super.viewDidLoad()
     }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
        // Dispose of any resources that can be recreated.
    }
    @IBAction func backToMainVC(sender: AnyObject) {
      //calling method defined in first View Controller with Object  
      self.delegateCustom?.getDataFromAnotherVC(temp: "I am sending data from second controller to first view controller.Its my first delegate example. I am done with custom delegates.")
        self.navigationController?.popViewControllerAnimated(true)
    }
    
}

In First ViewController Protocol conforming is done here:

class ViewController: UIViewController, getDataDelegate

Protocol method definition in First View Controller(ViewController)

func getDataFromAnotherVC(temp : String)
{
  // dataString from SecondVC
   lblForData.text = dataString
}

During push the SecondVC from First View Controller (ViewController)

let objectPush = SecondVC()
objectPush.delegateCustom = self
self.navigationController.pushViewController(objectPush, animated: true)
Unintelligent answered 13/5, 2016 at 7:9 Comment(2)
Your last 3 lines helped me to understand my scenario and solved my issue. Thanks Man! :)Stannic
Working For Men.....All
E
6

First class:

protocol NetworkServiceDelegate: class {
        
    func didCompleteRequest(result: String)
}


class NetworkService: NSObject {

    weak var delegate: NetworkServiceDelegate?
    
    func fetchDataFromURL(url : String) {
        delegate?.didCompleteRequest(result: url)
    }
}

Second class:

class ViewController: UIViewController, NetworkServiceDelegate {
    
    let network = NetworkService()
    
    override func viewDidLoad() {
        super.viewDidLoad()
        network.delegate = self
        network.fetchDataFromURL(url: "Success!")
    }

 
    
    func didCompleteRequest(result: String) {
        print(result)
    }


}
Eightfold answered 7/9, 2016 at 13:33 Comment(1)
on compiling above code it shows error Type 'ViewController' does not conform to protocol 'NetworkServiceDelegate' plz suggest. It is my 6th day on swift :)Precis
Q
4

Very easy step by step (100% working and tested)

step1: Create method on first view controller

 func updateProcessStatus(isCompleted : Bool){
    if isCompleted{
        self.labelStatus.text = "Process is completed"
    }else{
        self.labelStatus.text = "Process is in progress"
    }
}

step2: Set delegate while push to second view controller

@IBAction func buttonAction(_ sender: Any) {

    let secondViewController = self.storyboard?.instantiateViewController(withIdentifier: "secondViewController") as! secondViewController
    secondViewController.delegate = self
    self.navigationController?.pushViewController(secondViewController, animated: true)
}

step3: set delegate like

class ViewController: UIViewController,ProcessStatusDelegate {

step4: Create protocol

protocol ProcessStatusDelegate:NSObjectProtocol{
func updateProcessStatus(isCompleted : Bool)
}

step5: take a variable

var delegate:ProcessStatusDelegate?

step6: While go back to previous view controller call delegate method so first view controller notify with data

@IBAction func buttonActionBack(_ sender: Any) {
    delegate?.updateProcessStatus(isCompleted: true)
    self.navigationController?.popViewController(animated: true)
}

@IBAction func buttonProgress(_ sender: Any) {
    delegate?.updateProcessStatus(isCompleted: false)
    self.navigationController?.popViewController(animated: true)

}
Quarterage answered 8/3, 2018 at 9:41 Comment(0)
D
3

Simple Example:

protocol Work: class {
    func doSomething()
}

class Manager {
    weak var delegate: Work?
    func passAlong() {
        delegate?.doSomething()
    }
}

class Employee: Work {
    func doSomething() {
        print("Working on it")
    }
}

let manager = Manager()
let developer = Employee()
manager.delegate = developer
manager.passAlong() // PRINTS: Working on it
Digital answered 8/5, 2017 at 20:28 Comment(2)
why do u use keyword "class" in the protocol description? what's the difference in using and not using it?Accelerate
The class keyword means it's a class only protocol. You can limit protocol adoption to class types, and not structures or enumerations, by adding the class keyword. I probably should not have added it to avoid any confusion, but since you asked I will keep.Digital
H
3

Here Simple Code Example of Delegate:

//MARK: - Protocol ShowResult
protocol ShowResult: AnyObject {
       func show(value: Int) 
  }

//MARK: - MyOperation Class
class MyOperation {
weak var delegate: ShowResult?

func sum(fNumber: Int, sNumber: Int) {
    delegate?.show(value: fNumber + sNumber)
  }
}

//MARK: - ViewController Class
class ViewController: UIViewController,ShowResult {
var myOperation: MyOperation?

override func viewDidLoad() {
    super.viewDidLoad()
    loadMyOperation()
    myOperation?.delegate = self
    myOperation?.sum(fNumber: 100, sNumber: 20)
 }

private func loadMyOperation() {
    if myOperation == nil {
        myOperation = MyOperation()
     }
 }

func show(value: Int) {
    print("value: \(value)")
   }
}
Hettiehetty answered 2/12, 2021 at 6:53 Comment(0)
M
2

Delegates are a design pattern that allows one object to send messages to another object when a specific event happens. Imagine an object A calls an object B to perform an action. Once the action is complete, object A should know that B has completed the task and take necessary action, this can be achieved with the help of delegates! Here is a tutorial implementing delegates step by step in swift 3

Tutorial Link

Merchantman answered 17/2, 2017 at 21:4 Comment(0)
A
2

Here is real life delegate scenario
Lets make our own UITextField and UITextFieldDelegate

// THE MYSTERIOUS UITEXTFIELD

protocol UITextFieldDelegate {
    func textFieldDidChange(_ textField: UITextField) -> Void
}

class UITextField {
    var delegate: UITextFieldDelegate?
    private var mText: String?
    var text: String? {
        get {
            return mText
        }
    }
    
    init(text: String) {
        
    }
    
    init() {
        
    }
    
    func setText(_ text: String) {
        mText = text
        delegate?.textFieldDidChange(self)
    }

}
// HERE IS MY APP

class Main {
    
    let textfield = UITextField()
    
    func viewDidLoad() {
        print("viewDidLoad")
        textfield.delegate = self
        textfield.setText("Hello")
    }
    
}

extension Main: UITextFieldDelegate {
    
    func textFieldDidChange(_ textField: UITextField) {
        print(textField.text ?? "No string")
    }
    
}

let main = Main()
main.viewDidLoad()

Anatto answered 6/11, 2021 at 22:16 Comment(0)
Z
0

The solutions above seemed a little coupled and at the same time avoid reuse the same protocol in other controllers, that's why I've come with the solution that is more strong typed using generic type-erasure.

@noreturn public func notImplemented(){
    fatalError("not implemented yet")
}


public protocol DataChangedProtocol: class{
    typealias DataType

    func onChange(t:DataType)
}

class AbstractDataChangedWrapper<DataType> : DataChangedProtocol{

    func onChange(t: DataType) {
        notImplemented()
    }
}


class AnyDataChangedWrapper<T: DataChangedProtocol> : AbstractDataChangedWrapper<T.DataType>{

    var base: T

    init(_ base: T ){
        self.base = base
    }

    override func onChange(t: T.DataType) {
        base.onChange(t)
    }
}


class AnyDataChangedProtocol<DataType> : DataChangedProtocol{

    var base: AbstractDataChangedWrapper<DataType>

    init<S: DataChangedProtocol where S.DataType == DataType>(_ s: S){
        self.base = AnyDataChangedWrapper(s)
    }

    func onChange(t: DataType) {
        base.onChange(t)
    }
}



class Source : DataChangedProtocol {
    func onChange(data: String) {
        print( "got new value \(data)" )
    }
}


class Target {
    var delegate: AnyDataChangedProtocol<String>?

    func reportChange(data:String ){
        delegate?.onChange(data)
    }
}


var source = Source()
var target = Target()

target.delegate = AnyDataChangedProtocol(source)
target.reportChange("newValue")    

output: got new value newValue

Zsa answered 28/1, 2016 at 19:4 Comment(1)
I'm interested in learning more about this. Can you explain more about the terms you use: coupled, "avoid reuse the same protocol", "generic type-erasure". Why is abstracting it like this important? Should one always do this?Flapper
H
0

In swift 4.0

Create a delegate on class that need to send some data or provide some functionality to other classes

Like

protocol GetGameStatus {
    var score: score { get }
    func getPlayerDetails()
}

After that in the class that going to confirm to this delegate

class SnakesAndLadders: GetGameStatus {
    func getPlayerDetails() {

 }
}
Heller answered 27/7, 2017 at 4:22 Comment(0)
H
0

In swift 5

I am a beginner, I think this is easiest way to understand in practical scenario

Note:Any improvisations are most appreciated

protocol APIService {
   func onSuccessResponse() -> AnyObject
   func onFailureResponse() -> AnyObject
}

class APIHelper{

var delegate : APIService?

func postUsersDataAPI() {
    //assuming API communication is success
    if(success){
       let _: AnyObject? = delegate?.onSuccessResponse()
    }else if(failure){
       let _: AnyObject? = delegate?.onFailureResponse()
  }
 }

func getAllUsersAPI() {
    //assuming API communication is success
   if(success){
       let _: AnyObject? = delegate?.onSuccessResponse()
    }else if(failure){
       let _: AnyObject? = delegate?.onFailureResponse()
  }
 }
}


class ViewController:UIViewController,APIService {

func onSuccessResponse() -> AnyObject {
    print("onSuccessResponse") as AnyObject
}

func onFailureResponse() -> AnyObject {
    print("onFailureResponse") as AnyObject
}

@IBAction func clickBtnToPostUserData(_ sender: Any) { 
    let apiHelper = APIHelper()
    apiHelper.delegate = self
    apiHelper.postAPI()
}
Hulda answered 4/5, 2022 at 20:3 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.