Search code examples
swiftdelegatesprotocolsinitializer

Property 'self.myDelegate' not initialized at super.init - Delegate


I am experimenting with Protocols and Delegates and I am stuck while initializing the delegate.

At the top my of my class, I tried adding

protocol myDelegateProtocol {
   func clickedCellIndexPath(indexPath: Int)
}

class MyClass {

     var myDelegate : myDelegateProtocol


     override init() {
        super.init()
        self.setup()     // error 1
     }

     required init?(coder aDecoder: NSCoder) {
        super.init(coder: aDecoder)  // error 2
     }

}

It started giving errors for both init closures

Both errors: Property 'self.myDelegate' not initialized at super.init

What am I missing or doing wrong?


If I try declaring it as var myDelegate: myDelegateProtocol?, it doesn't give that error, but it forces me to force unwrap myDelegate! and it returns nil at this point.

Edit: If I force it unwrap at the top, then I receive error in the following parts..

 ...
var myDelegate : myDelegateProtocol!

func handleTap(sender: UITapGestureRecognizer? = nil) {

    if let point = sender?.locationInView(collectionView) {
        print("Y")
        let clickedCell = collectionView!.indexPathForItemAtPoint(point)!.row

        print(clickedCell)  // prints

        self.myDelegate.clickedCellIndexPath(clickedCell) // error here
    }
}

Edit 2:

So my complete code..:

protocol myDelegateProtocol {
   func clickedCellIndexPath(indexPath: Int)
}

class MyClass {
     var myDelegate: KDRearrangableClickedCellDelegate!

     override init() {
         super.init()
         self.setup()
      }

     required init?(coder aDecoder: NSCoder) {
         super.init(coder: aDecoder)
     }

    func setup() {
         if let collectionView = self.collectionView {
             let tap = UITapGestureRecognizer(target: self, action: #selector(KDRearrangeableCollectionViewFlowLayout.handleTap(_:)))
        tap.delegate = self
        collectionView.addGestureRecognizer(tap)
          }
     }

    func handleTap(sender: UITapGestureRecognizer? = nil) {

         if let point = sender?.locationInView(collectionView) {

             let clickedCell = collectionView!.indexPathForItemAtPoint(point)!.row
             print(clickedCell)

             self.myDelegate.clickedCellIndexPath(clickedCell)
         }
    }

And I receive this error at this point:

fatal error: unexpectedly found nil while unwrapping an Optional value


Solution

  • As others have pointed out, because myDelegate is not initialized in init, you must make it optional. For example:

    var myDelegate: KDRearrangableClickedCellDelegate?
    

    Then when you need to use it, unwrap this optional, e.g.:

    func handleTap(sender: UITapGestureRecognizer) {
        if let point = sender.locationInView(collectionView) {
            if let clickedCell = collectionView?.indexPathForItemAtPoint(point)?.item {
                print(clickedCell)
                myDelegate?.clickedCellIndexPath(clickedCell)
            }
        }
    }
    

    Personally, I'd recommend the use of an optional (KDRearrangableClickedCellDelegate?) rather than an implicitly unwrapped one (KDRearrangableClickedCellDelegate!), so that if you haven't yet set myDelegate, your app won't crash. Clearly, if you want clickedCellIndexPath to be called, you have to actually set myDelegate. I'm also doing optional binding of the clickedCell, so that if the tap wasn't on a cell, it won't crash. Also, I'm using item rather than row, as collection views generally don't use item/section rather than row/section.

    You don't show us how you set collectionView nor how you set myDelegate, but for the above to work, you need to set both of those before the gestures can be handled correctly.


    As an aside, I don't know what the relationship is between MyClass and its myDelegate, but often you make delegates weak to avoid strong reference cycles. To do that, define your protocol as a class protocol:

    protocol myDelegateProtocol : class {
       func clickedCellIndexPath(indexPath: Int)
    }
    

    And then define your myDelegate to be weak:

    weak var myDelegate: KDRearrangableClickedCellDelegate?
    

    You only need to do this is if the object that will be the myDelegate for MyClass will, itself, maintain a strong reference to MyClass. If not, you may not need to make your delegate weak. It just depends upon your object model, which you haven't shared with us.