'How to use view modifiers only available for iOS 14+ with deployment target of iOS 13

How can you add a view modifier to a view when it's only available on iOS 14 but your app is available for iOS 13?

For an example, textCase(_). Section headers in iOS 14 are uppercase so to use the case of your header text you should set .textCase(.none) on your Text, but this didn't exist until iOS 14.

Section(header:
    Text("Header")
        .textCase(.none) //FIXME: 'textCase' is only available in iOS 14.0 or newer
        .font(.system(size: 14, weight: .bold))
        .foregroundColor(.secondary)
        .padding(.top, 50)
)

Xcode offers some suggestions:

  • Add if #available version check
  • Add @available attribute

If you use the #available version check, it wraps all of that scoped code with #available, so you'd have to duplicate all of that to add that one line of code. If you use @available you have to duplicate the entire body property or entire struct.

I considered creating my own ViewModifier that would only apply it if iOS 14 but that gives this dreaded error:

Function declares an opaque return type, but the return statements in its body do not have matching underlying types

struct CompatibleTextCaseModifier: ViewModifier {
    func body(content: Content) -> some View {
        if #available(iOS 14.0, *) {
            return content
                .textCase(.none)
        } else {
            return content
        }
    }
}


Solution 1:[1]

Mark body as @ViewBuilder - this will allow track internal different return types automatically, and remove return because explicit return disables view builder wrapper.

So here is fixed variant

struct CompatibleTextCaseModifier: ViewModifier {

    @ViewBuilder
    func body(content: Content) -> some View {
        if #available(iOS 14.0, *) {
            content
                .textCase(.none)
        } else {
            content
        }
    }
}

and usage

Section(header:
    Text("Header")
        .modifier(CompatibleTextCaseModifier())
        .font(.system(size: 14, weight: .bold))
        .foregroundColor(.secondary)
        .padding(.top, 50)
) {
    Text("test")
}

backup

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1