Weird Swift TextField glitch that might not have a solution?

Viewed 341

so I have a fairly large file that defines the view of a search bar. I just spent the last two hours removing all of the excess/unnecessary code from the file. The error I'm having is that when I type pretty fast into the search bar, not every key that is pressed is registered, so it ends up coming out as some garbled mess. It seems like the more ObservedObjects, State variables, Binding variables, and just normal variables and code I remove, the quicker the better the text field works.

The glitch I'm having can be seen in this link: https://youtu.be/42sjhDxSKBw

For reference, what I typed in was "Hello stack overflow this is a test for typing fast"...if I type it in slower, it all appears.

In the example below, I removed all the variables so it runs pretty smoothly. Does anyone have any experience with SwiftUI TextFields demonstrating this odd behavior of not registering every key when there is a lot going on? The view for the text field (in it's simplest most broken down form, without all the different variables and stuff, is the following):

import SwiftUI
import Mapbox
import MapboxGeocoder

struct SearchBar: View {



var VModel : ViewModel
@Binding var searchedText: String


var body: some View {
    
    let binding = Binding<String>(get: {
        self.searchedText
    }, set: {
        self.searchText = $0
    self.searchedText = self.searchText
    self.VModel.findResults(address: self.searchedText)
    if self.VModel.searchResults.count >= 0 {
        self.showResults = true
        self.showMoreDetails = false
    } else {
        self.showResults = false
    }
    }
    )
    
    
    return VStack {
        HStack {
            TextField("Search", text: binding, onEditingChanged: { isEditing in
                print("we are not editing the text field")
            }, onCommit: {
                print("pressed enter")
                if self.VModel.searchResults.first != nil {
                                self.annotation.addNextAnnotation(address: self.rowText(result: self.VModel.searchResults.first!).label)
                                self.searchedText = "\(self.rowText(result: self.VModel.searchResults.first!).label)"
                            }
            })
        }
        .foregroundColor(Color(.white))
        .background(Color.gray)
    }
}
}

The ViewModel class looks like:

import SwiftUI
import CoreLocation
import Mapbox
import MapboxGeocoder

class ViewModel: ObservableObject {

@ObservedObject var locationManager = LocationManager()
@Published var lat: Double?
@Published var lon: Double?
@Published var location: CLLocationCoordinate2D?
@Published var name: CLPlacemark?
@Published var searchResults: [GeocodedPlacemark] = []

func findResults(address: String) {
    let geocoder = Geocoder(accessToken: "pk.eyJ1Ijoibmlja2JyaW5zbWFkZSIsImEiOiJjazh4Y2dzcW4wbnJyM2ZtY2V1d20yOW4wIn0.LY1H3cf7Uz4BhAUz6JmMww")
    let foptions = ForwardGeocodeOptions(query: address)
    foptions.maximumResultCount = 10
    geocoder.geocode(foptions) { (placemarks, attribution ,error) in
        guard let placemarks = placemarks else {
            return
        }
        self.searchResults = []
        for placemark in placemarks {
            self.searchResults.append(placemark)
        }
    }
}
}

In a function used to display the search results, I have the following code block that uses searchResults:

ForEach(self.VModel.searchResults, id: \.self) { result in
                Button(action: {
                    self.annotation.addNextAnnotation(address: self.rowText(result: result).label)
                    self.showResults = false
                    self.searchedText = self.rowText(result: result).label
                }, label: {
                    self.rowText(result: result).view.font(.system(size: 13))
                    
        }).listRowBackground(Color.gray)
}
1 Answers

Try like the following (not tested as env cannot be replicated)

import Combine

class ViewModel: ObservableObject {

    @ObservedObject var locationManager = LocationManager()
    @Published var lat: Double?
    @Published var lon: Double?
    @Published var location: CLLocationCoordinate2D?
    @Published var name: CLPlacemark?
    @Published var searchResults: [GeocodedPlacemark] = []

    private let searchValue = CurrentValueSubject<String, Never>("")
    private var cancellable: AnyCancellable?

    func findResults(address: String) {
        if nil == cancellable {
            cancellable = self.searchValue
                .debounce(for: 0.5, scheduler: DispatchQueue.main)
                .flatMap { newValue in
                    Future<[GeocodedPlacemark], Never> { promise in
                        let geocoder = Geocoder(accessToken: "pk.eyJ1Ijoibmlja2JyaW5zbWFkZSIsImEiOiJjazh4Y2dzcW4wbnJyM2ZtY2V1d20yOW4wIn0.LY1H3cf7Uz4BhAUz6JmMww")
                        let foptions = ForwardGeocodeOptions(query: address)
                        foptions.maximumResultCount = 10
                        geocoder.geocode(foptions) { (placemarks, attribution ,error) in
                            guard let placemarks = placemarks else {
                                return
                            }
                            promise(.success(placemarks))
                        }
                    }
                }
                .receive(on: DispatchQueue.main)
                .sink(receiveValue: { placemarks in
                    self.searchResults = placemarks
                })
        }
        self.searchValue.send(address)
    }
}
Related