I noticed that our macOS app written in SwiftUI becomes slower and slower as the user is using it. Digging into it, I found that SwiftUI keeps allocating small blocks of memory that are never released (and I think it's causing some unnecessary work too).
I isolated a simple macOS app that demonstrates the issue. Sample app: download.
The bug seems to be linked to adding a NSViewRepresentable view in SwiftUI hierarchy and passing to it a Binding to the SwiftUI View's state.
Every time the state of SwiftUI view is modified, a small block of memory is allocated that is never released.
In the sample app, I just added a timer that updates the state every 10ms to better highlight the issue.
Am I right to believe it's a SwiftUI bug? Looking at my code, I don't think I'm doing anything illegal, do I?
struct ContentView: View {
@State private var point: CGPoint = .zero
@State private var timer = Timer.publish(every: 0.01, tolerance: nil, on: .main, in: .common).autoconnect()
var body: some View {
Color.blue.frame(width: 300, height: 300)
.overlay(Text("\(point.x)"))
.onSomeExternalAction(someValue: $point)
.onReceive(timer) { _ in
point = CGPoint(x: CGFloat(arc4random() % 100), y: 0.0)
}
}
}
//////////////////////////////////////////////////////
class _MouseTrackingView: NSView {
// Just a dummy view
}
struct MouseTrackingView: NSViewRepresentable {
typealias NSViewType = _MouseTrackingView
let someValue: Binding<CGPoint>? // Binding is retained here!
func makeNSView(context: Context) -> _MouseTrackingView {
return _MouseTrackingView()
}
func updateNSView(_ nsView: _MouseTrackingView, context: Context) {
//...
}
}
extension View {
public func onSomeExternalAction(someValue: Binding<CGPoint>?) -> some View {
self
.background(MouseTrackingView(someValue: someValue))
}
}
