Initializers with delegates

I’d like to be able to require that a delegate be set on an object, but the object that is initializing this is supposed to be the delegate. You can see the example below.

The only reasonable way I’ve found to handle this is that I need to create a setup(withDelegate:) method, but this doesn’t make it a compile time requirement that you set the delegate property. There are other suggestions I’ve had, but none have given the compile time safety I want.

Does anyone have any other ideas that satisfy these requirements? It seems to me like it would have to be a change to the language.

Note: I know a delegate should usually be optional. In my case it’s not actually a delegate but a protocol for something, but this is to illustrate my point more easily.

protocol BDelegate: class {
    func foo()
}

class B {
    weak var delegate: BDelegate?
    init(delegate: BDelegate) {
        self.delegate = delegate
    }
}

class A: UIViewController, BDelegate {
    let b: B

    init() {
        b = B(delegate: self) // ERROR: using self before all properties initialized
        super.init(nibName: nil, bundle: nil)
    }

    func foo() {}
}

I assume you meant:

class A: UIViewController, BDelegate {
    let b: B

    init() {

Yes? In that case, the easiest solution is:

class A: UIViewController, BDelegate {
    private(set) var b: B!

    init() {
        super.init(nibName: nil, bundle: nil)
        b = B(delegate: self)
    }

Yes. That works however, then you have a var that could changed throughout the class which isn’t great and an IUO, which also isn’t great. I agree that does work.

Also, I just fixed the typo. Thanks.

Since B.delegate is optional, you can just do:

class A: UIViewController, BDelegate {
  let b: B
  
  init() {
    b = B(delegate: nil)
    b.delegate = self
  }
  
  func foo() {}
}

If B.delegate were non-optional, you could do this:

private class BDelegateDummy: BDelegate {
  func foo() {}
}

class A: UIViewController, BDelegate {
  let b: B
  
  init() {
    b = B(delegate: BDelegateDummy())
    b.delegate = self
  }
  
  func foo() {}
}

Clever. However, the whole idea is that I want to make sure someone doesn't set it to nil. So, I would want to keep it non-optional.

The dummy I think is going a bit too far. And then you have to make the delegate property settable from outside, which isn't great.

Well, there were 2 typos. (The other one was the unnecessary parameter to init.)

I understand your objections to an IUO, but is it really a practical concern, as opposed to a vague unease?

Whoops. Thanks. Fixed that typo too. I guess I should’ve read it over more carefully or done it in a playground.

The IUO is more of a cleanliness and principle kind of thing. The var aspect is more of the issue, I’d say. It’s still probably the best solution.

You can use a willSet accessor to prevent the property ever being set again.

Wouldn’t a lazy var simplify the code with the same issue above (potentially solved by the willSet accessor)?

lazy var b = B(delegate: self)

I think lazy let has been discussed somewhere, but I'm not 100% sure.