Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Make swift class conform to protocol - at static/class level

I'm trying to build a generic UITableViewController subclass in Swift that will accommodate any number of different kinds of table view cells without having internal knowledge of any of them.

To do this I'm trying to use protocols for my models and for my table view cells. The protocol for the models will return which cell class I should be going to, and the protocol for the cells will return answers to questions like what the height of the cell should be for a given model.

But I'm having a problem making the protocols work, because with the second protocol I want to go to the cell's class rather than its instance.

The protocol for the models is as follows:

protocol JBSTableItemDelegate
{
    func tableCellDelegate() -> JBSTableViewCellInterface
}

The protocol for the cells is as follows:

protocol JBSTableViewCellInterface: class
{
    static func registerNibsWithTableView(tableView: UITableView)

    static func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath?, tableItem: JBSTableItemDelegate) -> CGFloat

    static func tableView(tableView: UITableView, dequeueReusableCellWithIndexPath indexPath: NSIndexPath, tableItem: JBSTableItemDelegate, delegate: AnyObject) -> JBSTableViewCell
}

Notice the use of the keyword "static". These methods are class methods in the UITableViewCell subclasses, and adding static seems to be what I need to do to verify that these classes conform, or so I understand.

When I use the first protocol the code looks like so, and it compiles:

let tableViewCellInterface = tableItem!.tableViewCellInterface()

It's calling this method (as one example):

func tableViewCellInterface() -> JBSTableViewCellInterface
{
    return JBSLiteratureTableViewCell.self as! JBSTableViewCellInterface
}

This returns the class of the cell, such as, "JBSLiteratureTableViewCell.self"

When I use the second protocol the code looks like so, and it does not compile:

returnFloat = tableViewCellInterface.tableView(tableView, heightForRowAtIndexPath: indexPath, tableItem: tableItem!)

It fails to compile because of the static keyword up earlier, and the compiler error I get is:

'JBSTableViewCellInterface' does not have a member named 'tableView'

If I take the static keywords off of the protocol funcs, it compiles, but then the UITableViewCell subclasses complain saying:

'JBSLiteratureTableViewCell' does not conform to protocol 'JBSTableViewCellInterface'

This is because they're now trying to make sure instance methods exist, which is not where these are.

How do I make a swift class conform to a protocol at the class level, so it can be my delegate rather than some instance of a class? I'm sure I could get around this by creating helper classes of protocol JBSTableViewCellInterface that are singletons and let them do the work, but I'd rather build it right into the UITableViewCell subclasses in their class methods.

like image 984
John Bushnell Avatar asked Apr 18 '15 01:04

John Bushnell


2 Answers

UPDATED for Swift Version 2.0 and above

As per Gregzo's answer, Swift 2.0+ allows methods to be declared as static in the protocol definition. These must be satisfied with static/class methods in objects that implement the protocol.

You cannot satisfy a protocol definition for a instance method with a static method or vice-versa, which makes this an incomplete answer for the question above.

If you want to try this just use the keyword "static" in your protocol definition for methods you will implement as static or class methods in your conforming objects:

protocol InstanceVsStatic {
    func someInstanceFunc()
    static func someStaticFunc()
}

enum MyConformingEnum: InstanceVsStatic {
    case someCase

    static func someStaticFunc() {
           // code
    }
    func someInstanceFunc() {
        // code
    }
}

class MyConformingClass: InstanceVsStatic {
    class func someStaticFunc() {
           // code
    }
    func someInstanceFunc() {
        // code
    }
}

struct MyConformingStruct: InstanceVsStatic {
    static func someStaticFunc() {
           // code
    }
    func someInstanceFunc() {
        // code
    }
}

You can have an instance method call a static/class method:

This allows you to execute static code when you need to conform to a protocol that requires an instance method.

struct MyConformingStruct: InstanceVsStatic {
    static func doStuffStatically(){
        // code
    }

    static func someStaticFunc() {
           // code
    }

    func someInstanceFunc() {
        MyConformingStruct.doStuffStatically()
    }
}

Swift 1.2

Other than indirectly as above, there is no way to use static (class) methods to conform to a protocol in pure swift version 1.2 and below. It is a known bug / unimplemented feature: https://openradar.appspot.com/20119848

like image 103
WaterNotWords Avatar answered Nov 15 '22 00:11

WaterNotWords


If a protocol func is static, the implementer should implement it as a static method, and if it isn't, as an instance method:

protocol MixedProtocol
{
    static func staticFoo()
    func instanceBar()
}

class ExampleClass : MixedProtocol
{
    // implementing static func as class func is fine.
    // class funcs are overridable, not static ones
    class func staticFoo()
    {
        println( "I'm a class func" )
    }

    func instanceBar()
    {
        println( "I'm an instance func" )
    }
}

There's no straight way around it: conforming to a protocol means just that, and 'static' is very much a key feature of a protocol member declaration that implementers must respect.

like image 20
Gregzo Avatar answered Nov 15 '22 00:11

Gregzo