Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When to init a delegate in Swift

I have a basic question. I'm working on a project with many delegate patterns, and would like reference on the best way about initializing them..

Here's some ideas with a test delegate I made:

Option 1:

It fails because I'm initilizing the delegate to self before super.init()

protocol MyClassDelegate {
    func doSomething()
}

class MyClass {

    var delegate: MyClassDelegate!

    init(delegate: MyClassDelegate){
        self.delegate = delegate
    }

    func myClassFuction(){
        self.delegate.doSomething()
    }
}


class ClassConformingToDelegate: NSObject, MyClassDelegate {

    let myClass: MyClass

    override init(){
        myClass = MyClass(delegate: self) // Error because it's called before super.init
        super.init()
    }        

    func doSomething(){
        //called from the delegate
    }
}

Option 2:

It works, but then I risk having the delegate be nil.. Would I have to run a 'if delegate != nil' check each time I want to call a method? Is there a way around that? Is it a good practice to init the delegate this way?

protocol MyClassDelegate {
    func doSomething()
}

class MyClass {

    var delegate: MyClassDelegate!

    init(){
    }

    func myClassFuction(){
        self.delegate.doSomething() // might fail if delegate is nil
    }
}


class ClassConformingToDelegate: NSObject, MyClassDelegate {

    let myClass: MyClass

    override init(){
        myClass = MyClass()
        super.init()
        myClass.delegate = self // works because called after super.init
    }        

    func doSomething(){
        //called from the delegate
    }
}

Those were just a couple ideas. I'm just trying to learn the best way of doing it. I'm open to all suggestions.

Thanks!!

like image 670
JoeBayLD Avatar asked Sep 14 '15 21:09

JoeBayLD


2 Answers

Use lazy initialization to work around that issue.

protocol MyClassDelegate: class {

    func doSomething()

}


class MyClass {

    private(set) weak var delegate: MyClassDelegate?

    func myClassFuction ( ) {
        self.delegate?.doSomething()
    }

    init ( delegate: MyClassDelegate ) {
        self.delegate = delegate
    }

}


class ClassConformingToDelegate: NSObject, MyClassDelegate {

    lazy private(set) var myClass: MyClass = {
            return MyClass(delegate: self)
        }()

    func doSomething ( ) {
        //called from the delegate
    }
}

The variable myClass, which has no public setter, will be initialized the first time it is accessed by ClassConformingToDelegate itself or by some external code. If you want to make sure that it is always initialized when creating a new ClassConformingToDelegate object (as its sole creation may have side effects, like registration to notifications and so on), just access it from init:

override
init ( ) {
    super.init()
    _ = self.myClass
}
like image 60
Mecki Avatar answered Sep 18 '22 14:09

Mecki


Option 2 looks better. However, in this case it would be advisable to use an optional as your delegate type. That way, you don't need to check "if delegate != nil" every time you want to perform a task with it. You can simply use optional chaining, which is designed specifically for such cases where you want to perform a task on an optional only if it contains a value.

protocol MyClassDelegate {
    func doSomething()
}

class MyClass {

    var delegate: MyClassDelegate?

    init(){
    }

    func myClassFuction(){
        self.delegate?.doSomething() // will do nothing if delegate is nil
    }
}


class ClassConformingToDelegate: NSObject, MyClassDelegate {

    let myClass: MyClass

    override init(){
        myClass = MyClass()
        super.init()
        myClass.delegate = self
    }        

    func doSomething(){
        //called from the delegate
    }
}
like image 29
vinn Avatar answered Sep 18 '22 14:09

vinn