nalexn / clean-architecture-swiftui

SwiftUI sample app using Clean Architecture. Examples of working with CoreData persistence, networking, dependency injection, unit testing, and more.
MIT License
5.57k stars 671 forks source link

Another Question about Navigation/Routing #13

Closed Patrick3131 closed 4 years ago

Patrick3131 commented 4 years ago

Hey Alex, thanks a lot for your example application.

I am creating another sample app based on the provided architecture of your example to get a better understanding of SwiftUI and Combine.

I have another problem with the navigation/routing. I basically copied your approach but I have these weird back and forth navigation behaviour, maybe you could take a look at my code. I have been going through the Stackoverflow topics, but I wasn't able to fix it and at the same time keep your routing example.

struct CategoriesList: View {
    @Environment(\.locale) private var locale: Locale
    @Environment(\.injected) private var injected: DIContainer
    @State private var routingState: Routing = .init()
    private var routingBinding: Binding<Routing> {
        print(routingState)
        return $routingState.dispatched(to: injected.appState, \.routing.categories)
    }
    private var categories = Category.allCases
    var body: some View {
        content
            .onReceive(routingUpdate) { value in
                print(value)
                self.routingState = value
        }
    }

    private var content: some View {
        NavigationView {
            List(self.categories) { category in
                NavigationLink(
                    destination: Exercises(),
                    tag: category.rawValue,
                    selection: self.routingBinding.categories) {
                        CategorieCell(name: category.rawValue)
                }
            }.navigationBarTitle("Categories")
        }

    }
}

// MARK: - State Updates
private extension CategoriesList {
    var routingUpdate: AnyPublisher<Routing, Never> {
        injected.appState.updates(for: \.routing.categories)
    }
}

extension CategoriesList {
    struct Routing: Equatable {
        var categories: String?
    }
}

struct ExerciseDetail: View {
    var body: some View {
        Text("Hello DetailsView")
    }
}

Link to the file: https://github.com/Patrick3131/LearnHockey/blob/dev/LearnHockey/UI/Screens/CategoriesList.swift

Thanks!

nalexn commented 4 years ago

Hi @Patrick3131

So I could find two issues in your code. The main one that was causing the weird back and forth navigation was this code:

extension Category: Identifiable {
    var id: UUID { UUID()}
}

You are returning a random UUID each time this computed property is called, which totally confuses SwiftUI's list.

Here is a correct revision:

extension Category: Identifiable {
    var id: String { self.rawValue }
}

Another issue is that you didn't inject the DIContainer in the hierarchy, and the default value was used instead. You should do this:

var body: some View {
     CategoriesList().inject(container)
}
Patrick3131 commented 4 years ago

Thanks a lot, it works!