November 20, 2015

Swift blog: Protocols are not always the way to go

I keep forcing myself to use protocols more and more when coding Swift.

Today's issue came from the following scenario:

I have a (static) table view with 4 prototype cells defined on Interface Builder. Each cell has its own identifier and UITableViewCell subclass assigned to it.

The requirement: each of cells must have a specific property from where it will automatically populate its fields:

var foo: Foo? {  
    didSet {
        barTitleLabel.text = bar!.title
    }
}

Since I was quickly prototyping how everything would work, I had the following mess on my UITableViewDataSource implementation:

func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {  
    return 4
}

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {  
    switch indexPath.row {
    case 0:
        let cell = tableViewdequeueReusableCellWithIdentifier("FirstCell", forIndexPath: indexPath) as! FristCell
        cell.foo = currentFoo
        return cell

    case 1:
        let cell = tableViewdequeueReusableCellWithIdentifier("SecondCell", forIndexPath: indexPath) as! SecondCell
        cell.foo = currentFoo
        return cell

    case 2:
        let cell = tableViewdequeueReusableCellWithIdentifier("ThirdCell", forIndexPath: indexPath) as! ThirdCell
        cell.foo = currentFoo
        return cell     

    case 3:
        let cell = tableViewdequeueReusableCellWithIdentifier("FourthCell", forIndexPath: indexPath) as! FourthCell
        cell.foo = currentFoo
        return cell

    default: return UITableViewCell()
    }
}

So, there's an obvious issue. Lots and lots of code duplication. If you pay close attention, the only thing that's really changing is the identifier and the type, on each case everything is the same. Also, having that default clause there really bothers me.

Lets put protocols to good use.

Fixing the issue

I thought that I could map each String identifier with a unique value that identified that cell. Since each specific cell will be on the same spot every time, that was easy. I just created an enum:

enum Cell: Int {  
    case FirstCell = 0
    case SecondCell
    case ThirdCell
    case FourthCell
}

extension Cell: CustomStringConvertible {  
    var description: String {
        swith self {
            case .FirstCell: return "FirtCell"
            case .SecondCell: return "SecondCell"
            case .ThirdCell: return "ThirdCell"
            case .FourthCell: return "FourthCell"
        }
    }
}

It simplified the cell creation... kind of:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {  
    let cellType = Cell(rawValue: indexPath.row)! // the actual implementation does not use an explicitly unwrapped optional.

    let cell = tableView.dequeueReusableCellWithIdentifier(cellType.description, forIndexPath: indexPath)

    switch indexPath.row {
        case 0:
            var aCell = (cell as! FirstCell)
            aCell.foo = currentFoo
            return aCell

        case 1:
            var aCell = (cell as! SecondCell)
            aCell.foo = currentFoo
            return aCell

        // And so on...

    }
}

Not quite what I was hoping for, actually. Then I thought that it would be cool if, just as Cell can return the identifier for the appropriate row, it returned the type of the cell for the appropriate row. So I dug into it.

First approach

My first approach was just to have a variable that gave me the Type I was expecting:

extension Cell {  
    var type: AnyClass {
        case .FirstCell: return FirstCell.self
        case .SecondCell: return SecondCell.self
        case .ThirdCell: return ThirdCell.self
        case .FourthCell: return FourthCell.self
    }
}

Everything seemed ok — it compiled correctly, but when I tried to implement it:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {  
    let cellType = Cell(rawValue: indexPath.row)! // the actual implementation does not use an explicitly unwrapped optional.

    let cell = tableView.dequeueReusableCellWithIdentifier(cellType.description, forIndexPath: indexPath) as! cellType.type // ERROR!!!

    // ...
}

Which made Xcode spit thie error cellType is not a type. Turns out the type casting operator can't process statements on its right side. So that's an issue, that automatically discarded my option of making Cell generic on subclasses of UITableViewCell 1.

Second approach

After a lot of thinking about the most elegant way to solve this, I came up with the following: create a protocol that all my cells conformed to, so that way I don't have to worry about the cell's actual type:

protocol Fooable {  
    var foo: Foo? { get set }
}

"Let's use the magic of inheritance, and have every UITableViewCell subclass be Fooable," I thought to myself:

extension UITableViewCell: Fooable {  
    var foo: Foo?  // ERROR: “Extensions may not contain stored properties.”
}

Ok, so... "Extensions may not contain stored properties," huh? Let's change the protocol, then:

protocol Fooable {  
    func setFoo(foo: Foo)
}

extension UITableViewCell: Fooable {  
    func setFoo(foo: Foo) {}
}

It sounded great on my head, but you can see the issue here... It would require every UITableViewCell subclass to override the setFoo: method, and also, it would require them to define a custom var foo: Foo?, which, by the way, is not cool because that requirement is not expreesed anywhere. Ah, and also, this polutes UITableViewCell on the whole app context.

"Aha!" moment! Create a UITableViewCell subclass. That way, I could define te foo var on the protocol itself, and avoid poluting all UITableViewCell:

protocol Fooable {  
    var foo: Foo? { get set }
}

class BaseCell: UITableViewCell, Fooable {    var foo: Foo?  
}

class FirstCell: BaseCell {  
    override var foo: Foo? {
        didSet {
            // ...
        }
    }
}

// And so on...

So, my UITableViewDataSource now looks something like this:

func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {  
    let cellType = CellType(rawValue: indexPath.row)!

    let cell = tableView.dequeueReusableCellWithIdentifier(cellType.description, forIndexPath: indexPath) as! BaseCell
    cell.foo = currentFoo

    return cell
}

Ahhh... much cleaner!

And then it hit me! I don't need a protocol for this! So, 10 seconds later, Fooable disappeared and everything still worked correctly.

What I learned

  1. Protocol-Oriented-Programming is so fun and powerful that may make you forget certain aspects of Object-Oriented-Programming. Although it is useful, it is not always the way to go, as I found out today.

  2. Remember that Cocoa's architecture is built with OOP, so POP may bite you in the ass if you try to force it.

  3. I'd still use my original approach with the enum if it was possible. This is related to what Samuel Giddins wishes comes with Swift 3.0. I certainly wish it too.

  4. This was a fun experiment!

  5. I found this answer on StackOverflow. However, I didn't like that this approach depends creating a class from a string. I want strings the least possible, I want the actual type.

  1. This has been reported to Apple as a suggestion: rdar://23622714


November 11, 2015

Swift blog: Wrangling protocols

The app I'm working on has a set of interesting requirements on the data layer.

Let's try to break it down: I have a set of data that can be represented on 2 different ways, although is the same type of data.

My Protocol-Oriented-Programming training kicked in:

protocol CriteriaItem {  
    var title: String { get set }
}

struct SingleCriteriaItem: CriteriaItem {  
    var title: String
}

struct MultipleCriteriaItem: CriteriaItem {  
    var title: String 
}

I want to handle every item as a CriteriaItem, regardless it being Multiple or Single.

The server spits a set of strings, which then need to be converted to CriteriaItems:

let criteriaItemTitles = ["One", "Two", "Three", "Two"]

let criteriaItems: [CriteriaItem] = criteriaItemTitles.map { SingleCriteriaItem(title: $0) }  

Now, let's define a function to filter an array to get only unique values from it:

func uniq<S:SequenceType, E: Hashable where E == S.Generator.Element>(source: S) -> [E] {  
    var seen: [E:Bool] = [:]
    return source.filter { v -> Bool in
        return seen.updateValue(true, forKey: v) == nil
    }
}

// uniq([1, 3, 2, 3, 2]) # => [1, 3, 2]

Great, so now we're ready to get only an array of unique CriteriaItems:

let uniqueItems = uniq(criteriaItems)  
# ERROR: Cannot invoke `uniq` with an argument list of type `([CriteriaItem])`

If we check the type constraints on the generic uniq function, we know that:

  1. uniq is generic on S and E
  2. S, which is the parameter passed to the function, needs to be a SequenceType-conforming type.
  3. The Element on the sequence's Generator should be the same type as E, the function's output type.
  4. E, which is the output type of the function, should be Hashable.

Lets verify what we have:

  1. Arrays on Swift conform to CollectionType which inherits from SequenceType, so our criteriaItems constant is good ([CriteriaItem]).
  2. We expect an array of CriteriaItems as the function's output, so we comply with the 3rd point.
  3. CriteriaItem is a protocol, and today you can't make a protocol type conform to another protocol.

The solution: wrap the protocol on a struct that conforms to Hashable:

struct BoxedItem: Hashable {  
    let item: CriteriaItem

    var hashValue: Int {
        return item.title.hashValue
    }
}

func ==(lhs: BoxedItem, rhs: BoxedItem) -> Bool {  
    return lhs.hashValue == rhs.hashValue
}

So, now, to be able to extract only the unique CriteriaItems, we need to wrap every one of them in a box:

let boxedItems = criteriaItems.map { BoxedItem(item: $0) }

let uniqueItems = uniq(boxedItems).map { $0.item } // 3 => [{title "One"}, {title "Two"}, {title "Three"}]  

Extras

There seems to be a bug with Swift, were the compiler won't be able to infer the type of a collection of protocol implementations:

protocol A {  
    var a: String { get set }
}

struct X: A {  
    var a: String
}

func takesAs(aas: [A]) {  
    // Something
}

let letters = ["A", "B", "C", "D"]

let xs = letters.map { X(a: $0) } // xs is of type [X]  
takesAs(xs) // Cannot convert value of type `[X]` to expected argument type `[A]`

let xs2: [A] = letters.map { X(a: $0) } // xs2 is casted to type [A]  
takesAs(xs2) // Works as expected  

However, passing a protocol-comforming type instance alone works as expected:

func takesA(a: A) {  
    // Something
}

let x = X(a: "A")

takesA(x) // Works as expected  

This was reported to Apple as rdar://23499056.

Did I miss something or want to share some insight on this post? Drop me a line.