
Il y a quelques années, je travaillais sur un projet qui était censé avoir des fonctionnalités similaires avec Snapchat et en même temps être une application de type Instagram étroitement thématique. Le projet a été développé uniquement pour une plate-forme - iOS. Naturellement, lors du développement de la fonctionnalité principale - la publication de photos, le client a soudainement voulu ajouter la possibilité de marquer l'endroit où la photo a été prise. Dans la plupart des cas, beaucoup se souviennent immédiatement de l'API Places de Google et Facebook, mais le client n'était pas satisfait du fait que ces solutions avaient certaines limites. Par conséquent, après un examen supplémentaire, une autre alternative d'Apple a été trouvée -
CLPlacemark , qui était gratuite, et dans la documentation à l'époque, il n'y avait aucune mention de limites pour une utilisation quotidienne. Étant donné que le développement pour d'autres plates-formes n'était pas prévu initialement, cela semblait être une option très appropriée.
La documentation d'Apple montre que CLPlacemark peut fournir beaucoup de détails sur le point, et
CLGeocoder a également une
méthode qui vous permet de retourner facilement le tableau CLPlacemark avec les données requises par nom d'emplacement. En fin de compte, tout ne fonctionne pas si bien.
Le code source ressemblait à ceci:
import CoreLocation let geocoder = CLGeocoder() func findPlace(name: String) { geocoder.geocodeAddressString(name) { placemarks, error in print(placemarks) } } findPlace(name: “New”)
Dans ce scénario simple, le géocodeur renvoie toujours un tableau CLPlacemark, mais le problème est que ce tableau ne contient jamais plus d'un élément. Par conséquent, sur tout l'écran, où une grande liste de placements était attendue comme: New York, New Zeland, New Balance Store, etc., je n'ai reçu qu'un élément qui n'était pas toujours pertinent pour ce que j'avais entré.
Après une lutte infructueuse avec CLGeocoder, mon collègue m'a dit: "Et vous n'avez pas pensé à chercher, peut-être que MapKit a une opportunité similaire?" Il s'est avéré que MapKit a
MKLocalSearch , où nous pouvons obtenir le tableau
MKPlacemark , hérité de CLPlacemark. Le circuit semblait assez fonctionnel, j'ai donc commencé à essayer cette approche:
import MapKit let request = MKLocalSearchRequest() var localSearch: MKLocalSearch? func findPlace(name: String) { request.naturalLanguageQuery = text localSearch = MKLocalSearch(request: request) localSearch?.start { (searchResponse, _) in guard let items = searchResponse?.mapItems else { return } print(items) } } findPlace(name: “New)
Dans ce cas, j'ai reçu un tableau avec 10 éléments CLPlacemark en réponse. Ce résultat semblait plus acceptable, car en conséquence, une liste suffisante a été fournie. Mais loin d'être toujours, lorsque vous commencez à entrer le nom de l'une des institutions situées à proximité, cela a immédiatement montré le résultat souhaité. Par exemple, Domino's Pizza est à côté de moi. J'aimerais que lorsque j'entre une telle requête dans une ligne, je devrais d'abord obtenir des établissements aussi près de moi que possible.
J'ai commencé à étudier sur la base de la formation du réseau et de la manière de l'améliorer. J'ai identifié plusieurs choses qui peuvent influencer le choix des paramètres:
- Adresse IP à partir de laquelle la demande est adressée à Apple. Lorsque le VPN a été activé, les objets dans les résultats étaient déjà plus proches de l'emplacement du serveur VPN.
- L'emplacement actuel de l'utilisateur. Si les coordonnées utilisateur actuelles sont envoyées à la demande, les résultats seront beaucoup plus précis.
- La langue système de l'appareil.
Exemples VPNNew york
Toronto
Kiev
Londres
Francfort
Il est possible que d'autres facteurs puissent affecter les résultats de la recherche, mais cela m'a suffi pour obtenir le résultat souhaité.
Le cours de développement ultérieur était l'utilisation de l'emplacement actuel de l'appareil.
import UIKit import MapKit import CoreLocation final class ViewController: UIViewController, CLLocationManagerDelegate { private let locationManager = CLLocationManager() private let request = MKLocalSearch.Request() private var localSearch: MKLocalSearch? private var region = MKCoordinateRegion() override func viewDidLoad() { super.viewDidLoad() if CLLocationManager.locationServicesEnabled() { locationManager.delegate = self locationManager.desiredAccuracy = kCLLocationAccuracyBest locationManager.requestWhenInUseAuthorization() locationManager.startUpdatingLocation() } } func searchPlace(_ place: String) { localSearch?.cancel() request.naturalLanguageQuery = place request.region = region localSearch = MKLocalSearch(request: request) localSearch?.start { [weak self] response, error in let mapItems = response.mapItems
RésultatL'emplacement actuel de l'appareil - Madrid, fournisseur Internet Vodafone ES

Dans la méthode delagate didUpdateLocations, nous créons
MKCoordinateSpan . Si j'ai bien compris la documentation Apple, alors plus la valeur que nous fixons Delta de latitude / longitude est basse, plus notre région actuelle sera étroite (et précise), car il s'agit d'une sorte de zoom sur nos coordonnées actuelles dans MapKit.
Après cela, en effet, la priorité d'extradition a changé et m'a montré principalement les endroits à côté de moi.
Il ne reste plus qu'à embellir les noms de la liste. Puisque parfois, certaines propriétés de CLPlacemark peuvent avoir le même nom, au final cela ne sera pas très joli: New York, New York, NY. Pour ce faire, vous devez créer une structure distincte, qui formera un beau nom dans la liste.
import Foundation import MapKit struct Placemark { let location: String init(item: MKMapItem) { var locationString: String = "" if let name = item.name { locationString += "\(name)" } if let locality = item.placemark.locality, locality != item.name { locationString += ", \(locality)" } if let administrativeArea = item.placemark.administrativeArea, administrativeArea != item.placemark.locality { locationString += ", \(administrativeArea)" } if let country = item.placemark.country, country != item.name { locationString += ", \(country)" } location = locationString } }
Ensuite, déjà en réponse à la recherche, nous pouvons facilement mapper CLPlacemark dans la structure créée et la transmettre à la liste.
localSearch?.start { [weak self] searchResponse, error in guard let items = searchResponse?.mapItems else { return }
Maintenant, les résultats sont plus élégants et peuvent déjà être utilisés dans le projet pour marquer leurs emplacements visités.
L'un des principaux inconvénients est que vous ne pouvez utiliser cette solution que si le projet est affiné pour iOS / Mac OS. Si le projet implique un développement pour d'autres plateformes, je recommanderais d'utiliser une solution de
Google ou
Facebook . De plus, tous les emplacements ne sont pas idéalement définis dans toutes les régions.
Vous pouvez voir le code final du projet dans le
référentiel .