How to use Yelp data and drop Pins (Swift) - ios

I am currently reading a file using the yelp api that contains generated data based on a search I perform. I would like to place Pin annotations using the location field that is in the generated data.
Here is my model class Restaurant
var resultQueryDictionary:NSDictionary!
class Resturant: NSObject {
var name: String!
var thumbUrl: String!
var address: String!
var jsonData: NSData!
init(dictionary: NSDictionary) {
name = dictionary["name"] as? String
thumbUrl = dictionary["thumbUrl"] as? String
address = dictionary["address"] as? String
}
class func searchWithQuery(query: String, completion: ([Resturant]!, NSError!) -> Void) {
YelpClient.sharedInstance.searchWithTerm(query, success: { (operation: AFHTTPRequestOperation!, response: AnyObject!) -> Void in
let responseInfo = response as! NSDictionary
resultQueryDictionary = responseInfo
println(responseInfo)
}) { (operation: AFHTTPRequestOperation!, error: NSError!) -> Void in
println(error)
}
}
}
I am trying to make a method to drop all the pins in AttractionsViewController here:
func performYelpSearch(query: String) {
attractionsMap.removeAnnotations(attractionsMap.annotations)
matchingItems.removeAll()
Resturant.searchWithQuery(query, completion: { (BusinessList: [Resturant]!, error: NSError!) in
if(error != nil) {
println("Error occured in search: \(error.localizedDescription)")
} else if BusinessList.count == 0 {
println("No matches found")
} else {
println("Yelp matches found!")
for business in BusinessList as [Resturant] {
self.Businesses.append(business)
var annotation = MKPointAnnotation()
var yelpBusinessMock: YelpBusiness = YelpBusiness(dictionary: resultQueryDictionary)
annotation.coordinate = yelpBusinessMock.location.coordinate
annotation.title = yelpBusinessMock.name
self.attractionsMap.addAnnotation(annotation)
}
}
})
}
However, no annotation pins are dropped, so i'm a bit confused.
This is my annotationForViewMethod:
func mapView(mapView: MKMapView!, viewForAnnotation annotation: MKAnnotation!) -> MKAnnotationView! {
if(annotation is MKUserLocation) {
return nil;
}
let reuseId = "pin"
var pinView = mapView.dequeueReusableAnnotationViewWithIdentifier(reuseId) as? MKPinAnnotationView;
if(pinView == nil) {
pinView = MKPinAnnotationView(annotation: annotation, reuseIdentifier: reuseId);
pinView!.canShowCallout = true;
pinView!.animatesDrop = true;
}
var moreInfoButton = UIButton.buttonWithType(UIButtonType.DetailDisclosure) as! UIButton;
pinView?.rightCalloutAccessoryView = moreInfoButton;
return pinView;
}
I originally used Apple's LocalSearch:
func performSearch(input:String) {
attractionsMap.removeAnnotations(attractionsMap.annotations);
matchingItems.removeAll()
let request = MKLocalSearchRequest()
request.naturalLanguageQuery = input
println(input);
request.region = attractionsMap.region;
let search = MKLocalSearch(request: request)
search.startWithCompletionHandler({(response:
MKLocalSearchResponse!,
error: NSError!) in
if error != nil {
println("Error occured in search: \(error.localizedDescription)")
} else if response.mapItems.count == 0 {
println("No matches found")
} else {
println("Matches found")
for item in response.mapItems as! [MKMapItem] {
println("Name = \(item.name)")
println("Phone = \(item.phoneNumber)")
matchingItems.append(item as MKMapItem)
println("Matching items = \(matchingItems.count)")
var placemark = item.placemark;
var subThoroughfare:String = "";
var thoroughfare:String = "";
var locality:String = "";
var postalCode:String = "";
var administrativeArea:String = "";
var country:String = "";
var title = "";
var subtitle = "";
if (placemark.subThoroughfare != nil) {
subThoroughfare = placemark.subThoroughfare;
}
if(placemark.thoroughfare != nil) {
thoroughfare = placemark.thoroughfare;
}
if(placemark.locality != nil) {
locality = placemark.locality;
}
if(placemark.postalCode != nil) {
postalCode = placemark.postalCode;
}
if(placemark.administrativeArea != nil) {
administrativeArea = placemark.administrativeArea;
}
if(placemark.country != nil) {
country = placemark.country;
}
println("viewcontroller placmark data:");
println(locality);
println(postalCode);
println(administrativeArea);
println(country);
title = " \(subThoroughfare) \(thoroughfare) \n \(locality), \(administrativeArea) \n \(postalCode) \(country)";
subtitle = " \(subThoroughfare) \(thoroughfare)";
println(title);
var annotation = MKPointAnnotation()
annotation.coordinate = item.placemark.coordinate
annotation.title = item.name + " " + subtitle;
self.attractionsMap.addAnnotation(annotation)
}
}
})
}
And this worked perfectly...

This is what you need to do.
In your Resturant Model, you need to add this bit of code
let dataArray = responseInfo["businesses"] as! NSArray
for business in dataArray {
let obj = business as! NSDictionary
var yelpBusinessMock: YelpBusiness = YelpBusiness(dictionary: obj)
var annotation = MKPointAnnotation()
annotation.coordinate = yelpBusinessMock.location.coordinate
annotation.title = yelpBusinessMock.name
map.addAnnotation(annotation)
}
You want to get the businesses as an NSArray, and then each object or business inside the dataArray will be a NSDictionary which you'll pass into your yelpBusiness class.
This should work.

Related

translate string with YandexAPI in for loop

I want to translate googleMap Api result for location address to Persian. i'm using YandexApi for translate address. separate address components with "،" and pass this array to for api. my problem is after every service call my userInterface updated. how can solve this problem.
for example when I pass this array to method:["road","avenue"]
address label text update after every translate response
first text for label = "جاده"
last text for label = "جاده ، خیابان"
there is my code:
func translateText(text:[String],closure:#escaping ((_ success:String?,_ error:Error?) -> Void)) {
var translateString: String = ""
var responseError: Error?
for item in text {
myGroup.enter()
let urlString = "https://translate.yandex.net/api/v1.5/tr.json/translate?key=trnsl.1.1.20171105T134956Z.795c7a0141d3061b.dc25bae76fa5740b2cdecb02396644dea58edd24&text=\(item)&lang=fa&format=plain&options=1"
if let allowString = Utilities.shareInstance.getQueryAllowedString(url: urlString) {
if let url = URL(string:allowString){
Alamofire.request(url).responseJSON { response in
guard let responseData = response.data else {
return
}
do {
let json = try JSONSerialization.jsonObject(with: responseData, options: [])
if let res = json as? [String:Any] {
if let code = res["code"] as? Int {
if code == 200 {
if let textArr = res["text"] as? [AnyObject] {
let flattArr = Utilities.shareInstance.flatStringMapArray(textArr)
if flattArr.count > 0 {
translateString += " ، " + flattArr[0]
}
}
}
}
}
}catch {
responseError = error
}
self.myGroup.leave()
closure(translateString, responseError)
}
}
}
}
}
private func translatingAddressArrayForChecking(address:[String]) {
var addressString = ""
let addressComponent = address
if addressComponent.count > 0 {
YandexTranslateAPI.shateInstance.translateText(text: addressComponent, closure:{ success,_ in
if let res = success {
addressString = res
OperationQueue.main.addOperation {
self.layoutActivity(start: false)
if self.searchState == .Origin && self.destinationButton.isHidden == false{
if let selectOrigin = self.originSelectedCity {
if let city = selectOrigin.city_title {
if addressString.contains(city) {
self.originAddressLabel.text = addressString
if let location = self.startCoordinate {
self.setOrigin(location: location)
}
}else {
self.CreateCustomTopField(text: "wrong city", color: Constants.ERROR_COLOR)
}
}
}
}else if self.searchState == .Destination || self.destinationButton.isHidden == true {
if let selectOrigin = self.destinationSelectedCity {
if let city = selectOrigin.city_title {
if addressString.contains(city) {
self.destinationAddressLabel.text = addressString
if let location = self.endCoordinate {
self.setDestination(location: location)
}
}else {
self.CreateCustomTopField(text: "wrong city", color: Constants.ERROR_COLOR)
}
}
}
}
}
}
})
}
addresses label text update multiple with array component.

Managing encapsulated data in closure

Newbie here.Using Google API Nearby Search. I have problem sending encapsulated data into closure, already populated table with vicinity info, but when i try to send placeID info into closure to get Details, it gives me nil.
Here i get placeID and vicinity, and afterwards populate tableView with places array. Class Place is in separate swift file, function downloadPlaceID is inside ViewController.
class Place {
var placeId: String!
var vicinity: String!
var _placeId: String {
if placeId == nil {
placeId = ""
}
return placeId
}
var _vicinity: String {
if vicinity == nil {
vicinity = ""
}
return vicinity
}
init( place: [String:Any]) {
if let ids = place["id"] as? String {
self.placeId = ids
}
if let vicinities = place["vicinity"] as? String {
self.vicinity = vicinities
}
}
}
func downloadPlaceID (completed: #escaping DownloadComplete) {
let placeURL = URL(string: nearbyURL)
Alamofire.request(placeURL!).responseJSON { (response) in
let result = response.result
if let dictionary = result.value as? [String:Any] {
if let results = dictionary["results"] as? [[String:Any]] {
if let status = dictionary["status"] as? String {
if status == "OK" {
for obj in results {
place = Place(place: obj)
// here i get all the placeID's
places.append(place)
}
}
}
}
}
completed()
}
}
Then i try to get details, into which I put placeID:
func downloadDetails( input: String, completed: DownloadComplete) {
let details = "\(detailsBaseURL)\(detailsPlaceId)\(input)\(detailsKey)\(detailsSearchAPIKey)"
print(placeID)
Alamofire.request(details).responseJSON { response in
let result = response.result
if let dictionary = result.value as? [String:Any] {
if let result = dictionary["result"] as? [String:Any] {
if let phoneNumber = result["formatted_phone_number"] as? String {
self.phone = phoneNumber
print(self.phone!)
}
if let geometry = result["geometry"] as? [String:Any] {
if let location = geometry["location"] as? [String:Any] {
if let latitude = location["lat"] as? Double {
self.lat = latitude
print(self.lat!)
}
if let longitude = location["lng"] as? Double {
self.lng = longitude
print(self.lng!)
}
}
}
if let openingHours = result["opening_hours"] as? [String:Any] {
if let openNow = openingHours["open_now"] as? Bool {
self.workHours = openNow
print(self.workHours!)
}
}
}
}
}
}
Here is code inside viewDidLoad that i'm trying to use to get details.
override func viewDidLoad() {
super.viewDidLoad()
downloadPlaceID {
detail.downloadDetails(input: place.placeId, completed: {
})
}
}
It should be "place_id" instead of "id"
class Place {
var placeId: String!
var vicinity: String!
var _placeId: String {
if placeId == nil {
placeId = ""
}
return placeId
}
var _vicinity: String {
if vicinity == nil {
vicinity = ""
}
return vicinity
}
init( place: [String:Any]) {
if let ids = place["place_id"] as? String {
self.placeId = ids
}
if let vicinities = place["vicinity"] as? String {
self.vicinity = vicinities
}
}
}

Parse retrieving wrong PFFile

I am attempting to retrieve a PFFile image based off an objectID attached to the same object I am pulling the imageFile from. All other details are being pulled correctly (Strings) however, when I pull the PFFile, a random image from that class loads, not usually the matching image for that object, below is my code for how I am querying it and I am unsure where my logic is incorrect.
#objc(mapView:didTapMarker:) func mapView(_ mapView: GMSMapView, didTap marker: GMSMarker) -> Bool {
if z == 0 {
print("\(marker) tapped")
let lat = marker.position.latitude as Double
let long = marker.position.longitude as Double
let tempMarker = getMarkerForLongAndLat(long: long, lat: lat, markers: markers)
let index = getIndexForMarker(marker: tempMarker)
getInfo(self.markersID[index]) {
PFFile in
self.imageFile?.getDataInBackground(block: { (imageData, error) in
if(imageData != nil) {
self.fullImage = UIImage(data: imageData!)
}
})
self.performSegue(withIdentifier: "playerSegue", sender: nil)
}
}
return true
}
func getInfo(_ markerID : String, completion: #escaping (PFFile) -> Void)
{
self.group.enter()
print("printing")
print(markerLat)
print(markerLong)
print(postsLat)
print(postsLong)
print("A")
let query = PFQuery(className:"Posted")
print("b")
print(markerID)
//query.whereKey("GPS", equalTo: PFGeoPoint(latitude: markerLat, longitude: markerLong))
query.whereKey("objectId", equalTo: "\(markerID)")
query.findObjectsInBackground { (objects, error) in
print("c")
if(error == nil)
{
print("d")
for object in objects!
{
print("made it")
self.imageFile = (object.object(forKey: "ImageFile") as! PFFile?)
self.userofVideo = object.object(forKey: "User") as AnyObject?
self.hour = object.object(forKey: "Hour") as AnyObject?
self.minutes = object.object(forKey: "minutes") as AnyObject?
self.streetName = object.object(forKey: "Location") as AnyObject?
self.tempLat = (object.object(forKey: "GPS")! as AnyObject).latitude as Double
self.tempLong = (object.object(forKey: "GPS")! as AnyObject).longitude as Double
self.currentText = (object.object(forKey: "text")! as Any? as! String?)
self.tempCoordinates = CLLocationCoordinate2D(latitude: self.tempLat! as CLLocationDegrees, longitude: self.tempLong! as CLLocationDegrees)
//print(tempLong)
}
}
else
{
print("didn't make it")
print(error)
}
completion(self.imageFile!)
}
self.group.leave()
}
Add the getDataInBackground inside the for loop. Here is a quick swift 3 example for your code.
#IBOutlet weak var fullImage : UIImageView!
let imageFile = (object.object(forKey: "ImageFile") as? PFFile)
imageFile?.getDataInBackground (block: { (data, error) -> Void in
if error == nil {
if let imageData = data {
//Here you can cast it as a UIImage or do what you need to
self.fullImage.image = UIImage(data:imageData)
}
}
})

Swift - Generate an Address Format from Reverse Geocoding

I am trying to generate a Formatted Full address using CLGeocoder in Swift 3. I referred to this SO thread to get the code given below.
However, sometimes the app crashes with a 'nil' error at the line:
//Address dictionary
print(placeMark.addressDictionary ?? "")
Questions:
How can I concatenate these values retrieved from the GeoCoder to form a full address? (Street + City + etc)
How do I handle the nil error I get when the func is unable to find an address?
Full code:
func getAddress() -> String {
var address: String = ""
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: selectedLat, longitude: selectedLon)
//selectedLat and selectedLon are double values set by the app in a previous process
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
// Place details
var placeMark: CLPlacemark!
placeMark = placemarks?[0]
// Address dictionary
//print(placeMark.addressDictionary ?? "")
// Location name
if let locationName = placeMark.addressDictionary!["Name"] as? NSString {
//print(locationName)
}
// Street address
if let street = placeMark.addressDictionary!["Thoroughfare"] as? NSString {
//print(street)
}
// City
if let city = placeMark.addressDictionary!["City"] as? NSString {
//print(city)
}
// Zip code
if let zip = placeMark.addressDictionary!["ZIP"] as? NSString {
//print(zip)
}
// Country
if let country = placeMark.addressDictionary!["Country"] as? NSString {
//print(country)
}
})
return address;
}
func getAddressFromLatLon(pdblLatitude: String, withLongitude pdblLongitude: String) {
var center : CLLocationCoordinate2D = CLLocationCoordinate2D()
let lat: Double = Double("\(pdblLatitude)")!
//21.228124
let lon: Double = Double("\(pdblLongitude)")!
//72.833770
let ceo: CLGeocoder = CLGeocoder()
center.latitude = lat
center.longitude = lon
let loc: CLLocation = CLLocation(latitude:center.latitude, longitude: center.longitude)
ceo.reverseGeocodeLocation(loc, completionHandler:
{(placemarks, error) in
if (error != nil)
{
print("reverse geodcode fail: \(error!.localizedDescription)")
}
let pm = placemarks! as [CLPlacemark]
if pm.count > 0 {
let pm = placemarks![0]
print(pm.country)
print(pm.locality)
print(pm.subLocality)
print(pm.thoroughfare)
print(pm.postalCode)
print(pm.subThoroughfare)
var addressString : String = ""
if pm.subLocality != nil {
addressString = addressString + pm.subLocality! + ", "
}
if pm.thoroughfare != nil {
addressString = addressString + pm.thoroughfare! + ", "
}
if pm.locality != nil {
addressString = addressString + pm.locality! + ", "
}
if pm.country != nil {
addressString = addressString + pm.country! + ", "
}
if pm.postalCode != nil {
addressString = addressString + pm.postalCode! + " "
}
print(addressString)
}
})
}
Formatting addresses is hard because each country has its own format.
With a few lines of code, you can get the correct address format for each country and let Apple handle the differences.
Since iOS 11, you can get a Contacts framework address:
extension CLPlacemark {
#available(iOS 11.0, *)
open var postalAddress: CNPostalAddress? { get }
}
This extension is part of the Contacts framework.
This means, this feature is invisible to you in the XCode code completion until you do
import Contacts
With this additional import, you can do something like
CLGeocoder().reverseGeocodeLocation(location, preferredLocale: nil) { (clPlacemark: [CLPlacemark]?, error: Error?) in
guard let place = clPlacemark?.first else {
print("No placemark from Apple: \(String(describing: error))")
return
}
let postalAddressFormatter = CNPostalAddressFormatter()
postalAddressFormatter.style = .mailingAddress
var addressString: String?
if let postalAddress = place.postalAddress {
addressString = postalAddressFormatter.string(from: postalAddress)
}
}
and get the address formatted in the format for the country in the address.
The formatter even supports formatting as an attributedString.
Prior to iOS 11, you can convert CLPlacemark to CNPostalAddress yourself and still can use the country specific formatting of CNPostalAddressFormatter.
This is my code for swift 3
func getAdressName(coords: CLLocation) {
CLGeocoder().reverseGeocodeLocation(coords) { (placemark, error) in
if error != nil {
print("Hay un error")
} else {
let place = placemark! as [CLPlacemark]
if place.count > 0 {
let place = placemark![0]
var adressString : String = ""
if place.thoroughfare != nil {
adressString = adressString + place.thoroughfare! + ", "
}
if place.subThoroughfare != nil {
adressString = adressString + place.subThoroughfare! + "\n"
}
if place.locality != nil {
adressString = adressString + place.locality! + " - "
}
if place.postalCode != nil {
adressString = adressString + place.postalCode! + "\n"
}
if place.subAdministrativeArea != nil {
adressString = adressString + place.subAdministrativeArea! + " - "
}
if place.country != nil {
adressString = adressString + place.country!
}
self.lblPlace.text = adressString
}
}
}
}
You can esaily call above funcation like:
let cityCoords = CLLocation(latitude: newLat, longitude: newLon)
cityData(coord: cityCoords)
For fixing the empty address issue, either you can use a class property to hold the appended value or you can use a closure to return the value back to the calling function
For fixing the crash you need to avoid the force unwrapping of optionals
Using a closure you can do it like:
// Using closure
func getAddress(handler: #escaping (String) -> Void)
{
var address: String = ""
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: selectedLat, longitude: selectedLon)
//selectedLat and selectedLon are double values set by the app in a previous process
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
// Place details
var placeMark: CLPlacemark?
placeMark = placemarks?[0]
// Address dictionary
//print(placeMark.addressDictionary ?? "")
// Location name
if let locationName = placeMark?.addressDictionary?["Name"] as? String {
address += locationName + ", "
}
// Street address
if let street = placeMark?.addressDictionary?["Thoroughfare"] as? String {
address += street + ", "
}
// City
if let city = placeMark?.addressDictionary?["City"] as? String {
address += city + ", "
}
// Zip code
if let zip = placeMark?.addressDictionary?["ZIP"] as? String {
address += zip + ", "
}
// Country
if let country = placeMark?.addressDictionary?["Country"] as? String {
address += country
}
// Passing address back
handler(address)
})
}
You can call the method like:
getAddress { (address) in
print(address)
}
To concatenate you can simply replace return address by this :
return "\(locationName), \(street), \(city), \(zip), \(country)"
Keeping it simple - A full Swift 3 & 4 compatible View Controller example for obtaining a formatted address string from user's location (add in the other keys available in CLPlacemark if you want more information in your string):
import UIKit
import CoreLocation
class ViewController: UIViewController, CLLocationManagerDelegate {
let manager = CLLocationManager()
let geocoder = CLGeocoder()
var locality = ""
var administrativeArea = ""
var country = ""
override func viewDidLoad() {
super.viewDidLoad()
manager.delegate = self
manager.desiredAccuracy = kCLLocationAccuracyBest
manager.requestWhenInUseAuthorization()
manager.startUpdatingLocation()
func locationManager(_ manager: CLLocationManager, didUpdateLocations locations: [CLLocation]) {
let location = locations[0]
manager.stopUpdatingLocation()
geocoder.reverseGeocodeLocation(location, completionHandler: {(placemarks, error) in
if (error != nil) {
print("Error in reverseGeocode")
}
let placemark = placemarks! as [CLPlacemark]
if placemark.count > 0 {
let placemark = placemarks![0]
self.locality = placemark.locality!
self.administrativeArea = placemark.administrativeArea!
self.country = placemark.country!
}
})
}
func userLocationString() -> String {
let userLocationString = "\(locality), \(administrativeArea), \(country)"
return userLocationString
}
}
Calling print(userLocationString()) in this example will print: suburb, state, country
Don't forget to add Privacy - Location When In Use Usage Description to your Info.plist file beforehand, to allow the user to grant permissions to your app to utilise location services.
Here's a 2-3 line version of the answers here:
func getAddress(placemarks: [CLPlacemark]) -> String {
guard let placemark = placemarks.first, !placemarks.isEmpty else {return ""}
let outputString = [placemark.locality,
placemark.subLocality,
placemark.thoroughfare,
placemark.postalCode,
placemark.subThoroughfare,
placemark.country].compactMap{$0}.joined(separator: ", ")
print(outputString)
return outputString
}
func getAddress(from coordinate: CLLocationCoordinate2D, completion: #escaping (String) -> Void) {
let geoCoder = CLGeocoder()
let location = CLLocation.init(latitude: coordinate.latitude, longitude: coordinate.longitude)
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
// check for errors
guard let placeMarkArr = placemarks else {
completion("")
debugPrint(error ?? "")
return
}
// check placemark data existence
guard let placemark = placeMarkArr.first, !placeMarkArr.isEmpty else {
completion("")
return
}
// create address string
let outputString = [placemark.locality,
placemark.subLocality,
placemark.thoroughfare,
placemark.postalCode,
placemark.subThoroughfare,
placemark.country].compactMap { $0 }.joined(separator: ", ")
completion(outputString)
})
}
CLGeocoder().reverseGeocodeLocation(CLLocation(latitude: vehicleLocation.latitude, longitude: vehicleLocation.latitude), completionHandler: {(placemarks, error) -> Void in
guard error == nil else {completionHandler(nil); return}
guard let place = placemarks else {completionHandler(nil); return}
if place.count > 0 {
let pm = place[0]
var addArray:[String] = []
if let name = pm.name {
addArray.append(name)
}
if let thoroughfare = pm.thoroughfare {
addArray.append(thoroughfare)
}
if let subLocality = pm.subLocality {
addArray.append(subLocality)
}
if let locality = pm.locality {
addArray.append(locality)
}
if let subAdministrativeArea = pm.subAdministrativeArea {
addArray.append(subAdministrativeArea)
}
if let administrativeArea = pm.administrativeArea {
addArray.append(administrativeArea)
}
if let country = pm.country {
addArray.append(country)
}
if let postalCode = pm.postalCode {
addArray.append(postalCode)
}
let addressString = addArray.joined(separator: ",\n")
print(addressString)
completionHandler(addressString)
}
else { completionHandler(nil)}
})
I create my own static class for Geocoding and get attributes of CLPlacemark and obtain a complete address, like "usually" returns Google:
import Foundation
import CoreLocation
class ReverseGeocoding {
static func geocode(latitude: Double, longitude: Double, completion: #escaping (CLPlacemark?, _ completeAddress: String?, Error?) -> ()) {
CLGeocoder().reverseGeocodeLocation(CLLocation(latitude: latitude, longitude: longitude)) { placemarks, error in
guard let placemark = placemarks?.first, error == nil else {
completion(nil, nil, error)
return
}
let completeAddress = getCompleteAddress(placemarks)
completion(placemark, completeAddress, nil)
}
}
static private func getCompleteAddress(_ placemarks: [CLPlacemark]?) -> String {
guard let placemarks = placemarks else {
return ""
}
let place = placemarks as [CLPlacemark]
if place.count > 0 {
let place = placemarks[0]
var addressString : String = ""
if place.thoroughfare != nil {
addressString = addressString + place.thoroughfare! + ", "
}
if place.subThoroughfare != nil {
addressString = addressString + place.subThoroughfare! + ", "
}
if place.locality != nil {
addressString = addressString + place.locality! + ", "
}
if place.postalCode != nil {
addressString = addressString + place.postalCode! + ", "
}
if place.subAdministrativeArea != nil {
addressString = addressString + place.subAdministrativeArea! + ", "
}
if place.country != nil {
addressString = addressString + place.country!
}
return addressString
}
return ""
}
}
Then the implementation:
ReverseGeocoding.geocode(coordinate: coordinate, completion: { (placeMark, completeAddress, error) in
if let placeMark = placeMark, let completeAddress = completeAddress {
print(placeMark.postalCode)
print(placeMark)
print(completeAddress)
} else {
// do something with the error
}
Finaly the print:
15172
Calle del Arenal, 4, Calle del Arenal, 4, 15172 Oleiros, A Coruña, España # <+43.33190337,-8.37144380> +/- 100.00m, region CLCircularRegion (identifier:'<+43.33190337,-8.37144380> radius 70.84', center:<+43.33190337,-8.37144380>, radius:70.84m)
Calle del Arenal, 4, Oleiros, 15172, A Coruña, España
func convertLatLongToAddress(latitude:Double, longitude:Double) {
let geoCoder = CLGeocoder()
let location = CLLocation(latitude: latitude, longitude: longitude)
var labelText = ""
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
var placeMark: CLPlacemark!
placeMark = placemarks?[0]
if placeMark != nil {
if let name = placeMark.name {
labelText = name
}
if let subThoroughfare = placeMark.subThoroughfare {
if (subThoroughfare != placeMark.name) && (labelText != subThoroughfare) {
labelText = (labelText != "") ? labelText + "," + subThoroughfare : subThoroughfare
}
}
if let subLocality = placeMark.subLocality {
if (subLocality != placeMark.subThoroughfare) && (labelText != subLocality) {
labelText = (labelText != "") ? labelText + "," + subLocality : subLocality
}
}
if let street = placeMark.thoroughfare {
if (street != placeMark.subLocality) && (labelText != street) {
labelText = (labelText != "") ? labelText + "," + street : street
}
}
if let locality = placeMark.locality {
if (locality != placeMark.thoroughfare) && (labelText != locality) {
labelText = (labelText != "") ? labelText + "," + locality : locality
}
}
if let city = placeMark.subAdministrativeArea {
if (city != placeMark.locality) && (labelText != city) {
labelText = (labelText != "") ? labelText + "," + city : city
}
}
if let state = placeMark.postalAddress?.state {
if (state != placeMark.subAdministrativeArea) && (labelText != state) {
labelText = (labelText != "") ? labelText + "," + state : state
}
}
if let country = placeMark.country {
labelText = (labelText != "") ? labelText + "," + country : country
}
// labelText gives you the address of the place
}
})
}
Here as an improvement I added place name as well. It makes address more meaningful.
func getAddressFromlatLong(lat: Double, long: Double, completion: #escaping (_ address: String) -> Void){
let coordinate = CLLocationCoordinate2D(latitude: lat, longitude: long)
let geocoder = GMSGeocoder()
var add = ""
geocoder.reverseGeocodeCoordinate(coordinate) { (response, error) in
if let address = response?.firstResult() {
guard let arrAddress = address.lines else {return}
if arrAddress.count > 1 {
add = /(arrAddress[0]) + ", " + /(arrAddress[1])
}else if arrAddress.count == 1 {
add = /(arrAddress[0])
}
completion(add)
}
}
}

MKMapView Select Annotation

I'm new to swift and currently trying to figure out how to get data about the annotation that the user has selected. I have a localsearch function that will add the annotations, and after the user selects one I'd like to be able to access that. I'm trying to use selectedAnnotations, but it doesn't seem to be working.
Localsearch:
func performSearch(){
matchingItems.removeAll()
let request = MKLocalSearchRequest()
request.naturalLanguageQuery = searchTextField.text
request.region = mapView.region
let search = MKLocalSearch(request: request)
search.startWithCompletionHandler({(response:
MKLocalSearchResponse!,
error: NSError!) in
if error != nil {
println("Error occured in search: \(error.localizedDescription)")
} else if response.mapItems.count == 0 {
println("No matches found")
} else {
println("Matches found")
for item in response.mapItems as [MKMapItem] {
println("Name = \(item.name)")
println("Phone = \(item.phoneNumber)")
self.matchingItems.append(item as MKMapItem)
println("Matching items = \(self.matchingItems.count)")
var annotation = MKPointAnnotation()
annotation.coordinate = item.placemark.coordinate
annotation.title = item.name
annotation.subtitle = item.placemark.title
self.mapView.addAnnotation(annotation)
}
}
})
From there I'm trying to use
var selectedAnnotations: [MKPointAnnotation]!
// print signout location
println(selectedAnnotations)
To access the annotation, but this is just returning "nil"
Method for annotation:
#IBAction func signoutToLocationButton(sender: AnyObject) {
// saves current user location
PFGeoPoint.geoPointForCurrentLocationInBackground {
(geoPoint: PFGeoPoint!, error: NSError!) -> Void in
if error == nil {
// do something with the new geoPoint
println(geoPoint)
var signoutLocation = PFObject(className: "SignoutLocation")
signoutLocation["Location"] = geoPoint
signoutLocation.saveInBackgroundWithBlock {
(success: Bool, error: NSError!)-> Void in
if (success) {
// has been saved
}
else {
//went wrong
}
}
}
// get location of where they are signing out to
self.mapView.selectedAnnotations(AnyObject)
// print signout location
// println(selectedAnnotations)
}
Here's an example of how to use the selectedAnnotations property:
if self.mapView.selectedAnnotations?.count > 0 {
if let ann = self.mapView.selectedAnnotations[0] as? MKAnnotation {
println("selected annotation: \(ann.title!)")
let c = ann.coordinate
println("coordinate: \(c.latitude), \(c.longitude)")
//do something else with ann...
}
}
(Though whether you need to or want to do this inside your // has been saved block instead of outside is something you'll have to figure out.)

Resources