30

Can't connect delegate property of CustomView declared as @IBOutlet toViewController in Interface Builder – simply can't establish a connection.

Here's the code

class CustomView: UIView {
     @IBOutlet var delegate: CustomViewDelegate?
}

@objc protocol CustomViewDelegate {
     ...
}


class ViewController: UIViewController, CustomViewDelegate {
     ...
}

@objc is used because of swift protocol, IBOutlet property cannot have non-object type, don't know why protocol CustomViewDelegate: class {} doesn't work.

Anyone else came across something like that?

Community
  • 1
  • 1
Dmitry
  • 7,300
  • 6
  • 32
  • 55
  • Does your `UIViewController` in Interface Builder have its class specifically set to `ViewController`? Also, it's normally a requirement (last I checked) that `@IBOutlet` properties be defined as implicitly unwrapped types, like so: `CustomViewDelegate!`. This allows them to be `nil` at instantiation, while allowing you to use them without optional binding after they've been wired up. – Craig Otis Oct 03 '14 at 13:58

6 Answers6

58

From the Xcode release notes:

Interface Builder does not support connecting to an outlet in a Swift file when the outlet’s type is a protocol.

Workaround: Declare the outlet's type as AnyObject or NSObject, connect objects to the outlet using Interface Builder, then change the outlet's type back to the protocol.

EDIT: Xcode 9 beta 3 release notes say that this workaround should no longer be necessary.

matt
  • 515,959
  • 87
  • 875
  • 1,141
  • 3
    Wowzer! This worked, thanks. Apple... please get it together! – josephap Mar 09 '15 at 01:22
  • Nope. "Class is not key-value compliant" after the change back to Protocol type. – stone Mar 15 '15 at 12:48
  • This only partially worked for me. In one case, where I defined my own delegate on a UIView to be hooked up to a UIViewController, it worked. In another, where I define my delegate on a UIViewController to be hooked up to another UIViewController, it does not. I have tried so many configurations. Any ideas @matt what may be going on here? – josephap Mar 15 '15 at 21:50
  • "In another, where I define my delegate on a UIViewController to be hooked up to another UIViewController, it does not" That makes no sense to me, @josephap. You can _never_ draw an outlet connection of _any_ kind from one view controller to another view controller in a storyboard, so I can't imagine what you were trying to do in the first place. This has nothing do with Swift at all. It's just a fact about how nibs work. - See my book: http://apeth.com/iOSBook/ch07.html#SECconnectionBetweenNibs – matt Mar 15 '15 at 23:49
  • Yes, Matt, I realize now. I haven't used Storyboards all that much, and just found out that you can only connect outlets within view controllers, not between them. I did, separately, have a Swift related issue which your answer helped with. Thanks. – josephap Mar 17 '15 at 15:37
  • Now I'm torn as to whether I should switch back to Objective-C and/or ditch storyboards in favour of pure code. The main reason I'm using storyboards at the moment is because I'm writing a Cocoa app and I only have experience with iOS. Also it's nice to develop subviews with `@IBDesignable`. – Samah Jan 13 '16 at 21:55
  • It's February 2016 and still not fixed. Luckily there is this workaround. – Lars Blumberg Feb 02 '16 at 13:54
  • Should be fixed in Xcode 9 beta 3, according to the release notes. – matt Jul 10 '17 at 18:48
16

Adam Waite provides a nice workaround. I however prefer the following solution as it emphasizes the workaround and the extra property can also easily be removed once Xcode gets fixed.

class CustomView: UIView {
    @IBOutlet
    public var delegate: CustomViewDelegate?

    /// Workaround for Xcode bug that prevents you from connecting the delegate in the storyboard.
    /// Remove this extra property once Xcode gets fixed.
    @IBOutlet
    public var ibDelegate: AnyObject? {
        get { return delegate }
        set { delegate = newValue as? CustomViewDelegate }
    }

    func someMethod() {
        // Here we always refer to `delegate`, not `ibDelegate`
        delegate?.onSomethingHappened()
    }
}

@objc protocol CustomViewDelegate {
    ...
}

Hey, is this bug already one and a half years old?

Lars Blumberg
  • 19,326
  • 11
  • 90
  • 127
10

An elegant workaround:

#if TARGET_INTERFACE_BUILDER
@IBOutlet open weak var delegate: AnyObject?
#else
open weak var delegate: CustomViewDelegate?
#endif

See: https://github.com/WenchaoD/FSPagerView/blob/master/Sources/FSPagerView.swift#L88

WenchaoD
  • 136
  • 3
  • 5
9

Another that's not pretty but:

@IBOutlet weak var ibDelegate: NSObject?
@IBOutlet weak var ibDataSource: NSObject?
var delegate: MultipleButtonViewDelegate? { return ibDelegate as? MultipleButtonViewDelegate }
var dataSource: MultipleButtonViewDataSource? { return ibDataSource as? MultipleButtonViewDataSource }
Adam Waite
  • 19,175
  • 22
  • 126
  • 148
3

This is an old thread, but I thought I'd point out that as of Xcode 9 beta 3, it is now possible to connect a custom delegate written in swift to interface builder.

According to the release notes

Interface Builder now recognizes outlets, actions, and inspectable properties declared on classes which have a Swift protocol extension. (22201035)

// Can connect this to interface builder now    
class MyViewController: UIViewController {
    @IBOutlet weak var myDelegate: TheNewDelegate?
}
DerrickHo328
  • 4,664
  • 7
  • 29
  • 50
1

For me, the reason was the table view was nil at the point I attempted to set it's datasource and delegate. This was due to the designated initializer calling initWithNibName:bundle: which does not guarantee initialized connections. Deferring my delegate and datasource setting to viewDidload worked like a charm.

Community
  • 1
  • 1
Tom Howard
  • 4,672
  • 2
  • 43
  • 48