Karl Oscar Weber

Protocol Definitions in Swift

Today I ran into a difficult to identify bug while implementing a Swift Protocol.

Swift Protocols and the Protocol pattern has been around in Cocoa for a while and usually takes the form of Delegates. Swift Protocols are much easier for us to write, understand, and use. Protocol oriented development makes the whole shebang easier to understand.

Protocols are defined like this:

protocol MyProtocol {
  var myName: String { get set }
  func myFunction(doIt: Bool) -> Bool
}

Variables can also hold blocks as types, and you can require a block in a protocol:

protocol MyProtocol {
  var myBlock: (String) -> Bool { get set }
}

This is where I ran into a stumbling block. I had incorrectly added parameter names to the blocks parameters list:

protocol MyProtocol {
  var myBlock: (myName: String) -> Bool { get set }
}

This may seem innocuous, and the compiler didn't flag an error here either, but when trying to conform to the protocol You'll get a: Does not conform to protocol error:

class MyClass: MyProtocol {
    var myBlock = {(myName: String) in }
}

This confused me to no end! the method signatures are identical! Why is there a problem! The compiler's error details also didn't explain how to fix the problem. I finally figured out that in the protocol definition my block should have no parameters listed:

protocol MyProtocol {
  var myBlock: (String) -> Bool { get set }
}

Block method signatures should be written without parameter names. Perhaps writing block types with parameter names should throw an error? There's an idea for a Swift Evolution proposal.

Update:

Caleb Davenport has pointed out that The code does compile as long as the block signatures match. Argument names are part of block signatures just like functions.

Swift's Type inference is powerful, but I suspect that It can't infer named parameters within a block.

protocol MyProtocol {
  var myBlock: (myName: String) -> Bool { get set }
class MyClass: MyProtocol {
  var myBlock = {(myName: String) -> Bool in 
  return false}
}

Explicitly writing the block type as part of the protocol conformance is necessary:

protocol MyProtocol {
  var myBlock: (myName: String) -> Bool { get set }
class MyClass: MyProtocol {
  var myBlock: (myName: String) -> Bool = {(myName: String) -> Bool in 
  return false}
}

Thanks Caleb for that clarification.