The Solutions:
Solution 1: Using Optional Properties and Resetting Them on Disappearance
In iOS 17, SwiftUI views can sometimes leak memory due to a bug. One way to work around this issue is to use optional properties and reset them when the view disappears. This ensures that the objects referenced by the optional properties are deallocated when the view is dismissed.
To implement this solution, follow these steps:
- Declare the property as an optional.
- In the view’s
onDisappear
method, set the optional property tonil
. - Optionally, you can wrap the optional property in a property wrapper to simplify the code.
Here’s an example:
// Property wrapper to simplify the code
@propertyWrapper struct Resettable<Value> {
private var value: Value?
var wrappedValue: Value? {
get { value }
set { value = newValue }
}
init(wrappedValue: Value?) {
self.value = wrappedValue
}
func reset() {
value = nil
}
}
struct MyView: View {
// Declare the property as an optional
@Resettable var reference: Service?
var body: some View {
// Use the property as usual
Text("Hello, world!")
}
func onDisappear() {
// Reset the optional property when the view disappears
reference = nil
}
}
By following this approach, you can work around the memory leak issue caused by SwiftUI view leaks in iOS 17.
Solution 2: Stop creating objects directly in the view
Most already know that `@ObservedObject` is a leak because Apple said so in a WWDC video, e.g. this has a bad leak:
struct BadLeakView: View {
@ObserverdObject var log = LogDeinit() // bad leak
}
However, your example is a worse leak because not only did you init an object in a View struct (SwiftUI keeps many copies of `View` structs so it can diff them) but you didn’t even wrap it in any property wrapper, this is a very bad leak:
struct VeryBadLeakView: View {
let log = LogDeinit() // very bad leak!
}
You can fix this with `@StateObject`, however, you should question if you even need an object, usually, it’s only when you need to do something asynchronous, like a Combine pipeline. Since we now have async/await usually you don’t even need a `@StateObject` anymore since we have `.task` which is like a state object but as a simpler view modifier.
Q&A
What is the workaround for fixing the leak in SwiftUI views?
Use an optional property and reset it in onDisappear
.
How can the leak in @ObservedObject
be fixed?
Use @StateObject
instead.
When should @StateObject
be used?
Only when asynchronous operations are needed, such as a Combine pipeline.
Video Explanation:
The following video, titled "4. Create a To-Do App Part 1 - YouTube", provides additional insights and in-depth exploration related to the topics discussed in this post.
Code all the views and components for a fully functional To-Do app Watch the complete Build Quick Apps with SwiftUI course ...
The following video, titled "4. Create a To-Do App Part 1 - YouTube", provides additional insights and in-depth exploration related to the topics discussed in this post.
Code all the views and components for a fully functional To-Do app Watch the complete Build Quick Apps with SwiftUI course ...