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?
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"
}
}
}
The group of methods that are fundamental to all Objective-C objects. This protocol is imported into Swift with the name NSObjectProtocol. An object that conforms to this protocol can be considered a first-class object. Such an object can be asked about its: Class, and the place of its class in the inheritance hierarchy. Conformance to protocols.
SwiftUI ObservableObject Tutorial September 11, 2019 ObservableObject is a protocol that’s part of the Combine framework. It is used within a custom class/model to keep track of the state.
Enter SwiftUIObservableObjectTutorial as the Product Name, select the Use SwiftUI checkbox, and click Next. Choose a location to save the project on your Mac. In the canvas, click Resume to display the preview. If the canvas isn’t visible, select Editor > Editor and Canvas to show it. Add a new file to the project. Select File -> New -> File.
In the template selector, select iOS as the platform, select the Single View App template, and then click Next. Enter SwiftUIObservableObjectTutorial as the Product Name, select the Use SwiftUI checkbox, and click Next. Choose a location to save the project on your Mac. In the canvas, click Resume to display the preview.
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>()
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With