When to init a delegate in Swift
Asked Answered
S

2

15

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!!

Spraddle answered 14/9, 2015 at 21:14 Comment(2)
A delegate should usually be declared weak or you might get reference cycle issues. See: en.wikipedia.org/wiki/…Granadilla
@WolfgangSchreurs In Swift, a protocol may also be implemented by a struct and you cannot have retain cycles with structs; same holds true for enums in Swift. To make the variable week, it's not enough that is has a protocol type, the protocol requires an object constraint for that. See my answer below.Redd
N
5

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
    }
}
Nowise answered 1/3, 2016 at 20:49 Comment(3)
Why Option 2 is better? @NowiseByng
Probably because it allows you not to set one, or set it laterFistulous
but if so far in your code every time you use 'MyClass' you set its delegate immediately, then you might as include it in the 'MyClass' initializer so you'll never forget to set it and get unexpected behavior. If down the line you later decide you want to use 'MyClass' without the delegate then at that point go ahead and remove it from the initializer but at least that will make you consider what you're doing and think about why you might do it that way. Start out restrictive and only allow for less restrictive behavior when you have to and have good reason to.Herdsman
R
2

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
}
Redd answered 21/4, 2020 at 15:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.