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.
Uh oh!
There was an error while loading. Please reload this page.
Uh oh!
There was an error while loading. Please reload this page.
-
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