The concepts behind SwiftUI: What is the keyword “some” doing?

When you want to start learning SwiftUI, you create a project and get a basic example view with just a Text field. That default view is pretty simple, but already introduces many new concepts. Let’s focus in this article on the some keyword.

Note: If you are new to SwiftUI, you may want to read the The concepts behind Swift UI: Introduction.

The default SwiftUI content view

Here is an empty default view:

import SwiftUI

struct ContentView: View {
    var body: some View {
        Text("Hello World")
    }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

The ContentView is an implementation of the View protocol. The View protocol requires a body computed property of type some View. So, you may be wondering what is that some keyword doing?

Opaque Types

The some keyword was introduced in Swift 5.1 and is used to define an Opaque Type. An opaque type is a way to return a type without needing to provide details on the concrete type itself. It limits what callers need to know about the returned type, only exposing information about its protocol compliance. Using an opaque type is a way to let the compiler decide what would be the concrete type of a function return, based on the actual returned value, limiting the options to the types that comply to a given protocol.

So, in SwiftUI case, “some View” means that the body will always be implementing the View protocol, but the concrete implementation type does not need to be known by the caller.

As explained in Swift documentation on Opaque Types:

You can think of an opaque type like being the reverse of a generic type.

An opaque type lets the function implementation pick the type for the value it returns in a way that’s abstracted away from the code that calls the function.

In other words, standard generic placeholders are filled by the caller. When you call a generic function, you are constraining the generic types to the types passed to that function. You can think of opaque types as a kind of generic function where placeholder types are filled by the implementation return type.

Examples

In the following code using opaque type, the body will always be of type Text:

struct ContentView: View {
    var x: Bool = false

    var body: some View {
        if x {
            return Text("This is true")
        } else {
            return Text("This is false")
        }
    }
}

At compile time, the function is known to return a Text view. The code is valid because Text view is implementing the View protocol.

However, the following code does not contain a valid opaque type, as the two code branches return different concrete types. The first branch returns a Text view, the second branch returns a VStack view.

struct ContentView: View {
    var x: Bool = false

    var body: some View {
        if x {
            return Text("This is true")
        } else {
            return VStack { Text("This is false") }
        }
    }
}

The returned type cannot be known at compile time, so this is not a valid opaque type.

If we compare with just protocol constraints, the following code is valid because the computed value body is not an opaque type but a protocol. The type information are “dropped” by the compiler and the only method that can be called on that result are the one implemented by the protocol (in our case, none).

protocol P {}
struct S1: P {}
struct S2: P {}

var x: Bool = false

var body: P {
    if x {
        return S1()
    } else {
        return S2()
    }
}

Conclusion

If you are interested to dig deeper, you should read Opaque Types documentation, especially the section on the ‘Differences Between Opaque Types and Protocol Types’.

On the surface returning an opaque type or a protocol can look similar, but the opaque types solve many limitations encountered when using directly protocol return types. The main limitations is that functions returning a protocol are not nestable, because a value of a protocol type doesn’t conform to that protocol. Opaque types preserve the underlying type information through associated types and thus can be nested. That’s the reason why Opaques Types were needed to implement SwiftUI.

There is a lot more to learn on Opaque Types, as it is an advanced concept. You do not need to master opaque types to be using SwiftUI but, as always, it is still better to understand the fundamental idea to progress on your learning path.


Leave a Comment


This site uses Akismet to reduce spam. Learn how your comment data is processed.