Check if iOS 14 is available in SwiftUI body

Hi,
I am struggling to add new views only available on iOS 14 while maintaining compatibility with iOS 13.

What I'd like is using a LazyVStack on iOS 14 and a List on iOS 13.

The following code compiles correctly but I am getting a ETCBADACCESS error on runtime on iOS 13.

If I switch to VStack instead of LazyVStack (so a view available in iOS 13), the crash disappears.

So it looks like SwiftUI tries to run the code in the #available block even though it's running iOS 13.

Do you have any suggestions ?

Thanks

Code Block swift
var body: some View {
    Group {
        if #available(iOS 14.0, *) {
            ScrollView {
                LazyVStack {
                    content
                        .padding(.horizontal, 15)
                }
            }
        } else {
            List {
                content
            }
        }
    }
}


Post not yet marked as solved Up vote post of NicoD Down vote post of NicoD
27k views

Replies

Can confirm I'm also having issues

I've attempted these as well

Code Block
struct StyledList: ViewModifier {
func body(content: Content) -> some View {
if #available(iOS 14.0, *) {
return content
.listStyle(InsetGroupedListStyle())
} else {
return content
.listStyle(DefaultListStyle())
}
}
}
}


and


Code Block
extension View {
public func styledList() -> some View {
if #available(iOS 14.0, *) {
return self.listStyle(InsetGroupedListStyle())
} else {
return self
}
}
}


But they all fail with
Function declares an opaque return type, but the return statements in its body do not have matching underlying types

Removing the #if available and one of the options does work.
Can you post a stack trace of the crash? (For instance, make this crash with the debugger attached, use the “bt” command in the lldb prompt, and copy-and-paste the output here.)
Here is the stack trace :


Thanks for your help
Okay, this looks like a bug in either SwiftUI or the Swift runtime. Please file a bug report using Feedback Assistant. Some tips for bug reports in general, with specifics for this issue:
  • Give it a title that summarizes the aspects that you think are most likely to be important to figure out the bug. For this bug, I'd say these are (1) it's a crash, (2) it's in SwiftUI code, (2) it happens on an iOS 13 device, and (3) you're using an iOS 14-only API, so something like "iOS 13 crashes when running iOS 14 SwiftUI code" might be a good title.

  • Always state explicitly the steps the engineer should follow to trigger the bug, what you expected them to do, and what actually happened instead. Those last two are often easy for crashing bugs—you expected it to not crash, and instead it crashed 😉—but it's good to be in the habit, because for some bugs it may not be clear what exactly is wrong about the result.

  • Mention the versions of the software involved, ideally including the build number (the parenthesized alphanumeric identifier after the version number). For example: "I built this with Xcode 12 beta 1 (12A6159). It ran correctly on iOS 14 beta 1 (18A5301v), but crashed on iOS 13.6 beta 2 (17G5045c)."

  • For bugs you see in your own apps, ideally you should attach an Xcode project which can be used to reproduce it. It's actually best if this isn't your entire real project, but instead is a small sample that fails in the same way. However, if you can't spare the time or can't share all of the code involved, give us what you can and we'll do our best.

  • Also think about what else you can attach that might help engineers quickly understand the bug.

 Some specific ideas for that last point:
  • Crashes: Try to include a crash report file. You can retrieve crash reports for crashes on your Mac from Console.app, and you can retrieve them for crashes on your iOS devices with the Xcode "Devices and Simulators" window.

  • Build failures: Export a build log from Xcode. You should also check if there are any related crash reports on your Mac and, if so, include them.

  • UI glitches: A screenshot or screen recording is often worth a thousand words.

  • Processes hanging on your Mac: A sample or spindump is often helpful. (A sample basically repeatedly records backtraces in one process; a spindump does the same thing, but for all processes.) You can use Activity Monitor to collect these.

  • Miscellaneous: A sysdiagnose is never a bad thing to include and is particularly important for "weirdness" like poor battery life or difficulty connecting to a network. Sysdiagnoses include a wide range of logs and diagnostic files. The Feedback Assistant website has instructions for collecting sysdiagnoses from various devices.

Hope this helps!
Yup, I'm getting the same issue using the following steps:
  1. Running macOS 10.15.5

  2. Generated a new test project in Xcode 11 ( to set deployment target to iOS 13.5 )

  3. Opened it in Xcode 12 beta

  4. Run and get a crash in simulator and on a real device until I remove the conditional or change LazyHStack/LazyVStack to HStack/VStack/Text..etc

Test project is just this code here:
Code Block import SwiftUI
struct ContentView: View {
  var body: some View {
    VStack {
      Text("Hello, World!")
       
      if #available(iOS 14.0, *) {
        LazyVStack {
          Text("ios14 1")
          Text("ios14 2")
        }
      } else {
        HStack {
          Text("ios13 1")
          Text("ios13 2")
        }
      }
    }
     
  }
}
struct ContentView_Previews: PreviewProvider {
  static var previews: some View {
    ContentView()
  }
}





It seems if #available inside a @ViewBuilder is buggy. As a temporary workaround, you can wrap your LazyVStack with AnyView. As bellow:

Code Block swift
struct ContentView: View {
var body: some View {
    Group {
            if #available(iOS 14.0, *) {
                ScrollView {
                    AnyView(LazyVStack { content.padding(.horizontal, 15) })
                }
            } else {
                List { content }
            }
        }
    }
}


This worked for me:

Code Block struct CustomView: View {
    var body: some View {
        someView()
    }
    func someView() -> AnyView {
        if #available(iOS 14.0, *) {
            return AnyView(Text("iOS 14"))
        } else {
            return AnyView(Text("iOS 13"))
        }
    }
}


This bug is still present in beta 4.
Following from @macasasa's answer, using a @ViewBuilder and wrapping only the iOS 14 code in AnyView works for me. E.g.

Code Block
@ViewBuilder
    private var textImageView: some View {
        if #available(iOS 14.0, *) {
            AnyView(Label {
                text
            } icon: {
                image
            })
        } else {
            HStack {
                image
                text
            }
        }
    }


Seems to be fixed as of Xcode 12 beta 5 (build 12A8189h).
@NicoD I seem to be running into this same crash, except I’m not even using any iOS 14 features in my Watch app! I’m building with Xcode 12 beta 6, but running (well, crashing) on iOS 13.6. Did you or the Developer Tools Engineer ever find a fix?

Also running into this issue:

extension View {
 func listRow() -> some View {
  let view = self
   .frame(maxWidth: .infinity, minHeight: 0, maxHeight: .infinity, alignment: .leading)
   
  if #available(iOS 15, *) {
   return AnyView(view.listRowSeparatorVisibility(.hidden).listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0)))
  }
   
  return AnyView(view.listRowInsets(EdgeInsets(top: -1, leading: 0, bottom: -1, trailing: 0)))
 }
}

This crashes, among other approaches as well

  • Fixed my issue by changing listRowSeparatorVisibility to listRowSeparator

Add a Comment

it seems working in Xcode 12.5 but we do have a WORSE case.

Suppose You need to write something like that:

struct InfoSheetView: View {

    if #available(iOS 15, *) {
    } else {
    }

var body: some View {
     .....
 }
}

even if an EMPTY if # I got:

Expected declaration

on if #..

I would like to write ..

    if #available(iOS 15, *) {
            @Environment(\.dismiss) var dismiss
    } else {
            @Environment(\.presentationMode) var presentationMode
    }

see original example at: https://www.hackingwithswift.com/quick-start/swiftui/how-to-present-a-new-view-using-sheets and try to conditionally use

@Environment(\.dismiss) var dismiss

OR

    @Environment(.presentationMode) var presentationMode

  • I'm struggling too with such case and with other cases as well, for example: supporting iOS 14 and 15 in a project with deployment target set to 14, and if I want to use an iOS 15 modifier I need to either:

    use #available checks everywhere in the code, ending up with hundreds of these which's so bad practice.or create a new view modifier for each iOS15 modifier I want to use, in which I check #available between 14 and 15 versions.

    I created a new question describing the problem on SO.

Add a Comment

I am experiencing this issue in Xcode 13.2.1 on an iOS 14.8.1 device. Take this simple custom ViewModifier which applies the appropriate non-deprecated capitalization modifier based on the OS.

struct CapitalizationModifier: ViewModifier {
  let shouldCapitalize: Bool
   
  func body(content: Content) -> some View {
    if #available(iOS 15.0, *) {
      content.textInputAutocapitalization(shouldCapitalize ? .words : .never)
    } else {
      content.autocapitalization(shouldCapitalize ? .words : .none)
    }
  }
}

I found that the crash only happens when built in the release configuration, but not debug. Changing the Optimization Level in the Swift Compiler - Code Generation section of the target's Build Settings prevents the crash.

  • Having similar issues with iOS 14 devices when using Xcode 13.2 and Xcode 13.2.1. Not sure what has changed suddenly to begin causing these issues. Will try changing the optimization level to see if this helps.

  • Having a similar issue on Xcode 13.2.1 and iOS 14 devices only. error protocol witness for ViewModifier.body(content:) in conformance *** + 43700770729 SwiftUI ModifierBodyAccessor.body(of:) + 36

  • This solution of changing the Optimization Level worked for me.

Add a Comment

Having a similar issue on Xcode 13.2.1 and iOS 14 devices only. Error:

protocol witness for ViewModifier.body(content:) in conformance *** + 43700770729 
SwiftUI ModifierBodyAccessor.body(of:)