Fixed on Xcode 11, Beta 5
There is a problem with UIViewControllerRepresentable, where the UIViewControllers they create are never released. In the code below, I have created a minimal example of the problem. By tapping repeatedly on a button, a UIViewController gets created. However, when destroyed, the deinit method is never called. Not even after a simulated Memory Warning.
Bug Report: FB6190972
Workaround: Unknown
Last Version With Bug: Xcode 11, Beta 4.
Stackoverflow Question: https://stackoverflow.com/questions/56699009/swiftui-memory-leak-when-using-uiviewcontrollerrepresentable
Sample Code
This is the code to reproduce the problem:
import SwiftUI
struct ContentView : View {
@State private var showRepView = true
var body: some View {
VStack {
Button(action: {
self.showRepView.toggle()
}, label: { Text("Show Square").font(.title) })
if showRepView {
RepViewController().frame(width: 100, height: 100)
}
}
}
}
struct RepViewController: UIViewControllerRepresentable
{
func makeUIViewController(context: Context) -> SomeCustomeUIViewController {
let vc = SomeCustomeUIViewController()
print("\nmakeUIViewController \(vc)")
return vc
}
func updateUIViewController(_ uiViewController: SomeCustomeUIViewController, context: Context) {
print("updateUIViewController \(uiViewController)")
}
static func dismantleUIViewController(_ uiViewController: SomeCustomeUIViewController, coordinator: Self.Coordinator) {
print("dismantleUIViewController \(uiViewController)")
}
}
class SomeCustomeUIViewController: UIViewController {
override func viewDidLoad() {
super.viewDidLoad()
view.backgroundColor = UIColor.green
print("viewDidLoad \(self)")
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
print("viewWillDissapear \(self)")
}
deinit {
print("DEINIT \(self)")
}
}
Debug Output
This is the output of the example. It shows that all phases work fine. Even the dismantleUIViewController and viewWillDissapear are called. Unfortunatey deinit is not. Using Instruments we can confirm that the UIViewControllers sticks around, and never go away 🙁
makeUIViewController <Representable.SomeCustomeUIViewController: 0x7fe90670e520>
viewDidLoad <Representable.SomeCustomeUIViewController: 0x7fe90670e520>
updateUIViewController <Representable.SomeCustomeUIViewController: 0x7fe90670e520>
updateUIViewController <Representable.SomeCustomeUIViewController: 0x7fe90670e520>
dismantleUIViewController <Representable.SomeCustomeUIViewController: 0x7fe90670e520>
viewWillDissapear <Representable.SomeCustomeUIViewController: 0x7fe90670e520>
makeUIViewController <Representable.SomeCustomeUIViewController: 0x7fe906603570>
viewDidLoad <Representable.SomeCustomeUIViewController: 0x7fe906603570>
updateUIViewController <Representable.SomeCustomeUIViewController: 0x7fe906603570>
dismantleUIViewController <Representable.SomeCustomeUIViewController: 0x7fe906603570>
viewWillDissapear <Representable.SomeCustomeUIViewController: 0x7fe906603570>
makeUIViewController <Representable.SomeCustomeUIViewController: 0x7fe90660cea0>
viewDidLoad <Representable.SomeCustomeUIViewController: 0x7fe90660cea0>
updateUIViewController <Representable.SomeCustomeUIViewController: 0x7fe90660cea0>
dismantleUIViewController <Representable.SomeCustomeUIViewController: 0x7fe90660cea0>
viewWillDissapear <Representable.SomeCustomeUIViewController: 0x7fe90660cea0>
Xcode Instruments
And here is a screenshot of Instruments, where we can see that indeed, the UIViewController persists indefinitely.
