UIViewControllerRepresentable: Memory Leak

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.

Instruments Memory Leak

Leave a Comment

By continuing to use the site, you agree to the use of cookies. more information

The cookie settings on this website are set to "allow cookies" to give you the best browsing experience possible. If you continue to use this website without changing your cookie settings or you click "Accept" below then you are consenting to this.

Close