LAST CHANCE: Save 50% on all my Swift books and bundles! >>

SOLVED: Any suggestions to get around the memory leak caused by multiple presentation detents on Sheet?

Forums > SwiftUI

As per the title.

I reported a bug to Apple and mentioned it on the Apple forums some months ago here: https://developer.apple.com/forums/thread/729197

Essentially, using the sample code below, if you observe the printouts, the class is not deallocated if multiple presentationDetents are present.

import SwiftUI

struct ContentView: View {
    @State var showSheet = false

    var body: some View {
        Button("Show Sheet") {
            showSheet = true
        }
        .sheet(isPresented: $showSheet) {
            DetailView()
            // With a single detent, the below class prints "this is denit".
                .presentationDetents([.medium])

            // When multiple detents are specified the detail view appears to not deinit properly.
//                .presentationDetents([.medium, .large])
        }
    }
}

struct DetailView: View {
    let deinitPrinter = DeinitPrinter()
    var body: some View {
        Text("foobar")
    }
}

final class DeinitPrinter {
    init() { print("this is init") }
    deinit { print("this is deinit") }
}

struct ContentView_Previews: PreviewProvider {
    static var previews: some View {
        ContentView()
    }
}

In an actual use case, imagine the deinit printer is a ViewModel that would now not be deinitalised correctly.

I've raised this with Apple, FB12213332 but have since heard nothing back, I was wondering surely... I can't be the only one that's noticed this. Wondering if I'm having a brainfart and I'm actually missing something here...

Any help or suggestions would be appreciated!

2      

I'm facing with the same issue. Do you have any solution?

2      

Try to put object in .task modifier like so

struct ContentView: View {
    @State var showSheet = false

    var body: some View {
        Button("Show Sheet") {
            showSheet = true
        }
        .sheet(isPresented: $showSheet) {
            DetailView()
            // With a single detent, the below class prints "this is denit".
                .presentationDetents([.medium, .large])

            // When multiple detents are specified the detail view appears to not deinit properly.
            //                .presentationDetents([.medium, .large])
        }
    }
}

struct DetailView: View {
    @State var deinitPrinter: DeinitPrinter?

    var body: some View {
        Text("foobar")
            .task {
                deinitPrinter = DeinitPrinter()
            }
    }
}

final class DeinitPrinter {
    init() { print("this is init") }
    deinit { print("this is deinit") }
}

2      

Hacking with Swift is sponsored by Essential Developer.

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until July 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

In my case I init environment object for DetailView (which is ViewModel) in ContentView and then pass this VM via .environmentObject(vm) to the DetailView.

2      

Then i have a question, why do you need to deinit it? You create some object upper in hierarchy or in environment then reference to this object in subview or detail view, do something with it, then close detail view and you want that object to deinit???? Then you lose any reference to that object at all. In any case as soon there will be no reference to that object anywhere in the app, ARC will count as zero reference and will destroy that object... As far as I understand that all...

2      

This is the point! .presentationDetents([.medium, .large]) creates strong reference to my DetailVM and when I dismiss DetailView and set reference to VM in the ContentView to nill - I got memory leak.

2      

I just reproduced this bug in the test project. https://github.com/schornon/PresentationDetentsLeak

2      

I think in this case you need to pass vm object to enironment like so:

.environmentObject(vm)

2      

Well, then ContentViewModel will be available in DetailsView and that is a little strange because DetailsView has the own VM.

2      

If you find any other solution, post for others as well. Seems like a bug in SwiftUI, but I couldn't come up with another solution for this matter...

3      

Hey @Kaiman,

I've run into this issue before, and it can indeed be frustrating. It's great that you've reported it to Apple. One workaround I've found is to use a custom presentation transition instead of relying on the built-in presentationDetents. It's a bit more work, but it can help with memory management. Hope this helps!

Best regards,

2      

Hi all, thanks for the suggestions and help. Happy to report that Apple got back to me on the bug report and this is no longer an issue in iOS 17.4.1! - 🎊

It could have been fixed before in a previous version of iOS 17.x - but I haven't had a chance to check those.

1      

Hacking with Swift is sponsored by Essential Developer.

SPONSORED Join a FREE crash course for mid/senior iOS devs who want to achieve an expert level of technical and practical skills – it’s the fast track to being a complete senior developer! Hurry up because it'll be available only until July 28th.

Click to save your free spot now

Sponsor Hacking with Swift and reach the world's largest Swift community!

Reply to this topic…

You need to create an account or log in to reply.

All interactions here are governed by our code of conduct.

 
Unknown user

You are not logged in

Log in or create account
 

Link copied to your pasteboard.