Overload Swift type ambiguity

I may define default type for overloaded functions. sometimes.

This is Swift return overloading that may lead to ambiguity:

extension String {
    func encode() -> NSData? {
        return self.dataUsingEncoding(NSUTF8StringEncoding)?.base64EncodedDataWithOptions([])
    }

    func encode() -> String? {
        return self.dataUsingEncoding(NSUTF8StringEncoding)?.base64EncodedStringWithOptions([])
    }
}

I have two functions encode() that returns value of different type, depends on context. String or NSData. When I want use it, I get ambiguity error. As expected.

let base64 = "Hello World".encode() // ambiguous use

with list of found candidates

note: found this candidate
    func encode() -> String?

note: found this candidate
    func encode() -> NSData?

I can live with this, or use trick to make it slightly more convenience in use, by defining protocol (OOP FTW!)

protocol Base64StringEncodable {}
extension String: Base64StringEncodable {}

and move one of the functions to our protocol extension:

extension Base64StringEncodable {
    func encode() -> NSData? {
        guard let string = self as? String else { return nil }
        return string.dataUsingEncoding(NSUTF8StringEncoding)?.base64EncodedDataWithOptions([])
    }
}

and now, default returning type is String, while NSData is still available:

let base64          = "Hello World".encode() // String by default
let base64: NSData? = "Hello World".encode() // NSData? if asked

Function that return NSData is still available - it's just not default.

This trick don't apply to more than two types ;-( It's just a trick after all.

Wait... but why?

Keep in mind that Swift allows function overloading even when two signatures differ only in their return type. via swift blog

Presented trick relies on:

  1. ability to overload functions in Swift
  2. ability to implement function for Protocol
  3. calling sequence

Calling sequence

This is interesting.

  1. Fist is called function defined by the type body or extension to the type.
  2. Second is called function implemented by protocol.
  3. When one protocol inherit from another one, the one that is higher in hierarchy is more important.
    For given protocol A: B {}, A implementation is called.
  4. The order you define conformance to protocol doesn't matter.
    extension String: A,B {} and extension String: B,A {} is not changing the final sequence.

as simple as that.

Quiz

Sounds like possible interview question?

Question: Implementing function name() in two different protocols A,B and defining String protocol conformance to both protocols, like this:

protocol A {}
extension A {
    func name() -> String { return "nameA" }
}

protocol B {}
extension B {
    func name() -> String { return "nameB" }
}

extension String: A,B {}

called as

"abc".name()

Which one function is called first?

Answer: check the answer

Thank you

That's all folks.
Folow me on twitter/@krzyzanowskim or github/krzyzanowskim

photo by usgsbiml