BLACK FRIDAY: Save 50% on all my Swift books and bundles! >>

SOLVED: _variable - confusion with underscore

Forums > SwiftUI

In day 58, Paul uses an underscore in front of the fetchRequest variable in the initializer. I'm really not understanding the whole purpose of the underscore.

Why is this code valid:

struct FilteredList: View {
    @State private var score = 0
    @FetchRequest var fetchRequest: FetchedResults<Singer>

    var body: some View {
        List(fetchRequest, id: \.self) { singer in
            Text("\(singer.wrappedFirstName) \(singer.wrappedLastName)")

        }
    }

    init(filter: String) {
        _fetchRequest = FetchRequest<Singer>(sortDescriptors: [], predicate: NSPredicate(format: "lastName BEGINSWITH %@", filter))
    }
}

but this isn't?

struct AddView: View {
    @State private var score = 0

    var body: some View {
        Text(String(score))
    }

    init() {
        _score = 10
    }
}

gives error: Cannot assign value of type 'Int' to type 'State<Int>'

2      

The underscore refers to the property wrapper itself.

score refers to the value inside the property wrapper.

_score refers to the property wrapper.

$score refers to the property wrapper's projected value; in the case of @State, that is a Binding.

2      

Thanks. Though I'm not sure I'm any less confused.

How do you know when you can use the underscore?

2      

Save 50% in my WWDC sale.

SAVE 50% All our books and bundles are half price for Black Friday, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Mostly when a new property wrapper is being created, as in your first example. A new FetchRequest wrapper is created using the value of the filter parameter so you assign it to _fetchRequest (i.e., the wrapper itself) rather than fetchRequest (i.e., the value inside the wrapper).

In your second example, you would need to do:

_score = State(wrappedValue: 10)

5      

I'm having difficulty with this concept too. I've read a lot about property wrappers and they all say the same thing @roosterboy says, but I still don't understand it. What does 'a wrapped variable' actually mean? How is it different from a non-wrapped variable? How is 'refers to the value inside the property wrapper' different from 'refers the property wrapper' I understand how to use property wrappers (@State, @Binding, @StateObject, @ObservedObject, @Published) but I don't understand what's going on. I'm lost.

2      

First of all here are lists (Page 1 Page 2 ) of the terms - read at your leisure - they outline the numerous property wrapper types, with brief descriptions.

You can think of property wrappers as containers - containers that have been designed for specific purposes, from varying materials (methods), with particular attributes. Typically they have more complex behaviours than say an Int or String

This line says that we need a container (wrapper) that will hold some type of results.

@FetchRequest var fetchRequest: FetchedResults<Singer>

These lines are saying how to make the container (wrapper), so we need to refer to the container itself, not the contents.

init(filter: String) {
    _fetchRequest = FetchRequest<Singer>(sortDescriptors: [], predicate: NSPredicate(format: "lastName BEGINSWITH %@", filter))
}

In the second example it is nonsense, because you are saying the State property wrapper (the container) is set to the value 10.

init() {
    _score = 10
}

So the correct version is saying that the State property wrapper is a State container type that wraps around (contains) the value 10.

_score = State(wrappedValue: 10)

3      

Thanks, that helps

2      

The best way I've been able to wrap my head around property wrappers is to understand what the compiler synthesized in your code (source: SE-0258).

E.G. When you use:

// Note: no use of `private`
@State var score = 0

the compiler turns that into:

private var _score: = State<Int>(wrappedValue: 0)
var score: Int {
    get { return _score.wrappedValue }
    set { _score.wrappedValue = newValue }
}
var $score: Binding<Int> {
    get { return _score.projectedValue }
}

And when you use:

@FetchRequest var fetchRequest: FetchedResults<Singer>

the compiler synthesizes:

private var _fetchRequest = FetchRequest<FetchedResults<Singer>>()
var fetchRequest: FetchedResults<Singer> {
    get { return _fetchRequest.wrappedValue }
}
var $fetchRequest: Binding<FetchRequest<Result>.Configuration> {
    get { return _fetchRequest.projectedValue }
}

So to understand exactly what variables and types you'll end up with in your code, you often have to peek at the documentation for the property wrapper you're using, specifically its wrappedValue & projectedValue properties.

Here's a pseudo-code template for the above compiler synthesis:

@SomePropertyWrapper «my-access-level» var myVar: MyVarsType = «initial-myVar-value»

turns into:

private var _myVar = SomePropertyWrapper<MyVarsType>(wrappedValue: «initial-myVar-value»)

«my-access-level» var myVar: MyVarsType {
    get { return _myVar.wrappedValue }

    #if «SomePropertyWrapper's wrappedValue is settable»
    set { _myVar.wrappedValue = newValue }
    #endif
}

#if «SomePropertyWrapper has a projectedValue»
«my-access-level» var $myVar: «type-of-SomePropertyWrapper's-projectedValue» {
    get { return _myVar.projectedValue }

    #if «SomePropertyWrapper's projectedValue is settable»
    set { _myVar.projectedValue = newValue }
    #endif
}
#endif

or more simply:

private var _myVar = SomePropertyWrapper<MyVarsType>(wrappedValue: «initial-myVar-value»)
«my-access-level» var myVar: MyVarsType { «alias for `_myVar.wrappedValue`» }
«my-access-level» var $myVar: «type-of-SomePropertyWrapper's-projectedValue» { «alias for `_myVar.projectedValue`» }

So the big takeaways when using a property wrapper are:

  • _myVar will be the property wrapper instance, synthesized into your code.
  • myVar will be a property that gets/sets the property wrapper instance's wrappedValue property (which is a property/variable of the type you originally specified). The property wrapper technically can get/set anything of that type, but wrappedValue is usually just a simple variable that's effectively your original variable “moved” into the property wrapper instance.
  • $myVar (if available) can be of any type decided by the property wrapper. You can't expect it to be any specific type; you have to look at the property wrapper's docs/code to see what its projectedValue is giving you (if anything). For SwiftUI stuff, it usually seems to be a Binding or ObserverObject pointing to the property wrapper's wrappedValue.

5      

I kinda understand the functuanality _variable does, but why we can't just asign fetchRequest to new FetchRequest without "_"?

(in this code):

 init(filter: String) {
     _fetchRequest = FetchRequest<Singer>(sortDescriptors: [], predicate: NSPredicate(format: "lastName BEGINSWITH %@", filter))
}

2      

Because assigning a value to fetchRequest assigns it to the wrappedValue inside the property wrapper.

Assigning to _fetchRequest assigns to the property wrapper itself.

2      

Save 50% in my WWDC sale.

SAVE 50% All our books and bundles are half price for Black Friday, so you can take your Swift knowledge further without spending big! Get the Swift Power Pack to build your iOS career faster, get the Swift Platform Pack to builds apps for macOS, watchOS, and beyond, or get the Swift Plus Pack to learn advanced design patterns, testing skills, and more.

Save 50% on all our books and bundles!

Archived topic

This topic has been closed due to inactivity, so you can't reply. Please create a new topic if you need to.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.