include local on map with firebase swift - error - ios

I am encountering great difficulties in putting an integrated map based on firebase in my project, I am looking for expert knowledge to help me, even though I build run correctly at the time of running the system for, my code below:
Thanks
#IBOutlet var mapView: MKMapView!
override func viewDidLoad() {
super.viewDidLoad()
let locationsRef = Database.database().reference(withPath: "locations")
locationsRef.observe(.value, with: { snapshot in
for item in snapshot.children {
guard let locationData = item as? DataSnapshot else { continue }
var locationValue = locationData.value as! [String: Any]
var location: CLLocationCoordinate2D!
if let lat = locationValue["lat"] as? String {
let lng = Double(locationValue["lng"] as! String)!
location = CLLocationCoordinate2D(latitude: Double(lat)!, longitude: lng)
} else {
let lat = locationValue["lat"] as! Double
let lng = locationValue["lng"] as! Double
location = CLLocationCoordinate2D(latitude: lat, longitude: lng)
}
func addAnnotations(coords: [CLLocation]){
for coord in coords{
let CLLCoordType = CLLocationCoordinate2D(latitude: coord.coordinate.latitude,
longitude: coord.coordinate.longitude);
let anno = MKPointAnnotation();
anno.coordinate = CLLCoordType;
self.mapView.addAnnotation(anno);
}
}
}
})
}

Related

Swift Firebase Sort By Distance

I am trying to sort my array by distance. I already have everything hooked up to grab the distance's but unsure how to sort from closest to furthest from the users location. I've used the below code for MKMapItem's yet unsure how to apply to my current array.
func sortMapItems() {
self.mapItems = self.mapItems.sorted(by: { (b, a) -> Bool in
return self.userLocation.location!.distance(from: a.placemark.location!) > self.userLocation.location!.distance(from: b.placemark.location!)
})
}
Firebase Call
databaseRef.child("Businesses").queryOrdered(byChild: "businessName").observe(.childAdded, with: { (snapshot) in
let key = snapshot.key
if(key == self.loggedInUser?.uid) {
print("Same as logged in user, so don't show!")
} else {
if let locationValue = snapshot.value as? [String: AnyObject] {
let lat = Double(locationValue["businessLatitude"] as! String)
let long = Double(locationValue["businessLongitude"] as! String)
let businessLocation = CLLocation(latitude: lat!, longitude: long!)
let latitude = self.locationManager.location?.coordinate.latitude
let longitude = self.locationManager.location?.coordinate.longitude
let userLocation = CLLocation(latitude: latitude!, longitude: longitude!)
let distanceInMeters : Double = userLocation.distance(from: businessLocation)
let distanceInMiles : Double = ((distanceInMeters.description as String).doubleValue * 0.00062137)
let distanceLabelText = "\(distanceInMiles.string(2)) miles away"
var singleChildDictionary = locationValue
singleChildDictionary["distanceLabelText"] = distanceLabelText as AnyObject
self.usersArray.append(singleChildDictionary as NSDictionary)
/*
func sortMapItems() {
self.mapItems = self.mapItems.sorted(by: { (b, a) -> Bool in
return self.userLocation.location!.distance(from: a.placemark.location!) > self.userLocation.location!.distance(from: b.placemark.location!)
})
}
*/
}
//insert the rows
self.followUsersTableView.insertRows(at: [IndexPath(row:self.usersArray.count-1,section:0)], with: UITableViewRowAnimation.automatic)
}
}) { (error) in
print(error.localizedDescription)
}
}
First make these changes in your code
singleChildDictionary["distanceInMiles"] = distanceInMiles
Then you can sort it like this:
self.usersArray = self.usersArray.sorted {
!($0["distanceInMiles"] as! Double > $1["distanceInMiles"] as! Double)
}

Why is my MKCoordinateRegion nil?

So I've got the following function to set my map region for my app:
func mapRegion() -> MKCoordinateRegion {
databaseHandle = databaseRef.child("RunList").child(runName).observe(.value, with: { (snapshot) in
let runData = snapshot.value as? [String: AnyObject]
self.minLat = runData?["startLat"] as? Double
self.minLng = runData?["startLong"] as? Double
self.maxLat = runData?["endLat"] as? Double
self.maxLng = runData?["endLong"] as? Double
print("testing")
print(self.minLat!)
print(self.maxLng!)
self.coordinate = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: (self.minLat! + self.maxLat!)/2,
longitude: (self.minLng! + self.maxLng!)/2),
span: MKCoordinateSpan(latitudeDelta: (self.maxLat! - self.minLat!)*1.1,
longitudeDelta: (self.maxLng! - self.minLng!)*1.1))
})
return coordinate
}
I have initialised the following variables at the top of my class:
var minLat: Double!
var minLng: Double!
var maxLat: Double!
var maxLng: Double!
var coordinate: MKCoordinateRegion!
And am trying to set the map region as follows:
override func viewDidLoad() {
super.viewDidLoad()
configureView()
mapView.region = mapRegion()
}
The error I am getting is in the lines where I am setting my coordinate region under the block of code that contains my databaseHandle in mapRegion(). When the code is running, I get the following error under the line 'return coordinate':
fatal error: unexpectedly found nil while unwrapping an Optional value
To my understanding, this is occurring because 'coordinate' has no values and is an Optional. Why is it that coordinate has no values even though I am setting it up using a global variable? I feel like I am missing something so simple!
Thanks
Your code does not work because observe works asynchronously and it is impossible to return a value from a method containing a asynchronous task.
You need a completion handler
func mapRegion(completion: (MKCoordinateRegion)->()) {
databaseHandle = databaseRef.child("RunList").child(runName).observe(.value, with: { (snapshot) in
let runData = snapshot.value as? [String: AnyObject]
self.minLat = runData?["startLat"] as? Double
self.minLng = runData?["startLong"] as? Double
self.maxLat = runData?["endLat"] as? Double
self.maxLng = runData?["endLong"] as? Double
print("testing")
print(self.minLat!)
print(self.maxLng!)
let region = MKCoordinateRegion(
center: CLLocationCoordinate2D(latitude: (self.minLat! + self.maxLat!)/2,
longitude: (self.minLng! + self.maxLng!)/2),
span: MKCoordinateSpan(latitudeDelta: (self.maxLat! - self.minLat!)*1.1,
longitudeDelta: (self.maxLng! - self.minLng!)*1.1))
completion(region)
})
}
And use it
mapRegion() { region in
mapView.region = region
// do other things with the region
}
Side note:
There are too many question marks in the code. Use optional bindings to unwrap optionals safely.

MapBox swift 3 api

I use MapBox api swift 2.3 and reading geojson. But after swift 3 dont work began to upgrade.
Swift 2.3 examples; its work
for location in locations {
nate2D(latitude: location[1].doubleValue, longitude: location[0].doubleValue)
coordinates.append(coordinate)
}
Swift 3.0 dont work
if let feature = feature as? NSDictionary {
if let geometry = feature["geometry"] as? NSDictionary {
if geometry["type"] as? String == "Polygon" {
var coordinates: [CLLocationCoordinate2D] = []
if let locations = geometry["coordinates"] as? NSArray {
for location in locations {
for i in (0 ..< (location as AnyObject).count)
{
let coordinate = CLLocationCoordinate2D(latitude: ???, longitude: ???)
coordinates.append(coordinate)
}
}
}
let shape = MGLPolygon(coordinates: &coordinates, count: UInt(coordinates.count))
DispatchQueue.main.async(execute: {
[unowned self] in
self.mapView.addAnnotation(shape)
})
}
}
}
enter image description here
It ' s correct answer
for location in locations {
for i in (0 ..< (location as AnyObject).count)
{
let a = locations[0] as? NSArray
let b = a?[i] as? NSArray
var c = b?[0]
print(c);
let coordinate = CLLocationCoordinate2D(latitude: b?[1] as! CLLocationDegrees, longitude: b?[0] as! CLLocationDegrees)
coordinates.append(coordinate)
}
}

How to convert String to CLLocationDegrees Swift 2

I am trying to convert a String that I am retrieving from Firebase and adding it as several annotations on Google Maps. Unfortuanately, my app is crashing whenever it goes through the current code:
ref = FIRDatabase.database().reference()
ref.child("Locations").observeSingleEventOfType(.Value, withBlock: { (snapshot) in
let lat = (snapshot.value!["Latitude"] as! NSString).doubleValue
let lon = (snapshot.value!["Longitude"] as! NSString).doubleValue
let complainLoc = CLLocationCoordinate2DMake(lat, lon)
let Coordinates = CLLocationCoordinate2D(latitude: lat, longitude: lon)
})
My JSON Tree
My Code Block Which Crashes
Here is the code I used for saving data to Firebase
FIRDatabase.database().reference().child("Location").child(FIRAuth.auth()!.currentUser!.uid).setValue(["Latitude": locationManager.location!.coordinate.latitude, "Longitude": locationManager.location!.coordinate.longitude])
SWIFT 5
let dbLat = Double(latStr) // Convert String to double
let dbLong = Double(longStr)
Use latitude and longitude
let center = CLLocationCoordinate2D(latitude: dbLat! , longitude: dbLong! )
let pointAnnotation = MKPointAnnotation()
pointAnnotation.coordinate = CLLocationCoordinate2D(latitude: dbLat!, longitude:dbLong!)
Make sure when you are saving the values of lat and lon to the database you are saving them as Float or Double..
For retrieving use :
ref = FIRDatabase.database().reference()
ref.child("Locations").observeEventType(.Value, withBlock: { (snapshot) in
if snapshot.exists(){
if let locationDictionary = snapshot.value as [String : AnyObject]{
for each in locationDictionary{
//each will bring you every location dictionary in your database for your every user
let lat = each.value!["Latitude"] as! CLLocationDegrees
let lon = each.value!["Longitude"] as! CLLocationDegrees
let userId = each.key as! String
let complainLoc = CLLocationCoordinate2DMake(lat, lon)
let Coordinates = CLLocationCoordinate2D(latitude: lat, longitude: lon)
//Every time this for loop complete's itself it will generate a new set of Coordinates for each user
}
}
}
})
EDIT:
Updated code for Firebase 6 and Swift 5
let ref = self.ref.child("Locations")
ref.observeSingleEvent(of: .value, with: { snapshot in
let allLocations = snapshot.children.allObjects as! [DataSnapshot]
for location in allLocations {
let lat = location.childSnapshot(forPath: "Latitude").value as! CLLocationDegrees
let lon = location.childSnapshot(forPath: "Longitude").value as! CLLocationDegrees
let userId = location.key
let locCoord = CLLocationCoordinate2DMake(lat, lon)
let coordinates = CLLocationCoordinate2D(latitude: lat, longitude: lon)
}
})
note that self.ref points to my Firebase root ref.

How can I add a GPS that leads to pins on map?

I am making an app that displays pins ( that the user adds) on a map and saves them in a tableview and I would like to have a GPS lead the user when they tap on a button that opens the GPS so they can get to that place, how could I do this, this is the code I have in the table view:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! UITableViewCell
cell.textLabel?.text = places[indexPath.row]["name"]
return cell
}
override func tableView(tableView: UITableView, willSelectRowAtIndexPath indexPath: NSIndexPath) -> NSIndexPath? {
activePlace = indexPath.row
return indexPath
}
and this is the code I have in the map view:
func action(gestureRecognizer:UIGestureRecognizer) {
if gestureRecognizer.state == UIGestureRecognizerState.Began {
var touchPoint = gestureRecognizer.locationInView(self.Map)
var newCoordinate = self.Map.convertPoint(touchPoint, toCoordinateFromView: self.Map)
var location = CLLocation(latitude: newCoordinate.latitude , longitude: newCoordinate.longitude)
CLGeocoder().reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
var title = ""
if (error == nil) {
if let p = CLPlacemark(placemark: placemarks?[0] as! CLPlacemark) {
var subThoroughfare: String = ""
var thoroughfare: String = ""
if p.subThoroughfare != nil {
subThoroughfare = p.subThoroughfare
}
if p.thoroughfare != nil {
thoroughfare = p.thoroughfare
}
title = "\(subThoroughfare) \(thoroughfare)"
}
}
if title == "" {
title = "added \(NSDate())"
}
places.append(["name":title,"lat":"\(newCoordinate.latitude)","lon":"\(newCoordinate.longitude)"])
I have been trying the code below to add the GPS but I have a predefined destination so I suppose I have to change the coordinates to something else but I dont know exactly what, thanks for the help !
UIApplication.sharedApplication().openURL(NSURL(string: "http://maps.apple.com/maps?daddr=34.539250,-117.222025")!)
I am also using this piece of code for the user location
func locationManager(manager: CLLocationManager!, didUpdateLocations locations: [AnyObject]!) {
var userLocation:CLLocation = locations[0] as! CLLocation
var latitude = userLocation.coordinate.latitude
var longitude = userLocation.coordinate.longitude
var coordinate = CLLocationCoordinate2DMake(latitude, longitude)
var latDelta:CLLocationDegrees = 0.01
var lonDelta:CLLocationDegrees = 0.01
var span:MKCoordinateSpan = MKCoordinateSpanMake(latDelta, lonDelta)
var region:MKCoordinateRegion = MKCoordinateRegionMake(coordinate, span)
self.Map.setRegion(region, animated: true)
To piggyback off of #Mingebag 's answer, in response to your comment there:
You need to set the variables in the openMapForPlace() method he provided to the ones that correspond to the gps position you want to get directions to in the apple maps app.
Wherever you call this method, you can pass it the variables you need. For this to work, you really just need to give it Lat / Lon somehow.
You could do that by making it be:
func openMapsForPlace(lat: Double, lon: Double) {}
or whatever format you have those in.
You can also just put this code wherever you want if you have a better place for it to execute from:
let regionDistance:CLLocationDistance = 10000
//set the coordinates with your variables
var coordinates = CLLocationCoordinate2DMake(newCoordinates.latitude, newCoordinates.longitude)
let regionSpan = MKCoordinateRegionMakeWithDistance(coordinates, regionDistance, regionDistance)
var options = [
MKLaunchOptionsMapCenterKey: NSValue(MKCoordinate: regionSpan.center),
MKLaunchOptionsMapSpanKey: NSValue(MKCoordinateSpan: regionSpan.span)
]
//now your placemark will have the lat long you put in above
var placemark = MKPlacemark(coordinate: coordinates, addressDictionary: nil)
var mapItem = MKMapItem(placemark: placemark)
mapItem.name = "\(self.venueName)"
//this line then launches the app for you
mapItem.openInMapsWithLaunchOptions(options)
Let us assume you have location enabled so you just need the "user location" that you have saved in your table then try this:
Just pass your lang,lat and that should do it ^^
func openMapForPlace() {
var lat1 : NSString = self.venueLat
var lng1 : NSString = self.venueLng
var latitute:CLLocationDegrees = lat1.doubleValue
var longitute:CLLocationDegrees = lng1.doubleValue
let regionDistance:CLLocationDistance = 10000
var coordinates = CLLocationCoordinate2DMake(latitute, longitute)
let regionSpan = MKCoordinateRegionMakeWithDistance(coordinates, regionDistance, regionDistance)
var options = [
MKLaunchOptionsMapCenterKey: NSValue(MKCoordinate: regionSpan.center),
MKLaunchOptionsMapSpanKey: NSValue(MKCoordinateSpan: regionSpan.span)
]
var placemark = MKPlacemark(coordinate: coordinates, addressDictionary: nil)
var mapItem = MKMapItem(placemark: placemark)
mapItem.name = "\(self.venueName)"
mapItem.openInMapsWithLaunchOptions(options)
}
If there are any question feel free to ask

Resources