Skip to content

onPreview modifier breaks shared scope #146

Closed
@rmickeyd

Description

The onPreview modifier seems to break the shared scope. Looking at the example below, when the onPreview modifier is commented out the DefaultManager will init exactly once and the same instance will be used in both the TestViewModel and DetailViewModel. When it is uncommented, the DefaultManager will be initialized in the TestViewModel and then a second instance will init/deinit with every push/pop of the DetailView.

I also tested this with other modifiers and the results seem to be consistent.

extension Container {
    var manager: Factory<Manager> {
        self { DefaultManager() }
            .scope(.shared)
//            .onPreview(factory: { PreviewManager() }) <--- uncomment to break shared scope
    }
}

protocol Manager {}

final class DefaultManager: Manager, ObservableObject {
    init() {
        print("init default object")
    }
    
    deinit {
        print("deinit default object")
    }
}

final class PreviewManager: Manager, ObservableObject {
    init() {
        print("init preview manager")
    }
    
    deinit {
        print("deinit preview manager")
    }
}


final class TestViewModel: ObservableObject {
    @Injected(\.manager) private var manager
}

struct TestView: View {
    @StateObject var viewModel = TestViewModel()
    
    var body: some View {
        NavigationStack {
            NavigationLink {
                DetailView()
            } label: {
                Text("Hello, World!")
            }
        }
    }
}

struct DetailView: View {
    
    @StateObject var viewModel = DetailViewModel()
    
    var body: some View {
        Text("Detail")
    }
}

final class DetailViewModel: ObservableObject {
    @Injected(\.manager) private var manager
}

There does seem to be a couple of options to get around this but I still think the above should work.

Option 1 - Autoregister:

extension Container: AutoRegistering {
    public func autoRegister() {
        #if DEBUG
        Container.shared.manager.onPreview(factory: { PreviewManager() })
        #endif
    }
}

Option 2 - once() modifier:

extension Container {
    var manager: Factory<Manager> {
        self { DefaultManager() }
            .scope(.shared)
            .onPreview(factory: { PreviewManager() })
            .once() // <--- added this
    }
}

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions