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 ↩︎

When working with Interface Builder…

If something goes wrong, just delete and start over.

Yesterday I was trying to create a complex layout that had several UIStackViews nested… The most that Xcode could handle? 3. After that, it just choke and wouldn’t even load my Storyboard, even after ⌘+Q-ing it.

Then, connecting a view from a UITableViewCell to its class as an IBOutlet just made my app crash. I looked for wrong references, deleted them and added new ones… It didn’t work until I deleted the whole prototype cell and rebuilt the layout, re-connected everything.

Links from Tweetbot for Mac won’t load on Safari (El Capitan)

If you use Safari as your default browser and use Tweetbot for Mac, you may have had an issue on OS X El Capitan where links wouldn’t load the first time, they would just appear as “loading.”

You need to hit ⌘+R until, eventually, Safari says that it couldn’t load the link and hitting ⌘+R one more time finally loads what you were trying to see. A full minute later.

To fix this, open Terminal.app and paste the following:

defaults write com.tapbots.TweetbotMac OpenURLsDirectly YES

Press enter, and voilà.

No more excuses

Now that I live on my own, I’ve started to recognize the actual challenges of life: things that may sound superfluous but really make a dent on how you go through your day if you just don’t do them right; one of them is staying healthy.

I’ve read a lot of articles on the matter, and I’ve tried many many things. This is before I moved out of my parents house – back then, excuse after excuse kept me from going through with whatever I had planned.

However, now that I live on my own, there’s no room for excuses. This article by Benjamin Hardy really rang a bell. My key takeaway from it is: go one small step at a time.

After all, only good can come out of taking care of myself, right?

Right.

Swift blog: Why is my Hashable implementation crashing on an iPhone 4s?

Today I found what I thought of as a bug that had me scratching my head for about an hour.

My app ran just perfectly on my iPhone 6S Plus. However, when I sent a build to my designer for him to do proper testing, it always crashed on the same spot.

Ran it on the iPhone 4s simulator and the crash looked like this:

No output on the console nor the debugger. Tried to run instruments to catch the crash, with no luck.

Did a bit of research, and finally got to the official Swift documentation.

Using overflow operators (&+, &-, &*) fixes the issue:

public var hashValue: Int {
    return firstValue &+ secondValue
}

Numbers can overflow in both the positive and negative direction.

Which is exactly what may happen if you run your app on a 32 bit device, as the iPhone 4s and 5.

So, heads up! Make sure you do proper testing!

Reset Xcode’s “Load Bundles” warning

If you don’t use Alcatraz, the plugin manager for Xcode, you should.

When you install new plugins via Alcatraz (or any other way, really), you need to restart Xcode and then it prompts you with the following dialog:

If you’re not paying attention, you may select “Skip Bundles” which will make Xcode not load any of your newly installed plugins, which is annoying.

Fortunately, there’s a simple way you can solve this issue. Paste this in your terminal:

defaults delete com.apple.dt.Xcode DVTPlugInManagerNonApplePlugIns-Xcode-7.1.1

Press enter and restart Xcode. 🎉

Note the specific Xcode version at the end of that command — it’s important!

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.](mailto:oscar@swanros.com?subject=Swift blog 1)