Replies: 3 comments 10 replies
-
The example project has an example in plain SwiftUI with just ObservedObject models, where the issue is not present. The main difference seems to be this:
.sheet(
unwrapping: $model.destination,
case: /ContentModel.Destination.flow
) { $model in
FlowView(model: model)
}
.sheet(item: $model.presented) { model in
FlowView(model: model)
} If using a binding to the model, there seems to be a retain cycle, but only in the case where the TextField gains focus. |
Beta Was this translation helpful? Give feedback.
-
Hi @cabeca, we dug into this a bit today, and there's a bit of a conundrum. First of all, if you simply present and dismiss right away, you will see that only the first model never deinits. If you present/dismiss again, that model will be deallocated correctly. Second, somehow focusing a text field changes this behavior. If you present/focus/dismiss, then the model is never deallocated. These problems seem to stem from our So there doesn't seem to be a good option here. We can keep |
Beta Was this translation helpful? Give feedback.
-
I'm sorry to get back at you again, but I have another example that I cannot quite explain. I used your streamlined example and modified it slightly to include an enum Destination, as that is my most used pattern. I then used plain SwiftUI
I cannot explain this behavior. Could this be a problem with the import SwiftUI
import SwiftUINavigation
@main
struct TextFieldRetainCycleApp: App {
var body: some Scene {
WindowGroup {
ContentView()
}
}
}
@MainActor
public final class ContentModel: ObservableObject, Identifiable {
enum Destination: Identifiable {
var id: ObjectIdentifier {
switch self {
case .flow(let model): model.id
}
}
case flow(FlowModel)
}
@Published var destination: Destination? {
didSet {
print("🤯 destination \(String(describing: destination))")
}
}
func presentButtonTapped() {
destination = .flow(FlowModel())
}
var flowModel: FlowModel? {
get { (/ContentModel.Destination.flow).extract(from: destination) }
set { destination = newValue.map { (/ContentModel.Destination.flow).embed($0) } }
}
}
struct ContentView: View {
@StateObject var model = ContentModel()
var body: some View {
Button("Present") {
model.presentButtonTapped()
}
// // This one leaks
// .sheet(item: $model.destination.case(/ContentModel.Destination.flow)) { model in
// FlowView(model: model)
// }
// // This one does not leak
// .sheet(item: $model.flowModel) { model in
// FlowView(model: model)
// }
// // This one does not leak
// .sheet(item: $model.destination) { destination in
// switch destination {
// case .flow(let model):
// FlowView(model: model)
// }
// }
// // This one does not leak
// .sheet(item: flowBinding) { model in
// FlowView(model: model)
// }
}
var flowBinding: Binding<FlowModel?> {
Binding(
get: {
switch model.destination {
case .flow(let model): model
default: nil
}
},
set: { incoming in
if let incoming {
model.destination = .flow(incoming)
} else {
model.destination = nil
}
}
)
}
}
@MainActor
public final class FlowModel: ObservableObject, Identifiable {
@Published var idInput: String = ""
init() {
print("🤯 INIT FlowModel \(id)")
}
deinit {
print("🤯 DEINIT FlowModel \(id)")
}
}
struct FlowView: View {
@ObservedObject var model: FlowModel
var body: some View {
TextField("Text", text: $model.idInput)
}
} |
Beta Was this translation helpful? Give feedback.
-
I think I’ve found a retain cycle problem in the
SwiftUINavigation
library. Find a sample project below.I'm still using version 1.0.3 of the library (pre macros requirement) and using the
.sheet(unwrapping:case:)
because of it. The CasePathable.sheet(item:)
works fine. Any suggestions other than reverting to plain swiftUI for sheet presentation?Beta Was this translation helpful? Give feedback.
All reactions