SwiftUIX / Coordinator

A declarative navigation API for SwiftUI.
MIT License
252 stars 21 forks source link

Leaks related to `UIViewControllerCoordinator` #11

Open trunghvbk opened 1 month ago

trunghvbk commented 1 month ago

Hi @vmanot, In this sample code, I use UIViewControllerCoordinator to handle routing. I found that when I use it to assign .coordinator to the AView, AViewModel will never be released (The deinit func is never called). It will not happen if I use AppKitOrUIKitWindowCoordinator. But in our legacy app, we use AppKitOrUIKitWindowCoordinator so it's hard to change. Could you help to investigate the reason why it happens? I tried but have not yet found out. Thank you!

import SwiftUI
import Coordinator

@main
struct CoordinatorTestApp: App {
    var body: some Scene {
        WindowGroup {
            NavigationView {
                ContentView()
            }
        }
    }
}

enum ContentDestination {
    case aView
}

class ContentCoordinator: UIViewControllerCoordinator<ContentDestination> {
    override func transition(for route: ContentDestination) -> ViewTransition {
        switch route {
        case .aView:
            return .push(AView())
        }
    }
}

struct ContentView: View {
    @StateObject var coordinator = ContentCoordinator()
    var body: some View {
        VStack {
            Button {
                coordinator.trigger(.aView)
            } label: {
                Text("Go To AView")
            }
        }
        .padding()
        .coordinator(coordinator)
    }
}

struct AView: View {
    @StateObject var viewModel = AViewModel()
    @StateObject var coordinator = ACoordinator()

    var body: some View {
        VStack {
            Button {
                coordinator.trigger(.popBack)
            } label: {
                Text("Back")
            }
        }
        .coordinator(coordinator)
    }
}

class AViewModel: ObservableObject {
    deinit {
        // This will never run into
        debugPrint("AViewModel deinit")
    }
}

enum ADestination {
    case popBack
}

class ACoordinator: UIViewControllerCoordinator<ADestination> {
    override func transition(for route: ADestination) -> ViewTransition {
        switch route {
        case .popBack:
            return .pop
        }
    }
}
vmanot commented 1 month ago

@trunghvbk I'll investigate this, thanks!

If you're using Coordinator in your project, would you mind opening up a PR to contribute to documentation? I'm trying to encourage folks to contribute, it makes justifying and prioritizing this project a lot easier for me.

trunghvbk commented 1 week ago

Hi @vmanot What is the document that you mentioned? I see the How to Use is documented already. Sorry I have not been good at documenting, but I will try to contribute to the source code with my ability.