Unexplained SwiftUI - The Programming Language Nature of SwiftUI
Preface
Apple introduced SwiftUI at WWDC 2019. Most people may seem SwiftUI as yet another UI framework likes Flutter, React.js or Vue.js which rides on the trend of declarative stateless UI programming. Even though there are some common points between them, SwiftUI is far different from those UI frameworks I mentioned above from design to implementation.
In fact, SwiftUI is a programming language more than a UI framework. Don’t believe it? Let me show you an example which computes Fibonacci number with “native” SwiftUI code:
swiftimport SwiftUIstruct ContentView: View {var body: some View {Fibonacci(ordinal: 10)}}struct Fibonacci: View {var ordinal: Intprivate var value: Valueinit(ordinal: Int) {self.init(ordinal: ordinal, value: .zero)}private init(ordinal: Int, value: Value) {self.ordinal = ordinalself.value = value}var body: some View {if ordinal == 0 {Text("\(value.description)")} else {Fibonacci(ordinal: ordinal - 1, value: value.next())}}private enum Value: CustomStringConvertible {case zerocase onecase more(last: Int, current: Int)func next() -> Value {switch self {case .zero: return .onecase .one: return .more(last: 0, current: 1)case .more(let last, let current):return .more(last: current, current: last + current)}}var description: String {switch self {case .zero: return "0"case .one: return "1"case .more(let last, let current): return "\(last + current)"}}}}
By adding two lines of code, we can preview the code above in Swift Playground.
swiftimport PlaygroundSupportPlaygroundPage.current.setLiveView(ContentView())
As you see, we computed Fibonacci number with “native” SwiftUI code. But being able to write a program which computes Fibonacci number with things come from SwiftUI does not mean that SwiftUI is a programming language. We can only say that SwiftUI is programming language unless it offers real “language structures” which make the program ran.
Unexplained View Protocol
To understand the “language structure” of SwiftUI, we have to understand the unexplained part of View protocol firstly.
Basically, a Veiw protocol looks like below:
swiftpublic protocol View {associatedtype Body: View@ViewBuildervar body: Body { get }}
We can see that once a type conforms to View protocol, its instance has a body property which returns an instance whose type also conforms to View protocol. This leads an interesting phenomenon: if we get the body of a View instance then we can get the body of the body of this View instance. Since the body of the body of the View instance is also a View instance, thus we can continuously get its body. Theoretically, we can repeat this pattern infinitely.
Yes. You may have noticed that the design of View protocol is recursive. In fact, this recursive design of View protocol offers the basic language structure of SwiftUI which introduces a recursive-able execution pattern. SwiftUI’s internal mechanism can continually get the body of a View instance unless the recursion ended.
But when does the recursion end?
SwiftUI extends the Never type in Swift Standard Library to conform to View protocol. In this extension, Never’s body also returns Never. Thus we can know that this is the recursion end.
swiftextension Never: View { }extension Never {public typealias Body = Neverpublic var body: Never { get }}
In SwiftUI’s internal implementation, once the body of a View instance is Never type, SwiftUI stops getting the body of the View instance and switches to the built-in logic of the view. If the built-in logic does not hand over the execution to other View instances, the recursion ends.
SwiftUI ships with a lot of View types whose body is Never type. Since this kind of View types have their own built-in logic, they are also called built-in views.
In the example of Fibonacci number I showed above, the recursion ends when the “program” of SwiftUI reaches the Text view in the body of Fibonacci — because the Text view’s body is Never type and the built-in logic of Text view would not hand over the execution to other View instances.
swiftextension Text : View {public typealias Body = Never}
Other Language Structures
In previous descriptions, I narrowed the condition of the end of the recursion in SwiftUI’s execution pattern to be:
A built-in view whose logic does not hand over the execution to other
Viewinstances
From this description, you may infer that there are built-in views in SwiftUI which hand over execution to other View instances. And yes, there are.
To help understand the language structures offered by SwiftUI in the Fibonacci number example I shown above, I would introduce one of them here — the language structure brought by if...else... statement in the body of Fibonacci — _ConditionalContent.
swiftstruct Fibonacci: View {var body: some View {// The `if...else...` statement// would finally generates a// `_ConditionalContent` instanceif ordinal == 0 {// ...} else {// ...}}}
_ConditionalContent is not exposed in SwiftUI’s public documentation. But we can find it in SwiftUI’s swiftinterface file.
swiftpublic struct _ConditionalContent<TrueContent, FalseContent> {internal enum Storage {case trueContent(TrueContent)case falseContent(FalseContent)}internal let storage: Storage}extension _ConditionalContent : View where TrueContent : View, FalseContent : View {public typealias Body = Neverinternal init(storage: Storage)}
swiftinterfacefile to a Swift module is just like header files to a clang module.
At the same time, there is an extension for SwiftUI’s ViewBuilder which shows when we using if...else... statements in a @ViewBuilder marked-up function, the Swift compiler would generate codes which wrap branches of if...else... statement into a _ConditionalContent at compile-time: when the expression after if evaluated into true, the generated code invokes first buildEither function; when the expression evaluated into false, the generated code invokes the second buildEither function.
swiftextension ViewBuilder {public static func buildEither<TrueContent : View, FalseContent : View>(first: TrueContent) -> ConditionalContent<TrueContent, FalseContent> {.init(storage: .trueContent(first))}public static func buildEither<TrueContent : View, FalseContent : View>(second: FalseContent) -> ConditionalContent<TrueContent, FalseContent> {.init(storage: .falseContent(second))}}
By combining what we found in ViewBuilder extension and the design of _ConditionalContent, we can infer that a _ConditionalContent instance is able to carry two kinds of instance — one refers to the content under true branch of the if...else.. statement and the other refers to the content under false branch.
For example, in the Fibonacci number example, when the expression after if evaluated into true, the body of Fibonacci equals to the following code:
swiftstruct Fibonacci: View {var body: some View {_ConditionalContent(storage: .trueContent(Text(...)))}}
when the expression evaluated into false, the body of Fibonacci equals to the following code:
swiftstruct Fibonacci: View {var body: some View {_ConditionalContent(storage: .falseContent(Fibonacci(...)))}}
And the built-in logic of _ConditionContent would finally hand over the execution to the View instance stored in its storage.
Thus we can know that unless the ordianal of Fibonacci is equal to 0, the Fibonacci’s body always recursively generates another Fibonacci. And when the ordinal of Fibonacci is equal to 0, the Fibonacci’s body generates a Text view which renders the output value of Fibonacci.
Conclusion
With previous analysis, we can clearly know that SwiftUI does offer the critical language structures for the execution in the example of computing Fibonacci number above — which include:
- The View protocol which introduces recursive-able execution pattern.
- A View protocol conformed type: _ConditionalContent which enables conditional branching.
which is quite like a real programming language. Thus we can say that SwiftUI is a programming language more than a UI framework.
A Formal Perspective
Actually, the Fibonacci example shows that SwiftUI is Turing complete: if a procedure can re-enter itself, and the procedure itself can change its behavior infinitely, then the language behind the procedure is Turing complete.

