1

In Swift 5.1 there are opaque types. I see that e.g. body is a required protocol variable. It's contract is defined as follows:

var body: Self.Body { get }

It means we should be able to mark body as immutable (no set). How must this be done? Is it possible for opaque variable types to be immutable? I tried:

import SwiftUI

struct ContentView : View {

    init() {
        body = AnotherView(body: Text(""))
    }

    let body: some View
}

struct AnotherView: View {
    var body: Text
}

But I get the error that AnotherView must be casted to some View. After doing that, I get the error:

'some' types are only implemented for the declared type of properties and subscripts and the return type of functions

Am I able to conform to View with immutable body variables which are of type some View (not marking it explicit as AnotherView)? AnotherView is some View, I don't understand why I can't just assign the instance of AnotherView to body. I want to remain flexible and not expose the actual implementation type of body outside the struct, but I want to initialize it directly inside the initializer (because I am passing in values inside the initializer, making more properties and use them in the body property is verbose).

1 Answer 1

1

Because there is no setter, any body implementation that is a value type will be immutable. The var just means that body is lazily evaluated, not that it is mutable. You could declare let body, but, as you point out, this exposes the underlying View's implementation:

public struct StaticTextView : View {
    public let body: Text

    public init(string: String) {
        self.body = Text(string)
    }
}

One way you could fix this would be to have body just return an internal private value, like so:

public struct StaticTextView : View {
    private let textView: Text
    public var body: some View { textView }

    public init(string: String) {
        self.textView = Text(string)
    }
}

However, you should bear in mind that body is designed to be run dynamically whenever any of the bound state changes, and if you want to assign your view to a constant, nothing in that view hierarchy could be bound to any dynamic state. For example, this would not be possible:

struct DynamicStepperView : View {
    @State var stepperValue = 1

    var body: some View {
        Stepper(value: $stepperValue, in: 1...11, label: { Text("Current Value: \(stepperValue)") })
    }
}

If your primary concern is preventing the leaking of implementation details of your view hierarchy, note that the opaque return type of some View is indeed opaque to any clients of the code, and they will not be able to see any of the details of the underlying implementation other than that it is something that conforms to the View protocol.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.