Articles, podcasts and news about Swift development, by John Sundell.

SwiftUI extensions using generic type constraints

Published on 17 Jul 2020
Discover page available: SwiftUI

Most SwiftUI views that act as containers for other views are implemented as generics over the type of content that they contain. For example, SwiftUI’s built-in Button has a generic Label type that determines what type of view that each instance will be rendered using:

struct Button<Label>: View where Label: View {
    ...
}

Since SwiftUI views are, at the end of the day, just regular structs, we can use the same type of generic programming techniques when working with them as we can within other contexts. For example, we can use generic constraints to implement specialized convenience APIs — like this one that makes it easy to create a Button by using a given system image as its icon:

extension Button where Label == Image {
    init(iconName: String, action: @escaping () -> Void) {
        self.init(action: action) {
            Image(systemName: iconName)
        }
    }
}

The cool thing about generic type constraints is that the compiler will automatically infer what specialization to use based on each call site — meaning that we can call our new Button convenience initializer without having to manually type out Button<Image> when doing so:

struct PlayerView: View {
    ...

    var body: some View {
        ...
        Button(iconName: "play.fill") {
            startPlayback()
        }
    }
}

The fact that SwiftUI makes such heavy use of Swift’s type system and its generics capabilities means that there’s often a lot of opportunities to create very lightweight convenience APIs without having to define a new View type every time.