Alex Marchant
Alex Marchant

Reputation: 660

What's the difference between an Extension func, Extension static func & Extension class func in Swift?

I was trying to create an extension function on the UIColor which could take a parameter of type Card.Colour and return a UIColor back to the caller.

button.backgroundColor = UIColor.getColour(cardColour: cardToDeal.colour)


extension UIColor {
    func getColour(cardColour: Card.Colour) -> UIColor {
        switch cardColour {
        case .Red:
            return UIColor.red
        case .Green:
            return UIColor.green
        case .Blue:
            return UIColor.blue
        }
    }
}

When I tried to do this, the extension function of UIColor.getColour required me to enter a parameter of type UIColor rather than the specified type of Card.Colour in the extension method.

However, when I changed the extension function of getColour to either:

static func getColour(cardColour: Card.Colour) -> UIColor {
class func getColour(cardColour: Card.Colour) -> UIColor {

It allowed me to pass a parameter of type Card.Colour

Why is this? Why does changing the function to either a static function or a class function change the type required to be passed in?

Thanks in advance!

(A detailed answer would be much appreciated)

Upvotes: 3

Views: 1656

Answers (2)

hashemi
hashemi

Reputation: 2678

Remember that UIColor is a class. A class is kind of like a blueprint that you use to create instances or objects that conform to that class. UIColor.red is an example of an instance of the UIColor class.

When you define a func inside a class (in your case, as an extension), Swift assumes that you want to add that func to the blueprint, which will in turn be available in all instances of the UIColor class, like UIColor.red.

You could also define your func outside of all classes, by just placing it on the top level of the module, instead of inside an extension.

However, to keep your functions organized, you can place functions like that inside the class name. You just have to tell Swift that you're not trying to add the function to the blueprint that will be applied to all instances, and that all you want instead is to have a function whose name is prefixed with the name of the class.

Here's an example to illustrate the difference in usage:

class Test {
    func notStatic() {
        print("called from an instance")
    }

    static func thisIsStatic() {
        print("called on class name directly")
    }
}

let instance = Test() // this is an *instance* of Test

instance.notStatic() // we can call a non static func on instance

Test.thisIsStatic() // we can call a static func directly on the class only

Now, let's go back to your specific example for a second. Notice that in your example, you're starting with a instance of Card.Colour and trying to create a new instance of UIColor.

In other words, adding a func to UIColor instances (i.e., a non-static or class) is useless to you, because you don't have an instance of UIColor yet.

The idiomatic way of creating a new instance of a class is using an initializer (init). So you could turn your function into an initializer on UIColor like this:

extension UIColor {
    convenience init(cardColour: Card.Colour) {
        switch cardColour {
        case .Red: self.init(cgColor: UIColor.red.cgColor)
        case .Blue: self.init(cgColor: UIColor.blue.cgColor)
        case .Green: self.init(cgColor: UIColor.green.cgColor)
        }
    }
}

Now you just call UIColor(cardColour: .Red) to get what you want. Note that in the implementation, I'm converting UIColor.red to cgColor and back as a quick hack. Feel free to use the initializer you see fit on UIColor for each case of Card.Colour.

But there's another way, which I think is even more elegant. Since you already have an instance of Card.Colour, you can extend Card.Colour with a function that gives you the UIColor corresponding to the instance. Within that function, you can refer to the Card.Colour instance using the keyword self.

Since you already have the Card.Colour instance through self, you don't need to pass any arguments to that function. This allows you to use a cool feature called computed properties to make the usage even nicer.

This is how you'd add such an extension to Card.Colour:

extension Card.Colour {
    var uiColor: UIColor {
        switch self {
        case .Red: return .red
        case .Blue: return .blue
        case .Green: return .green
        }
    }
}

And you can then get a UIColor from a Card.Colour like this Card.Colour.Red.uiColor or mainColour.uiColor, where mainColour is of type Card.Colour.

Finally, as Leo Dabus noted in a comment, Swift's naming conventions is that cases should start with a lowercase letter. You should be using Card.Colour.red instead of Card.Colour.Red, etc. Those conventions came out around Swift 3 time. It was common to capitalize case names before then.

Upvotes: 8

bartlomiej.n
bartlomiej.n

Reputation: 510

An extension method operates on an instance of provided type. You can use all of the internal properties and methods of an instance inside the method block.

static methods are methods that are namespaced by the class name and do not operate on any specific instance of your class. class methods are pretty much the same, just the difference between class and static is, that you can override class methods in a subclass.

Upvotes: 2

Related Questions