Search code examples
swiftgenericstypesswiftuiprotocols

Reuse Extension for multiple types


I am attempting to reuse a piece of functionality in an extension across multiple types but I'm currently having a difficult time with the types. My code is as follows:

struct TitleStyle: ViewModifier {
    func body(content: Content) -> some View {
        content.font(.system(size: 34, weight: .bold))
    }
}

protocol TextStyle {
    associatedtype V: View

    func textStyle<Style: ViewModifier>(_ style: Style) -> V
}


extension TextStyle where Self: View {
    func textStyle<Style: ViewModifier>(_ style: Style) -> some View {
        ModifiedContent(content: self, modifier: style)
    }
}

extension Text: TextStyle {}
extension TextEditor: TextStyle {}

This would allow me to create a reusable component as follows:

func ExpandingTextEditor<Style: ViewModifier>(text: Binding<String>, style: Style) -> some View {
    ZStack {
        TextEditor(text: text)
            .textStyle(style)

        Text(text.wrappedValue)
            .textStyle(style)
            .opacity(0)
            .padding(.all, 8)
    }
}

Looking at similar questions to mine on Stack Overflow I've managed to get to this stage but I am getting the errors:

  • "Text does not conform to protocol TextStyle"
  • "TextEditor does not conform to protocol TextStyle"

What am I missing?


Solution

  • You just need extension to View, like

    extension View {
        func textStyle<Style: ViewModifier>(_ style: Style) -> some View {
            ModifiedContent(content: self, modifier: style)
        }
    }
    
    func ExpandingTextEditor<Style: ViewModifier>(text: Binding<String>, style: Style) -> some View {
        ZStack {
            TextEditor(text: text)
                .textStyle(style)
    
            Text(text.wrappedValue)
                .textStyle(style)
                .opacity(0)
                .padding(.all, 8)
        }
    }
    

    or if you want to limit to only specific views:

    protocol TextStyle {}
    
    extension Text: TextStyle {}
    extension TextEditor: TextStyle {}
    
    extension View where Self: TextStyle {
        func textStyle<Style: ViewModifier>(_ style: Style) -> some View {
            ModifiedContent(content: self, modifier: style)
        }
    }
    

    Tested with Xcode 13.3 / iOS 15.4