NEW: My new book Pro SwiftUI is out now – level up your SwiftUI skills today! >>

Day 72 Project 14 part 5

Forums > 100 Days of SwiftUI

I have completed the BucketList project by following the instructions to make the project conform to MVVM. But I am still getting the purple warning message "Publishing changes from within view updates is not allowed, this will cause undefined behavior."

It is showing up in my BucketListApp.swift file and nowhere else, so I'm not sure what this would mean. The warning shows on the line that just has @main on it.

import SwiftUI

@main
struct BucketListApp: App {
  var body: some Scene {
    WindowGroup {
      ContentView()
    }
  }
}

Did anybody else run into this problem, or have I just messed something up while following the instructions?

Day 72 Project 14 part 5

   

The compiler is lying to @ostrich:

It is showing up in my BucketListApp.swift file and nowhere else,
so I'm not sure what this would mean.

This is the error message:

the purple warning message "Publishing changes from within view updates is not allowed,
this will cause undefined behavior."

This is a clear error, but may be tricky to find.

In short, what causes any view struct to redraw itself? By this point in your lessons, you should have a great handle on these reasons. Primarly, if one of the struct's @State or similar variables change, the struct will ask the OS to recalculate the struct, and redraw it if necessary. Also, changes to your data set may cause a view to redraw itself. But what if you cause the view to redraw itself, but WHILE it's calculating its new look, you somehow change the dataset again? This is not allowed.

What the purple message is telling you is that while the OS is updating one of your views (Button color? Label text? List content? Some internal VStack alignment?) you are attempting to change the view!

Wait whut?

Think about it. While you are redrawing a view struct, let's say a button, the button struct is telling the OS, "Wait a minute, please change my color while you are drawing me." This may cause undefined behavior, because (perhaps) the button tries to redraw itself again, causing yet another color change.

Weird!

   

I understand the concept of what we are trying to do. But I find it strange that even by following the instructions provided, I still get the warning message, even when Paul says that it should disappear at this point. (Unless I missed a step somewhere that I can't seem to find.)

import SwiftUI
import MapKit

struct ContentView: View {
  @StateObject private var viewModel = ViewModel()

  var body: some View {
    ZStack {
      if viewModel.isUnlocked {
        Map(coordinateRegion: $viewModel.mapRegion, annotationItems: viewModel.locations) { location in
          MapAnnotation(coordinate: location.coordinate) {
            VStack {
              Image(systemName: "star.circle")
                .resizable()
                .foregroundColor(.red)
                .frame(width: 34, height: 34)
                .background(.white)
                .clipShape(Circle())

              Text(location.name)
                .fixedSize()
            }
            .onTapGesture {
              viewModel.selectedPlace = location
            }
          }
        }
        .ignoresSafeArea()

        Circle()
          .fill(.blue)
          .opacity(0.3)
          .frame(width: 32, height: 32)

        VStack {
          Spacer()

          HStack {
            Spacer()

            Button {
              viewModel.addLocation()
            } label: {
              Image(systemName: "plus")
                .padding()
                .background(.black.opacity(0.5))
                .foregroundColor(.white)
                .font(.title)
                .clipShape(Circle())
                .padding(.trailing)
            }
          }
        }
      } else {
        Button("Unlock Places") {
          viewModel.authenticate()
        }
        .padding()
        .background(.blue)
        .foregroundColor(.white)
        .clipShape(Capsule())
      }
    }
    .sheet(item: $viewModel.selectedPlace) { place in
      EditView(location: place) {
        viewModel.update(location: $0)
      }
    }
  }
}

The only line I can see where the data model might be modified by something outside of the ViewModel is

viewModel.selectedPlace = location

And Paul tells us in the video instructions that there isn't much we can do about that line so just leave it as is. So, if that is the problem, I don't know how to fix it.

   

I commented out this section of my code, and it seems to make the warnings go away. So, the problem must be in this segment of code. This leads me to believe that the binding $viewModel.mapRegion being updated when the Map view loads is what is causing the issue. But I don't know how I could make that run on the main actor instead...

Unless I am supposed to make this entire Map View a property of my ViewModel maybe 🧐

        Map(coordinateRegion: $viewModel.mapRegion, annotationItems: viewModel.locations) { location in
          MapAnnotation(coordinate: location.coordinate) {
            VStack {
              Image(systemName: "star.circle")
                .resizable()
                .foregroundColor(.red)
                .frame(width: 34, height: 34)
                .background(.white)
                .clipShape(Circle())

              Text(location.name)
                .fixedSize()
            }
            .onTapGesture {
              viewModel.selectedPlace = location
            }
          }
        }
        .ignoresSafeArea()

   

@Ostrich puts on his thinkin' cap....

The only line I can see where the data model might be modified by something outside of the ViewModel is

    viewModel.selectedPlace = location // the viewModel publishes to views. Maybe this action is on an async thread?

So without seeing your viewModel, I can only guess what's on the other side. (I've not completed this lesson!)

Indeed, if you have a view model that writes to a remote data source (or even to disk?) you may have that code on a background thread. In that case (just guessing) you may be trying to update your view right now, even though the model may take a few seconds to save, publish and notify the views of the updated data.

(Previous thoughts may be pure nonsense. But it's a thread -ha! pun! - I would follow.)

   

I've discovered that a lot of people seem to be having problems with this since updating to XCode 14 https://developer.apple.com/forums/thread/711899?page=2

So, I guess there is probably some kind of bug causing this issue for me right now. It has just been very confusing for me since I am just barely trying to learn about this stuff.

   

Hacking with Swift is sponsored by Stream

SPONSORED Build Chat messaging quickly with Stream Chat. The Stream iOS Chat SDK is highly flexible, customizable, and crazy optimized for performance. Take advantage of this top-notch developer experience, get started for free today!

Click here

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

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.