11

I'm going through and learning Swift by porting an existing application. I'm stuck on setting a delegate and cannot work out what the issue is.

I have a class which extends UITableViewCell

import UIKit

protocol SwitchCellDelegate{
    func switchChanged(switchCell: SwitchCell, state: Bool)
}

class SwitchCell: UITableViewCell {

    @IBOutlet var swtSelector: UISwitch
    @IBOutlet var lblTitle: UILabel

    var delegate: SwitchCellDelegate?

    init(style: UITableViewCellStyle, reuseIdentifier: String) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
    }

    @IBAction func switchChanged(){
        delegate?.switchChanged(self, state: swtSelector.on)
    }

}

Then in the ViewController is defined as

class SettingsViewController: UIViewController, UITableViewDelegate, UITableViewDataSource, SwitchCellDelegate {

and within the method

func tableView(tableView: UITableView!, cellForRowAtIndexPath indexPath: NSIndexPath!) -> UITableViewCell! {

we have

case 2:
    storeCredentialsCell = tableView.dequeueReusableCellWithIdentifier("StoreCredentialsCell") as? SwitchCell
    if(storeCredentialsCell != nil){
        ...
        NSLog("Setting delegate to %@ for %@", self.description, storeCredentialsCell.description)
        storeCredentialsCell!.delegate = self
        ...
    }

the log output is as expected but when it hits the actual setting of the delegate the app crashes with

EXC_BAD_ACCESS(code=1, address=0xfffffffffffffff8)

I should also note that if I don't set the delegate value when delegate?.switchChanged(self, state: swtSelector.on) fires it also causes an EXC_BAD_ACCESS error but according to the doco for delegates this should fail gracefully if delegate is not set to anything.

===========================

I've simplified down to a basic project to replicate the issue.

TestTableViewController.swift

import UIKit

class TestTableViewController: UITableViewController, TestCellDelegate {

    init(style: UITableViewStyle) {
        super.init(style: style)
    }

    init(coder aDecoder: NSCoder!) {
        super.init(coder: aDecoder)
    }

    override func viewDidLoad() {
        super.viewDidLoad()
    }

    override func didReceiveMemoryWarning() {
        super.didReceiveMemoryWarning()
    }

    override func numberOfSectionsInTableView(tableView: UITableView?) -> Int {
        return 1
    }

    override func tableView(tableView: UITableView?, numberOfRowsInSection section: Int) -> Int {
        return 1
    }

    override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell? {
        let cell = tableView!.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as? TestCell

        if(cell != nil){
            cell!.delegate = self
            cell!.lblTest.text = "Test Successful"
        }

        return cell
    }

    func eventFired(sender: TestCell) {
        NSLog("Hooray!")
    }

TestCell.swift

import UIKit

protocol TestCellDelegate{
    func eventFired(sender: TestCell)
}

class TestCell: UITableViewCell {

    @IBOutlet var lblTest: UILabel
    @IBOutlet var swtTest: UISwitch

    var delegate: TestCellDelegate?

    init(style: UITableViewCellStyle, reuseIdentifier: String) {
        super.init(style: style, reuseIdentifier: reuseIdentifier)
    }

    @IBAction func switchChanged(sender: UISwitch){
        delegate?.eventFired(self)
    }
}

I then created a single Table View Controller Scene with Class of TestTableViewController. The table view is dynamic with a single cell of type TestCell. That cell contains one label and one switch which are bound to the IBOutlets from the TestCell class. The switchChanged function is bound to the value changed event on the switch.

Same EXC_BAD_ACCESS error is thrown.

János
  • 32,867
  • 38
  • 193
  • 353
Moth
  • 765
  • 2
  • 7
  • 14
  • I'm not able to reproduce this. Can you post more code? Have you tried using the debugger to stop on the NSLog line and see what the variables are (such as `storeCredentialsCell`, `self`, etc.)? – jtbandes Jun 10 '14 at 04:25
  • Try initializing your delegate `var delegate: SwitchCellDelegate? = nil` – Dash Jun 10 '14 at 04:31
  • This is what the NSLog line produces:2014-06-10 14:31:59.310 SmartDelivery[316:60b] Setting delegate to <_TtC13CantaraClient22SettingsViewController: 0x13fd05ec0> for <_TtC13CantaraClient10SwitchCell: 0x13fd1f4f0; baseClass = UITableViewCell; frame = (0 0; 540 44); layer = > – Moth Jun 10 '14 at 04:32
  • I'm not sure what other code would be relevant. Maybe I'll try to create just a basic version and see if I can get it to work. One thing to note is that all of these classes are in their own Framework that is used by the main application. – Moth Jun 10 '14 at 06:23

2 Answers2

21

Currently you have to explicitly mark your protocols with @objc if the delegate should be an Object of a Objective-C class (Like the UITableViewController):

@objc protocol SwiftProtocol

This will enable interoperating with Objective-C

Jayman
  • 276
  • 1
  • 5
  • 2
    It might be that Swift is not a mess, but a beta. It seems that this is a bug that (we hope) will be fixed. Right now (Xcode 6 beta 5), strong delegates work well, but weak delegates still don't work. – George Aug 18 '14 at 20:39
6

And now for the solution....

@objc protocol SwitchCellDelegate

instead of

protocol SwitchCellDelegate
Moth
  • 765
  • 2
  • 7
  • 14