SwiftUI Modifying state during view update, this will cause undefined behavior - Proper use explanation

Viewed 41

Im learning swift and this error/warning is driving me crazy because I cant see what call Im making that causing it... The Xcode warning only shows up in my @main struct

Modifying state during view update, this will cause undefined behavior.

I thought it might be in the ListView, but I realized the warning only shows after the "Submit Post" button is it.

Im looking for a fix, but more importantly and explanation as to why this is happening and the proper usage moving forward.

import SwiftUI
import Firebase

@main

struct SocialcademyApp: App {
    
    init() {
        FirebaseApp.configure()
    }
    
    var body: some Scene {
        WindowGroup {
            PostsList()
        }
    }
}
struct PostsList: View {
    
   @StateObject var viewModel = PostsViewModel()
   @State private var searchText = ""
   @State private var showNewPostForm = false
    
    var body: some View {
        
        NavigationView {
            List(viewModel.posts) { post in
                if searchText.isEmpty || post.contains(searchText) {
                    PostRow(post: post)
                }
            }
            .searchable(text: $searchText)
            .navigationTitle("Posts")
            .toolbar {
                Button {
                    showNewPostForm = true
                } label: {
                    Label("New Post", systemImage: "square.and.pencil")
                }
            }
            .sheet(isPresented: $showNewPostForm) {
                NewPostView(creationAction: viewModel.makeCreationAction())
            }
        }
        
    }
}
struct NewPostView: View {
    
    typealias CreationAction = (Post) async throws -> Void
    let creationAction: CreationAction
    
    @State private var post = Post(title: "", content: "", authorName: "")
    @State private var state = FormState.idle
    
    @Environment(\.dismiss) private var dismiss
    
    var body: some View {
        NavigationView {
            Form {
                Section {
                    TextField("Title", text: $post.title)
                    TextField("Author Name", text: $post.authorName)
                }
                Section {
                    TextField("Content", text: $post.content)
                        .multilineTextAlignment(.leading)
                }
                Button(action: createPost, label: {
                    if state == .working {
                        ProgressView() } else {
                            Text("Submit Post")
                        }
                })
                .font(.headline)
                .frame(maxWidth: .infinity)
                .foregroundColor(.white)
                .padding()
                .listRowBackground(Color.accentColor)
            }
        }
        .navigationTitle("New Post")
        .disabled(state == .working)
        .alert("Cannot Create Post", isPresented: $state.isError, actions: {}) {
            Text("Sorry, something went wrong")
        }
        .onSubmit {
            createPost()
        }
    }
    
    private func createPost() {
        print("[NewPostForm] creating a new post")
        Task {
            state = .working
            do {
                try await creationAction(post)
                dismiss()
            } catch {
                state = .error
                print("[NewPostForm] Cannot create post: \(error)")
            }
        }
    }
}

private extension NewPostView {
    
    enum FormState {
        case idle, working, error
        
        var isError: Bool {
            get {
                self == .error
            }
            set {
                guard !newValue else { return }
                self = .idle
            }
        }
    }
    
    
    
}
@MainActor
class PostsViewModel: ObservableObject {
    @Published var posts = [Post.testPost]
    
    func makeCreationAction() -> NewPostView.CreationAction {
        return { [weak self] post in
            try await PostsRepository.create(post)
                self?.posts.insert(post, at: 0)
        }
    }
}
0 Answers
Related