swiftswiftuicompiler-directivesviewmodifier

SwiftUI compiler directives trigger "Cannot infer contextual base" error for following statements


I have a SwiftUI View for a watchOS / iOS app in which I use the #if os(xxxOS) compiler directive to select between a watchOS specific View or an iOS specific View to provide the content for the the OSCommonView body var. Both the watchOS and iOS body views call the same bunch of view modifiers, which themselves reference functions declared in the parent OSCommonView. So instead of replicating those modifiers in each of the iso/watchOS subviews I thought I could use the #if...#else...#endif compiler directives to switch-in the OS-specific subview view, followed by the modifiers. However, I get the error "Cannot infer contextual base for ..." for the first and last modifier, whatever modifier is the first or last. It seems that #endif makes a break such that the compiler does not "see" the optional view above the first modifier??

struct OSCommonView: View {
    var body: some View {
    #if os(watchOS)
        body_watchOS
    #else
        body_iOS
    #endif
        .onAppear() { handleOnAppear() } <= Cannot infer contextual base error...
        .onDisappear() { handleOnDisappear() }
        .onReceive(uiUpdateTimer) { handleReceive_uiUpdateTimer(newDate: $0) }
        /* --- several other modifiers --- */

    }
    var body_iOS: some View {
        /* view subviews etc */
    }
    var body_watchOS: some View {
        /* view subviews etc */
    }

    func handleOnAppear() { /* ... */}
    func handleOnDisappear() { /* ... */}
    func handleReceive_uiUpdateTimer(newDate: Date) { /* ... */}
}

I naively assumed the compiler directives were "invisible" and just presented the code seamlessly according to the switch. I have tried to create a ViewModifier to group them but failed to find a way to get the parameters (some with Bindings) and methods of the OSCommonView into a ViewModifier struct. At the moment I just duplicate the long list of view modifiers within each definition of body_watchOS and body_iOS.

If I put the modifiers inside the compiler switch above they work fine but that defeats the point of not duplicating them!

#if os(watchOS)
    body_watchOS
        .onAppear() { handleOnAppear() }
        .onDisappear() { handleOnDisappear() }
        ...
#else
    body_iOS
        .onAppear() { handleOnAppear() }
        .onDisappear() { handleOnDisappear() }
        ...
#endif

Any suggestions? Many thanks


Solution

  • You can wrap those kinds of directives in closures, while waiting for the missing feature that would negate the need for them:

      var body: some View {
        {
    #if os(watchOS)
          body_watchOS
    #else
          body_iOS
    #endif
        } ()
          .onAppear(perform: handleOnAppear)
    
    

    However, it's not obvious why you don't just do this:

      var body: some View {
        _body
          .onAppear(perform: handleOnAppear)
    
    #if os(watchOS)
      private var _body: some View {
        …
      }
    #else
      private var _body: some View {
        …
      }
    #endif