It's been asked a couple times before (1, 2) without an answer, so does anyone know what prevents the following from compiling?
Text("Hello").body // ๐
Value of type 'Text' has no member 'body'
Text (and many other SwiftUI views) have Body = Never, and while it makes sense that invoking .body isn't useful, it's not clear what compiler affordance is preventing it.
I haven't been able to reproduce the behavior myself, though, by employing an underscored protocol, so is it something else? Is there special compiler logic involved? An underscored attribute that I'm overlooking in the Swift interface?
Isn't simpler than that? Text("Hi there").body is requesting that a value be materialized, and that's simply not possible for Never types, by definition.
SwiftUI doesn't call the body of views directly, it invokes a hidden requirement of the View protocol. The default implementation of that requirement, which all user-defined views call, is defined in terms of body. Primitive views provided by the framework have implementations that don't use the body. No compiler magic necessary.
In fact, SwiftUI's swiftinterface file doesn't declare a body property for Text. Perhaps there is no compiler magic, and instead the SwiftUI framework simply has a (magic) build step that strips it from the swiftinterface file.
Great detective work! And I like your "body(of: Text("hello"))" (so we can still call it even if it is "hidden").
I totally understand the desire to know the relevant ins and outs of how compiler doing this. That aside, I'm curious why would anyone want to call View's body directly?
While I'm sure there are folks who can come up with some interesting use cases, I'm more interested from a library design perspective. Principles of SwiftUI's design can be applied to a lot of other problem domains where you compose types together using result builders, and the Body = Never pattern is a nice way to express primitives that aren't directly composed out of other units. I was curious if the compiler magic that prevented the concrete body properties from being invoked was more broadly available, but if @mayoff's right about the Swift interface itself being stripped, I guess that's not the case
It's just a convention - Xcode hides methods with leading underscore from the interface, but actually they are public methods in the protocol and you can implement them yourself, but to provide a meaningful implementation you need access to private types.
By default, Swift tooling hides any identifier that starts with an underscore and is declared in a swiftinterface file. The compiler only generates a swiftinterface file for a module built with library evolution enabled, so this doesn't normally apply to your own modules, only to system modules and third-party binary modules.
Shows underscored protocols from the standard library in the generated interface.
By default, SourceKit hides underscored protocols from the generated swiftinterface (for all modules, not just the standard library), but this attribute can be used to override that behavior for the standard library.
Worth pointing out that there's a difference between "the generated interface", which is what Xcode shows when you command-click a module and displays all the declarations in a module, and the swiftinterface file, which is a stable textual format that fully specifies the ABI of a module, and while it more-or-less human readable, full human readability is not required for this format the way that it is for the "generated interface"
The compiler writes MyLib.swiftinterface as follows, omitting the body declaration:
// swift-interface-format-version: 1.0
// swift-compiler-version: Apple Swift version 5.7 (swiftlang-5.7.0.123.7 clang-1400.0.29.50)
// swift-module-flags: -target arm64-apple-macosx12.0 -enable-objc-interop -enable-library-evolution -module-name MyLib
import Swift
import SwiftUI
import _Concurrency
import _StringProcessing
public struct MyView : SwiftUI.View {
public typealias Body = Swift.Never
}
And it writes MyLib.private.swiftinterface as follows, containing the body declaration:
// swift-interface-format-version: 1.0
// swift-compiler-version: Apple Swift version 5.7 (swiftlang-5.7.0.123.7 clang-1400.0.29.50)
// swift-module-flags: -target arm64-apple-macosx12.0 -enable-objc-interop -enable-library-evolution -module-name MyLib
import Swift
import SwiftUI
import _Concurrency
import _StringProcessing
public struct MyView : SwiftUI.View {
@_spi(Private) @_Concurrency.MainActor(unsafe) public var body: Swift.Never {
get
}
public typealias Body = Swift.Never
}
@mayoff Thank you for sharing this insight! It makes me wonder why the library doesn't hide more implementation details this way, such as Never's body, the various public-but-underscored requirements for View, ViewModifier, etc.
The @_spi attribute was introduced a couple of years after SwiftUI. Most underscored members and types pre-date the attribute, though there are still some cases where @_spi isnโt general enough and underscored members are still used.