iOS Development

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


I am creating an app that permits for content material creation and show. The UX I yearn for requires the content material creation view to make use of programmatic navigation. I intention at structure with a important view mannequin and a further 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 title
  • abstract display screen

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

Code

ContentView

struct ContentView: View {
    @EnvironmentObject var mannequin: ContentViewViewModel
    var physique: some View {
        VStack {
            NavigationStack(path: $mannequin.path) {
                Record(mannequin.content material) { ingredient in
                    Textual content(ingredient.title)
                }
                .navigationDestination(for: Content material.self) { ingredient in
                    ContentDetailView(content material: ingredient)
                }
                .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.title)
    }
}

AddContentPresenterView

As navigationDestination associates a vacation spot view with a introduced information kind 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("Title 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() {
        swap path.depend {
        case 0:
            path.append(Web page.contentName)
        case 1:
            path.append(Web page.contentSummary)
        default:
            fatalError("Navigation error.")
        }
    }
    
    func navigateToRoot() {
        path.removeLast(path.depend)
    }
    
    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(title: contentName)
    }
}

Web page

Enum containing creation display screen pages.

enum Web page: Hashable {
    case contentName, contentSummary
}

What’s improper

Presently, for every web page pushed onto the navigation stack, a brand new StateObject is created. That makes the creation of object not possible, because the addContentViewModel.contentName holds worth just for the certain display screen.

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

What I’ve tried

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

Wraping up

The right 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 technique for my AddContentViewModel and reset the info on getting into or quiting the display screen?

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

What's your reaction?

Leave A Reply

Your email address will not be published.