Ambiguous convenience initializers

I was just setting up a class with a designated initializer and a convenience initializer to be able to pass in initializing properties beyond the defaults:

class Font {
    var name = "Arial"
    var size = 12
    init() {

    }
    convenience init(name:String,size:Int) {
        self.init()
        self.name = name
        self.size = size
    }
}

Great, all good so far. But sometimes a Font will be instantiated with a different name, but we want to leave the size as the default(or vice versa). Let’s add a default value to the name and size parameters in the convenience initializer:

convenience init(name:String="Arial",size:Int=12) {

Oh. You’ll see the obscure error on `self.init()`:
> Could not find an overload for ‘init’ that accepts the supplied arguments

What’s going on here? The error message isn’t very clear. If you comment out the offending line in the convenience initializer, and try to instantiate the class, you get a better idea of the problem:

Screenshot 2015-08-03 10.38.06

Two initializers without parameters appear for the Font class. How does the compiler know which initializer you intend it to run with the following line?

var font = Font()

If you instantiate Font without parameters, you’ll see the error:
> Cannot invoke initializer for type ‘Font’ with no arguments”.

Still not a very clear error message, but you’ll probably be starting to get the idea why. You’ll see a much clearer error message if you set up two functions with the same name, one with a parameter with a default value:

class Planet {
    var name = "World"
    func greet()->String {
        return "Hello \(name)"
    }
    
    func greet(name:String = "World")->String {
        self.name = name
        return "Hello \(name)"
    }
}
var planet = Planet()
planet.greet()

This time, you’ll see the error message:
> Ambiguous use of ‘greet’

Short and sweet, but I think it gets the point across much more clearly – ambiguity is the issue!

So stepping back a second – what’s a convenience initializer for? Supporting the designating initializer. But is a convenience initializer necessary if all of the parameters have default values? No!

class Font {
    var name = "Arial"
    var size = 12

    init(name:String="Arial",size:Int=12) {
        self.name = name
        self.size = size
    }
}
var arial = Font()
arial.name
arial.size

var comicSans = Font(name: "ComicSans", size: 10)
comicSans.name
comicSans.size

var times = Font(name: "Times")
times.name
times.size

var bigArial = Font(size: 48)
bigArial.name
bigArial.size

Interactive developer, Author - iOS development with Swift - book coming 2017 https://manning.com/books/ios-development-with-swift

Tagged with: ,
Posted in Swift

Leave a Reply

Please log in using one of these methods to post your comment:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: