How do views like EmptyView conform to the View protocol?

I've noticed that the body type of EmptyView is Never. But how does this conformance work?

For example, if I wanted to create my own "empty" view, I could do:

struct MyEmptyView: View {
  var body: Never {
    // What do I do here?
  }
}

but then what would I do in the body? I can't use a trapping method because it will cause a crash. So how does SwiftUI do it? Does it put a call to fatalError() or something and dynamically remove the call at runtime?

1 Like

cc @Joe_Groff

Why do you want to do this? What’s the use case?

Just curious :slight_smile:

can you call EmptyView body property?

I’d guess EmptyView has special consideration in SwiftUI. You could return an EmptyView value in your body? The return type would be some View. I’d guess it is not an appropriate idea though because you’d be doing something that isn’t view related in your view code.

I don't think EmptyView itself conforms to View (can't verify it at this moment). You can lookup yourself at:

/Applications/Xcode-beta.app/Contents/Developer/Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS.sdk/System/Library/Frameworks/SwiftUI.framework/Modules/SwiftUI.swiftmodule/arm64.swiftinterface

And the view builder might also pickup some hidden types such as _View. Again I can't verify this at this moment as I'm not at my mac with Catalina.

Okay I was right about the view builder:

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
@_functionBuilder public struct ViewBuilder {
  @_transparent public static func buildBlock() -> EmptyView {
        return EmptyView()
    }
  @_transparent public static func buildBlock<Content>(_ content: Content) -> Content where Content : SwiftUI.View {
        return content
    }
}

But EmptyView does conform to View:

extension SwiftUI.EmptyView : SwiftUI.View {}

But there is more:

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension Never {
  public typealias Body = Never
  public var body: Never {
    get
  }
}

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension Never {
  public typealias Value = Never
}

@available(iOS 13.0, OSX 10.15, tvOS 13.0, watchOS 6.0, *)
extension Never : View, _UnaryView {
}

Yeah I’ve looked at the swiftinterface file but I still don’t understand how the conformance is satisfied i.e I’m curious about what's happening in the body.

1 Like

I’m not sure about EmptyView in particular, but functions that have a Never return type, never return. For example:

func foo() -> Never {
    while true { }
}

There is likely some extension somewhere that looks like this:

extension View where Self.Body == Swift.Never {
  var body: Body {
    get
  }
}

But I can't find it.

Can you try and compile your type with just typealias Body = Never and drop the body?

That doesn't work:

struct MyEmptyView: View { // Type 'MyEmptyView' does not conform to protocol 'View'
  typealias Body = Never
}

That's odd, now you made me even more curious. cc @jrose could the swiftinterface file have a bug a not expose a public var body: Body for some reason?

Even if there's an extension like

extension View where Self.Body == Swift.Never {
  var body: Body {
    get { /* fatalError? */ }
  }
}

then what happens in the body of the getter? As the getter can't be empty or else you'd get Function with uninhabited return type 'Never' is missing call to another never-returning function on all paths.

That's weird though, I thought we don't need to return anything if the return value is Never. I recall switch working like that:

let never: Never! = nil

switch never as Never {
 // done
}

But I guess it would be fine to use fatalError here. I would assume that SwiftUI would check if the view has a Body that is Never and use other parts of View to do other things. I know I can only speculate here.

Yeah I guess there's some runtime dynamism involved.

Have you tried using fatalError and see if the custom view is accepted by the preview?

Yeah, if you use a fatalError() then the code traps at runtime (as expected), so you can't see the view.

The body doesn’t need to contain magic, it just can’t ever be called.

I recall discussion in the WWDC sessions of the View tree being collapsed/optimized. Speculation, but perhaps the EmptyView is optimized away.

I think for that reason EmptyView implements hidden static functions.

public static func _makeView(view: _GraphValue<EmptyView>, inputs: _ViewInputs) -> _ViewOutputs
public static func _makeViewList(view: _GraphValue<EmptyView>, inputs: _ViewListInputs) -> _ViewListOutputs