简体   繁体   中英

SwiftUI - ObservableObject that inherits from NSObject does not update in iOS 13

I know, this is one of those "Not working in iOS XX" questions, but I'm completely stuck...

So I have an ObservableObject class that inherits from NSObject , because I need to listen to the delegate methods of UISearchResultsUpdating .

class SearchBarListener: NSObject, UISearchResultsUpdating, ObservableObject {
    
    @Published var searchText: String = ""
    let searchController: UISearchController = UISearchController(searchResultsController: nil)
    
    override init() {
        super.init()
        self.searchController.searchResultsUpdater = self
    }
    
    func updateSearchResults(for searchController: UISearchController) {
        
        /// Publish search bar text changes
        if let searchBarText = searchController.searchBar.text {
            print("text: \(searchBarText)")
            self.searchText = searchBarText
        }
    }
}

struct ContentView: View {
    @ObservedObject var searchBar = SearchBarListener()
    
    var body: some View {
        Text("Search text: \(searchBar.searchText)")
        .padding()

        /// more code that's not related 
    }
}

The problem is that even though print("text: \(searchBarText)") prints fine, the Text("Search text: \(searchBar.searchText)") is never updated (in iOS 13). It works fine in iOS 14.


Here's a minimal reproducible example:

class SearchBarTester: NSObject, ObservableObject {

    @Published var searchText: String = ""

    override init() {
        super.init()
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
            print("updated")
            self.searchText = "Updated"
        }
    }
}

struct ContentView: View {
    @ObservedObject var searchBar = SearchBarTester()
    
    var body: some View {
        NavigationView {
            Text("Search text: \(searchBar.searchText)")
            .padding()
        }
    }
}

After 5 seconds, "updated" is printed in the console, but the Text doesn't change. In iOS 14, the Text changes to "Search text: Updated" as expected.

在控制台中打印“更新”但模拟器没有改变

However, if I don't inherit from NSObject , both iOS 13 and iOS 14 work!

class SearchBarTester: ObservableObject {

    @Published var searchText: String = ""

    init() {
        
        DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
            print("updated")
            self.searchText = "Updated"
        }
    }
}    

控制台和模拟器中打印的“更新”也发生了变化

I think the problem has something to do with inheriting from a class. Maybe it's something that was fixed in iOS 14. But does anyone know what is going on?


Edit

Thanks @Cuneyt for the answer: Here's the code that finally worked:

import SwiftUI
import Combine /// make sure to import this

class SearchBarTester: NSObject, ObservableObject {

    let objectWillChange = PassthroughSubject<Void, Never>()
    @Published var searchText: String = "" {
        willSet {
            self.objectWillChange.send()
        }
    }

    override init() {
        super.init()

        DispatchQueue.main.asyncAfter(deadline: .now() + 5) {
            print("updated")
            self.searchText = "Updated"
        }
    }
}

It appears to be on iOS 13, if you subclass an object and do not conform to ObservableObject directly (as in class SearchBarTester: ObservableObject ), you'll need to add this boilerplate code:

@Published var searchText: String = "" {
    willSet {
        objectWillChange.send()
    }
}

However, calling the default objectWillChange will still not work, hence you'll need to define it yourself again:

let objectWillChange = PassthroughSubject<Void, Never>()

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM