I'm trying to apply some MVVM principles to my SwiftUI app. I'm having trouble though with creating a search screen for finding locations on a map within the MVVM model. I created a Model, SearchState, which is a struct that has properties for currentLocation (my own child struct), currentText (String), and for currently suggested location ( [MKLocalSearchCompletion] ) and some other functions as well. The Model is then instantiated and published to the relevant Views by a ViewModel class.
The issues I'm having is with providing the suggestedLocations : [MKLocalSearchCompletion] to the View. The thing is the MKLocalSearchCompleter doesn't really work with SwiftUI, it works with UIKit - which I don't fully understand but I still managed to create a class that works:
import MapKit
class AutocompleteLocations: NSObject, MKLocalSearchCompleterDelegate {
var completer: MKLocalSearchCompleter
var suggestions: [MKLocalSearchCompletion]
init(suggestions: inout [MKLocalSearchCompletion]) {
self.completer = MKLocalSearchCompleter()
self.suggestions = suggestions
super.init()
self.completer.delegate = self
}
// MKLocalSearchCompleterDelegate method
func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
suggestions = completer.results
}
func updateQuery(query: String)
{
self.completer.queryFragment = query
}
func completer(_ completer: MKLocalSearchCompleter, didFailWithError error: Error) {
// Handle error
}
}
Now the question I have is how to ensure changes in AutocompleteLocations.suggestions are published to the View by the ViewModel?
Here are some things I tried:
- Instantiate AutocompleteLocations in my SearchState struct as locationSuggestions and then use locationSuggestions.suggestions in my Views?
This doesn't work obviously because changes within a nested object don't trigger the objectWillChange.send() in SwiftUI.
- Trigger objectWillChange.send() myself in the ViewModel when relevant.
This doesn't work well because the locations could be returned at any point from the internet and so you don't know when to trigger objectWillChange.send(), from within the ViewModel.
- Modify AutocompleteLocations to take an array from SearchState, so it can append results into that array that instead of its own array.
It seems because of the fact that arrays are value types in Swift, you can't do this. i played around with it but it doesn't seem to work
- Decouple AutocompleteLocations from SearchState, make AutocompleteLocations an ObservableObject, published the results of AutocompleteLocations.suggestions to the ViewModel who will subscribe and then publish these results from its own field.
This works, but I just feel like this goes against MVVM and means I can't have suggestions in my SearchState Model which is where I want them.
I feel like I might be missing something fundamental here, so looking forward to input.