1

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

senty
  • 12,385
  • 28
  • 130
  • 260
  • Some related reading on the topic can be found [here](http://stackoverflow.com/a/36612507/2792531) and [here](http://stackoverflow.com/a/32108404/2792531). Basically, you fundamentally don't understand how Swift's initializer's work... – nhgrif Apr 14 '16 at 23:47

3 Answers3

1

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.

Rob
  • 415,655
  • 72
  • 787
  • 1,044
0

You've declared myDelegate as non-Optional, which means that it must be initialized in your init() methods before calling super.init(). If your setup() method is where you're assigning a value to myDelegate (which I'm guessing is the case, although you haven't included your setup()'s implementation here), then you need to call setup() before super.init(), and/or declare myDelegate to be Optional (with a '?').

NRitH
  • 13,441
  • 4
  • 41
  • 44
  • Can you please add a code example in your answer to make it clear for me. I tried moving it in to both initialisers, but I couldn't make it work :/ – senty Apr 14 '16 at 23:48
0

This is line that is causing the problem:

var myDelegate : myDelegateProtocol

myDelegate is declared as non-optional but you did not initialise it in your init function. If you are initialising it in your setup() function, you need to change the declaration of myDelegate to like this.

var myDelegate : myDelegateProtocol!
Christian Abella
  • 5,747
  • 2
  • 30
  • 42
  • I added an edit part in my question. When I change it to !, then it causes further problems. What am I missing? :/ – senty Apr 14 '16 at 23:57
  • Can you show the rest of your code? like the setup function. Just to be safe. You need to check if myDelegate is not null before call the function clickedCellIndexPath – Christian Abella Apr 15 '16 at 00:01
  • When I try `print(clickedCell)`, it logs the integer value successfully. I am adding more code to my question – senty Apr 15 '16 at 00:04
  • yep. but I meant if self.myDelegate { self.myDelegate.clickedCellIndexPath(clickedCell) } – Christian Abella Apr 15 '16 at 00:05
  • Please don't recommend [implicitly unwrapped optionals](https://metova.com/blog/dev/problem-implicitly-unwrapped-optionals/). – nhgrif Apr 15 '16 at 00:07
  • I put the code together in the question. Please tell me if you need to know more.. And thanks for the tip for checking, I am adding it now, too. I think it's about that the delegate doesn't seem to work. :/ – senty Apr 15 '16 at 00:10
  • I am not recommending. I am just trying to help the guy with his programming question. Whether you use implicitly unwrapped options or not that is debatable. And the fact that the compiler is allowing it is just a proof that there are just too many scenarios or design patterns that following strictly one rule just wont fix it. And thank you for the downvote. appreciate it. – Christian Abella Apr 15 '16 at 00:11
  • I wasn't the one downvoting it (and I am highly grateful and appreciate your help). I tried checking if myDelegate is nil and it is! What may be the problem? (upvoted) – senty Apr 15 '16 at 00:16