The compiler can at times throw confusing errors at you and "Property wrapper cannot be applied to a computed property" is one such error. In this episode, you learn what the error means, why you are running into it, and how you can resolve it.

What Does It Mean?

To be clear, the error itself isn't unclear or confusing. The compiler is correct that the Published property wrapper cannot be applied to computed properties. If you apply the Published property wrapper to a computed property, you made a mistake you need to fix. In that case, I don't have a solution for you.

Property Wrapper Cannot Be Applied to a Computed Property

Why Are You Seeing the Error?

If you applied the Published property wrapper to a stored property and the compiler throws the aforementioned error at you, you are most likely using the Observable() macro. Correct? Let me explain what the problem is with an example from Mastering MVVM with SwiftUI.

In Migrating from ObservableObject to Observable, we refactor the LocationViewModel class. It no longer conforms to the ObservableObject protocol. It instead adopts Observation through the Observable() macro. Take a look at this code snippet to better understand what that looks like.

// Before
@MainActor
final class LocationViewModel: ObservableObject {

    // MARK: - Properties

    @Published private(set) var state: State = .fetching

	...

}
// After
@MainActor
@Observable final class LocationViewModel {

    // MARK: - Properties

    @Published private(set) var state: State = .fetching

	...

}

The compiler throws an error the moment we apply the Observable() macro to the declaration of the LocationViewModel class. Why is that? The compiler complains that the Published property wrapper cannot be applied to the state property because it is a computed property. This is confusing because the state property is a stored property, not a computed property.

The compiler is correct, though. I won't cover macros in detail in this episode. What you need to know is that a macro changes the code it is applied to. This happens before the code is compiled and that can make it difficult and confusing to debug issues like this. The code we wrote looks fine. It is the code the Observable() macro generates that is causing a problem.

Simply put, the Observable() macro converts the stored state property to a computed state property. It generates setters and getters for the computed state property. With this in mind, the error the compiler throws makes more sense.

At the time of writing, the Observable() macro cannot be used in combination with the Published property wrapper. This might be a temporary limitation that is addressed in a future release.

How Do You Resolve the Error?

The good news is that we no longer need to apply the Published property wrapper to the state property. By adopting Observation, changes of the state property are automatically detected and propagated. There is no longer a need for us to use the Published property wrapper in the LocationViewModel class.

The solution is simple. Remove the Published property wrapper from the property declaration. That's it.

@MainActor
@Observable final class LocationViewModel {

    // MARK: - Properties

    private(set) var state: State = .fetching

	...

}

Other Options

It is possible that you cannot remove the Published property wrapper without breaking your code. For example, it is possible the view model subscribes to the state publisher. You have two options if that is true for you. The first option is to leave your implementation as is. Don't adopt Observation. It it doesn't replace the ObservableObject protocol, nor does it make the Published property wrapper obsolete. It is fine to continue using the ObservableObject protocol in combination with Published properties.

The second option is to get rid of the Published properties and use Combine's CurrentValueSubject. Remember that the Published property wrapper isn't magical. It simply exposes a publisher that emits the values of the stored property it is applied to. You can implement a similar solution using the CurrentValueSubject class.