Availability for modifiers

Is there a new way to specify availability for modifiers?

I'm looking for something like this (pseudocode):

struct Bar: View {
    var body: some View {
        Foo()
            #if available(iOS 15)
            .refreshable
            #endif
    }
}

or, perhaps even more clean:

struct Bar: View {
    var body: some View {
        Foo()
            .refreshable_ios15
    }
}

whereas refreshable_ios15 is a modifier that is marked in a special way with some availability marker, and does something on iOS 15 and nothing on previous OS versions (but still compiles without a problem).

instead of the current dance:

struct Bar: View {
    var body: some View {
        if #available(iOS 15, *) {
            Foo()
                .refreshable {
                    // something
                }
        } else {
            Foo()
        }
    }
}
1 Like

@davedelong has an excellent article tackling the same issue

1 Like

And @shapsuk has been aggregating a number of SwiftUI-specific stuff into his repo here using this approach: GitHub - shaps80/SwiftUIBackports: A collection of SwiftUI backports for iOS, macOS, tvOS and watchOS

1 Like

Great stuff.

However I wonder if something like this (pseudocode below) worth considering into the Swift language itself to help not having such wrappers.

Foo()
    .first
    #available(iOS 15).second
    .third

I don't find myself just returning an unmodified self much. If I did, I might use one of those other solutions. As it is, I do this:

Foo().modifier {
  if #available(iOS 15, *) {
    $0.refreshable {

    }
  } else {
    $0 // .someKindOfFallback
  }
}
public extension View {
  /// Modify a view with a `ViewBuilder` closure.
  ///
  /// This represents a streamlining of the
  /// [`modifier`](https://developer.apple.com/documentation/swiftui/view/modifier(_:))
  /// \+ [`ViewModifier`](https://developer.apple.com/documentation/swiftui/viewmodifier)
  /// pattern.
  /// - Note: Useful only when you don't need to reuse the closure.
  /// If you do, turn the closure into an extension! ♻️
  func modifier<ModifiedContent: View>(
    @ViewBuilder body: (_ content: Self) -> ModifiedContent
  ) -> ModifiedContent {
    body(self)
  }
}
2 Likes

I find it a bit hard to wrap some newer modifiers, such as popoverTip(_:arrowEdge:action:) , because it takes a newer type as a parameter.

It is true that I can still use the conditional view approach to wrap the whole view with an availability check, but the custom modifier approach seems not useful for this case. :thinking: Do you have any advice on this? Thanks.

2 Likes