Commit e6583de2 by Demid Merzlyakov

Search implementation and a bit of refactoring.

parent d761faf9
...@@ -94,7 +94,7 @@ private extension SavedCitiesViewController { ...@@ -94,7 +94,7 @@ private extension SavedCitiesViewController {
func prepareSearchBar() { func prepareSearchBar() {
searchBar.searchBarStyle = .minimal searchBar.searchBarStyle = .minimal
searchBar.placeholder = "Search" searchBar.placeholder = "Search".localized(comment: "Search bar placeholder text.")
view.addSubview(searchBar) view.addSubview(searchBar)
searchBar.snp.makeConstraints { (make) in searchBar.snp.makeConstraints { (make) in
make.top.equalToSuperview() make.top.equalToSuperview()
...@@ -143,12 +143,12 @@ private extension SavedCitiesViewController { ...@@ -143,12 +143,12 @@ private extension SavedCitiesViewController {
//MARK: UItableView Data Source //MARK: UItableView Data Source
extension SavedCitiesViewController: UITableViewDataSource { extension SavedCitiesViewController: UITableViewDataSource {
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return locationsViewModel.savedCities.count return locationsViewModel.cities.count
} }
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: CityCell.kIdentifier, for: indexPath) as! CityCell let cell = tableView.dequeueReusableCell(withIdentifier: CityCell.kIdentifier, for: indexPath) as! CityCell
cell.configure(geoName: locationsViewModel.savedCities[indexPath.row], cell.configure(geoName: locationsViewModel.cities[indexPath.row],
mode: locationsViewModel.displayMode) mode: locationsViewModel.displayMode)
return cell return cell
} }
...@@ -161,7 +161,7 @@ extension SavedCitiesViewController: UITableViewDelegate { ...@@ -161,7 +161,7 @@ extension SavedCitiesViewController: UITableViewDelegate {
container.backgroundColor = ThemeManager.currentTheme.navigationBarBackgroundColor container.backgroundColor = ThemeManager.currentTheme.navigationBarBackgroundColor
let titleLabel = UILabel() let titleLabel = UILabel()
titleLabel.text = "Saved Cities (\(self.locationsViewModel.savedCities.count))" titleLabel.text = "Saved Cities".localized + "( \(self.locationsViewModel.cities.count))"
titleLabel.font = AppFont.SFPro.bold(size: 24) titleLabel.font = AppFont.SFPro.bold(size: 24)
titleLabel.textColor = ThemeManager.currentTheme.primaryTextColor titleLabel.textColor = ThemeManager.currentTheme.primaryTextColor
container.addSubview(titleLabel) container.addSubview(titleLabel)
......
...@@ -6,6 +6,7 @@ ...@@ -6,6 +6,7 @@
// //
import UIKit import UIKit
import AlgoliaSearchClient
protocol LocationsViewModelDelegate:class { protocol LocationsViewModelDelegate:class {
func viewModelDidChange(model:LocationsViewModel) func viewModelDidChange(model:LocationsViewModel)
...@@ -22,7 +23,12 @@ enum LocationsViewModelDisplayMode { ...@@ -22,7 +23,12 @@ enum LocationsViewModelDisplayMode {
class LocationsViewModel { class LocationsViewModel {
//Public //Public
weak var delegate:LocationsViewModelDelegate? weak var delegate:LocationsViewModelDelegate?
private(set) var savedCities = [GeoNamesPlace]() private(set) var cities = [GeoNamesPlace]() {
didSet {
assert(Thread.isMainThread)
self.delegate?.viewModelDidChange(model: self)
}
}
private(set) var displayMode:LocationsViewModelDisplayMode = .savedCities private(set) var displayMode:LocationsViewModelDisplayMode = .savedCities
init() { init() {
...@@ -49,28 +55,93 @@ class LocationsViewModel { ...@@ -49,28 +55,93 @@ class LocationsViewModel {
kazan.city = "Kazan" kazan.city = "Kazan"
kazan.stateCode = "000000" kazan.stateCode = "000000"
kazan.country = "Russia" kazan.country = "Russia"
savedCities = [perm, moscow, sp, kazan] cities = [perm, moscow, sp, kazan]
} }
func fetchCities(query:String) { func fetchCities(query:String) {
//Some fetching method
//Tell delegate to refresh UI // This is a test location that can be interpreted on the server to return all alerts
self.delegate?.viewModelDidChange(model: self) guard query != "1wville" else {
let fakePlace = GeoNamesPlace()
fakePlace.latitude = "67.25639"
fakePlace.longitude = "-150.18417"
fakePlace.city = "1WVille"
fakePlace.state = "Alaska"
fakePlace.stateCode = "AK"
fakePlace.country = "United States"
fakePlace.countryCode = "US"
fakePlace.fcodeName = "populated place"
fakePlace.toponymName = "1WVille"
fakePlace.isMyLocation = false
DispatchQueue.main.async {
self.cities = []
}
return
}
Logger.minSeverityLevel = .warning
var algoliaQuery = PlacesQuery(query)
algoliaQuery.aroundLatLngViaIP = true
algoliaQuery.type = .city
let placesClient: PlacesClient = PlacesClient(appID: ApplicationID (rawValue: kAlgoliaAppId),
apiKey: APIKey(rawValue: kAlgoliaAPIKey))
let language: Language = Language(rawValue: NSLocale.preferredLanguages.first?.components(separatedBy: "-").first ?? "en")
print("Search: using language \(language.rawValue)")
var filteredPlaces = [GeoNamesPlace]()
placesClient.search(query: algoliaQuery, language: language, requestOptions: nil) { (result: Result<PlacesClient.SingleLanguageResponse, Error>) in
switch (result) {
case .success(let response):
print("Search: got \(response.nbHits) results")
for hit: Hit<Place> in response.hits {
let place = GeoNamesPlace()
if hit.object.localeNames?.first == nil {
print("Search: skip 1 object.");
continue
}
place.city = hit.object.localeNames?.first
place.state = hit.object.administrative?.first
place.country = hit.object.country ?? "" // ?? "" part is from the Android code, may be not needed on iOS
place.countryCode = hit.object.countryCode?.rawValue.uppercased() ?? "" // ?? "" part is from the Android code, may be not needed on iOS
if let geolocation = hit.geolocation {
place.latitude = "\(geolocation.latitude)"
place.longitude = "\(geolocation.longitude)"
}
else {
place.latitude = ""
place.longitude = ""
}
filteredPlaces.append(place)
}
DispatchQueue.main.async {
self.cities = filteredPlaces
}
break
case .failure(let error):
DispatchQueue.main.async {
self.delegate?.viewModel(model: self, errorHasOccured: error)
}
break
}
}
} }
func add(city:GeoNamesPlace) { func add(city:GeoNamesPlace) {
//Tell delegate to refresh UI // TODO: Just update self.cities, it will trigger a viewModelDidChange call
self.delegate?.viewModelDidChange(model: self)
} }
func delete(city:GeoNamesPlace) { func delete(city:GeoNamesPlace) {
//Tell delegate to refresh UI // TODO: Just update self.cities, it will trigger a viewModelDidChange call
self.delegate?.viewModelDidChange(model: self)
} }
func select(city:GeoNamesPlace) { func select(city:GeoNamesPlace) {
//Tell delegate to refresh UI // TODO: Tell delegate to refresh UI
self.delegate?.viewModelDidChange(model: self) self.delegate?.viewModelDidChange(model: self)
} }
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment