Preface

Recently, I started to officially use SwiftUI to do projects, but I discovered a big problem, that is, many APIs commonly used in UIKit require very high versions to be used in SwiftUI.

To give just a few examples, the property ScrollViewof scrolling to close the keyboard keyboardDismissModeis supported by iOS 7 in UIScrollView, but only supported by iOS 16 in SwiftUI. For another example, if UIAlertControlleradded to TextField, iOS 8 supports it in UIKit, but iOS 16 only supports it in SwiftUI.

Therefore, it is impossible to use pure SwiftUI for projects at this stage, unless your project is at least compatible with iOS 15, or even 16 or above. Having said that, when some lower versions of the API are not supported, some compatibility methods need to be used. Recently, I have seen some smart ways to share them with everyone.

Wrong compatibility method

We assume that the minimum version of your project supports iOS 14. When using some incompatible APIs on 14, an error will be reported:

You can see cyanthat this color can only be used in iOS 15. One way is to add judgment VStackunder this:#available

var body: some View {
    if #available(iOS 16.0, *) {
        VStack {
            Text("Hello, world!")
                .fontWeight(.bold)
                .background(Color.red)
                .foregroundColor(.blue)
                .padding()
                .frame(width: 100, height: 100)
            Text("Hello, world!")
                .fontWeight(.bold)
                .background(Color.red)
                .padding()
                .frame(width: 100, height: 100)
                .foregroundColor(.cyan)
        }
    } else {
        VStack {
            Text("Hello, world!")
                .fontWeight(.bold)
                .background(Color.red)
                .foregroundColor(.blue)
                .padding()
                .frame(width: 100, height: 100)
            Text("Hello, world!")
                .fontWeight(.bold)
                .background(Color.red)
                .padding()
                .frame(width: 100, height: 100)
                .foregroundColor(.blue)
        }
    }
}

There are several problems with this solution. First of all, in order to use it cyan, the entire <code> VStackmust be wrapped by an if statement. If VStackthere is a lot of code under this <code>, there will be a lot of repeated code. Secondly, this method is not conducive to maintenance. If there is another attribute that can only be used in iOS 16, should it be if iOS 16wrapped again? Obviously unreasonable.

Correct approach

The correct way is to achieve compatibility through extension. We can Colorextend a new newCyanattribute to be compatible with this situation:

extension Color {
    static let newCyan: Self = {
        if #available(iOS 15.0, *) {
            return .cyan
        } else {
            return .black
        }
    }()
}

In this case, just use it directly when you use it again newCyan:

Text("Hello, world!")
    .foregroundColor(.newCyan)

This solves the above problem.

Other examples

Using an extended compatibility scheme is a good idea, here are some other examples:

1. listRowSeparatorThe method of hiding the dividing line of List can only be used in iOS 15:

Also use extensions:

func newHiddenListRowSeparator() -> some View {
    if #available(iOS 15.0, *) {
        return listRowSeparator(.hidden)
    } else {
        return self
    }
}

hiddenListRowSeparatorJust change it to newHiddenListRowSeparator.

2. ScrollView prohibits scrolling

func newScrollDisabled() -> some View {
    if #available(iOS 16.0, *) {
        return self.scrollDisabled(true)
    } else {
        return self
    }
}

3. The tint method of View can only be used on iOS 15 or above.

Then write an extension for View

extension View {
    func newTint(_ color: Color?) -> some View {
        if #available(iOS 16.0, *) {
            return self.tint(color)
        } else {
            return self.accentColor(color)
        }
    }
}

Summarize

The benefits of using extension-compatible APIs are obvious:

  1. Fixed duplicate code issue
  2. The compatibility judgment of each API is independent and highly maintainable.
  3. When you upgrade to the lowest version, you only need to delete the version judgment or directly change the method name.

Leave a Reply

Your email address will not be published. Required fields are marked *