Observable does not conform with Equatable (and Hashable)

Published by malhal on

I noticed a snag when using NavigationLink(value:label:) and .navigationDestination(for:destination:) with the new @Observable vs the old Combine ObservableObject (which “just worked™️”). There are 2 compilation errors:

Initializer 'init(value:label:)' requires that 'ObservableContent' conform to 'Hashable'
Instance method 'navigationDestination(for:destination:)' requires that 'ObservableContent' conform to 'Hashable'

Fortunately it is relatively simple to add Hashable conformance to an @Observable class by implementing a static func == and a func hash that uses ObjectIdentifier as follows:

import SwiftUI

@Observable class ObservableContent: Hashable {
    var text1 = "Default"
    var text2 = ""
    
    func hash(into hasher: inout Hasher) {
        hasher.combine(ObjectIdentifier(self))
    }

    static func == (lhs: ObservableContent, rhs: ObservableContent) -> Bool {
        lhs === rhs
    }
}

struct ContentView: View {
    
    @State var observableContent: ObservableContent?
    
    var body: some View {
        Group {
            if let observableContent {
                NavigationStack {
                    NavigationLink(value: observableContent) {
                        Text("Navigation Link")
                    }
                    .navigationDestination(for: ObservableContent.self) { content in
                        ObservableContentView(content: content)
                    }
                }
            }
        }
        .onAppear {
            if observableContent == nil {
                observableContent = ObservableContent()
            }
        }
        .onDisappear {
            observableContent = nil
        }
    }
}

struct ObservableContentView: View {
    @Bindable var content: ObservableContent
    
    var body: some View {
        Form {
            TextField("Text1", text: $content.text1)
            Text(content.text1)
        }
    }
}

I also posted my solution to the Apple Developer solutions to help someone who faced the same problem: https://developer.apple.com/forums/thread/734848?answerId=786038022#786038022

Categories: SwiftUI