ios – The best way to maintain SwiftUI from creating extra StateObjects on this customized web page view?


I am creating an app that enables for content material creation and show. The UX I yearn for requires the content material creation view to make use of programmatic navigation. I purpose at structure with a foremost view mannequin and an extra one for the content material creation view. The issue is, the content material creation view mannequin doesn’t work as I anticipated on this particular instance.


Code construction

Suppose there’s a ContentView: View with a nested AddContentPresenterView: View. The nested view consists of two phases:

  • specifying object’s identify
  • abstract display screen

To permit for programmatic navigation with NavigationStack (new in iOS 16), every part has an related worth.

Code

ContentView

struct ContentView: View {
    @EnvironmentObject var mannequin: ContentViewViewModel
    var physique: some View {
        VStack {
            NavigationStack(path: $mannequin.path) {
                Listing(mannequin.content material) { aspect in
                    Textual content(aspect.identify)
                }
                .navigationDestination(for: Content material.self) { aspect in
                    ContentDetailView(content material: aspect)
                }
                .navigationDestination(for: Web page.self) { web page in
                    AddContentPresenterView(web page: web page)
                }
            }
            Button {
                mannequin.navigateToNextPartOfContentCreation()
            } label: {
                Label("Add content material", systemImage: "plus")
            }

        }
    }
}

ContentDetailView (irrelevant)

struct ContentDetailView: View {
    let content material: Content material
    var physique: some View {
        Textual content(content material.identify)
    }
}

AddContentPresenterView

As navigationDestination associates a vacation spot view with a introduced information sort to be used inside a navigation stack, I discovered no higher method of including a paged view to be navigated utilizing the NavigationStack than this.

extension AddContentPresenterView {
    var contentName: some View {
        TextField("Identify your content material", textual content: $addContentViewModel.contentName)
            .onSubmit {
                mannequin.navigateToNextPartOfContentCreation()
            }
    }
    var contentSummary: some View {
        VStack {
            Textual content(addContentViewModel.contentName)
            Button {
                mannequin.addContent(addContentViewModel.createContent())
                mannequin.navigateToRoot()
            } label: {
                Label("Add this content material", systemImage: "checkmark.circle")
            }
        }
    }
}

ContentViewViewModel

Controls the navigation and including content material.

class ContentViewViewModel: ObservableObject {
    @Printed var path = NavigationPath()
    @Printed var content material: [Content] = []
    
    func navigateToNextPartOfContentCreation() {
        change path.rely {
        case 0:
            path.append(Web page.contentName)
        case 1:
            path.append(Web page.contentSummary)
        default:
            fatalError("Navigation error.")
        }
    }
    
    func navigateToRoot() {
        path.removeLast(path.rely)
    }
    
    func addContent(_ content material: Content material) {
        self.content material.append(content material)
    }
}

AddContentViewModel

Manages content material creation.

class AddContentViewModel: ObservableObject {
    @Printed var contentName = ""
    
    func createContent() -> Content material {
        return Content material(identify: contentName)
    }
}

Web page

Enum containing creation display screen pages.

enum Web page: Hashable {
    case contentName, contentSummary
}

What’s fallacious

At the moment, for every web page pushed onto the navigation stack, a brand new StateObject is created. That makes the creation of object unimaginable, because the addContentViewModel.contentName holds worth just for the sure display screen.

I assumed that, since StateObject is tied to the view’s lifecycle, it is tied to AddContentPresenterView and, subsequently, I’d be capable to share it.

What I’ve tried

The error is resolved when addContentViewModel in AddContentPresenterView is an EnvironmentObject initialized in App itself. Then, nevertheless, it is tied to the App‘s lifecycle and subsequent content material creations greet us with stale information – accurately.

Wraping up

The best way to maintain SwiftUI from creating extra StateObjects on this customized web page view?

Ought to I resort to ObservedObject and take a look at some wizardry? Ought to I simply implement a reset methodology for my AddContentViewModel and reset the information on coming into or quiting the display screen?

Or possibly there’s a higher method of reaching what I’ve summarized in summary?

Leave a Reply