Firebase Location to Maps - swift

I am trying to add my locations values from firebase and turn them into annotation within my app. This is the code I have at the minute which is not working! Looking for a helping hand, feel like I am very close to getting this right but I have hit a mind block!
import UIKit
import Firebase
import MapKit
class ViewController: UIViewController {
#IBOutlet weak var mapView: MKMapView!
let locationsRef = FIRDatabase.database().reference(withPath: "locations")
override func viewDidLoad() {
super.viewDidLoad()
locationsRef.observe(.value, with: { snapshot in
for item in snapshot.children {
guard let locationData = item as? FIRDataSnapshot else { continue }
let locationValue = locationData.value as! [String: Any]
let location = CLLocationCoordinate2D(latitude: locationValue["lat"] as! Double, longitude: locationValue["lng"] as! Double)
let name = locationValue["name"] as! String
self.addAnnotation(at: location, name: name)
}
})
}
func addAnnotation(at location: CLLocationCoordinate2D, name: String) {
// add that annotation to the map
let annotation = MKPointAnnotation()
annotation.title = location["name"] as? String
annotation.coordinate = CLLocationCoordinate2D(latitude: location["latitude"] as! Double, longitude: location["lonitude"] as! Double)
mapView.addAnnotation(annotation)
}
}
My code is now working but I am getting this issue:
Could not cast value of type "NSTaggedPointerString' (0x10a708d10) to 'NSNumber' (0x109d10488).
Relating to this line of code:
let location = CLLocationCoordinate2D(latitude: locationValue["lat"] as! Double, longitude: locationValue["lng"] as! Double)
I have read something about converting a string to double but there is no string in this line? Can anybody help please?
Updated Code Subtitle
Subtitle
Update Error
Error

You are most likely storing the coordinates as String objects in your Firebase database. That's the most common reason that would cause that error. In that case, try the following
let lat = Double(locationValue["lat"] as! String)
let lng = Double(locationValue["lng"] as! String)
let location = CLLocationCoordinate2D(latitude: lat, longitude: lng)
Update
Some location entries of your database are stored as String objects, others as floating point numbers. You should have a consistent structure (i.e all of them be strings or doubles).
Either way, here's a solution for your error:
var location: CLLocationCoordinate2D!
if let lat = locationValue["lat"] as? String {
// stored as String
let lng = Double(locationValue["lng"] as! String)!
location = CLLocationCoordinate2D(latitude: Double(lat)!, longitude: lng)
}
else {
// stored as a floating point number
let lat = locationValue["lat"] as! Double
let lng = locationValue["lng"] as! Double
location = CLLocationCoordinate2D(latitude: lat, longitude: lng)
}
EDIT 2
Just remove the let here

Related

Display Only Mapkit Annotations From Firebase Created Within The Last Hour

I am currently able to pull down all of my annotations from Firebase Firestore and display them with no problem. In my snapshot listener I would like to be able to only display annotations created within the last hour. I've included my code below that isn't working and is still returning all annotations in my Firestore.
let hourAgo = Date().addingTimeInterval(-3600)
db.collection("pins").addSnapshotListener { QuerySnapshot, Error in
guard let documents = QuerySnapshot?.documents else {
print("No documents")
return
}
let annotation = documents.map { QueryDocumentSnapshot -> Pin in
let data = QueryDocumentSnapshot.data()
let latitude = data["latitude"] as? Double ?? 0.0
let longitude = data["longitude"] as? Double ?? 0.0
let eventTitle = data["eventTitle"] as? String ?? ""
let eventSubtitle = data["eventSubtitle"] as? String ?? ""
let lastUpdated = data["lastUpdated"] as? Timestamp ?? Timestamp.init()
let pinDate = lastUpdated.dateValue()
let coordinate = CLLocationCoordinate2D(latitude: latitude, longitude: longitude)
if pinDate >= self.hourAgo {
self.displayPins(coordinate: coordinate, eventSubtitle: eventSubtitle, eventTitle: eventTitle)
}else{
return Pin(latitude: latitude, longitude: longitude, eventTitle: eventTitle, eventSubtitle: eventSubtitle)
}
//print(annotation)
return Pin(latitude: latitude, longitude: longitude, eventTitle: eventTitle, eventSubtitle: eventSubtitle)
}
}
}
func displayPins(coordinate: CLLocationCoordinate2D, eventSubtitle: String, eventTitle: String) {
let annotation = MKPointAnnotation()
annotation.coordinate = coordinate
annotation.title = eventTitle
annotation.subtitle = eventSubtitle
mapView.addAnnotation(annotation)
}```

Trying to display location data from Firebase to MapKit

searched about this question a lot but couldn't find a simple answer.
I want to be able to read location data from a Firebase database and display them on the MapKit as annotation.
Can you help?
Right now I can display annotations that are hard-coded but can't figure out how to read the database and then create a loop that displays all annotations on the map.
This is the code for showing the annotations.
I want to fill title, latitude, longitude with the relevant data from Firebase.
The Firebase database is as follows: Firebase Database
**The application is already connected with the Firebase!
let points = [
["title": , "latitude": , "longitude": ],
]
for point in points {
let annotation = MKPointAnnotation()
annotation.title = point["title"] as? String
annotation.coordinate = CLLocationCoordinate2D(latitude: point["latitude"] as! Double, longitude: point["longitude"] as! Double)
mapView.addAnnotation(annotation)
}
After suggestion I edited the code and it is like this right now:
let ref = Database.database().reference()
ref.child("x-database-alpha/name").observe(.childAdded, with: { (snapshot) in
let title = (snapshot.value as AnyObject?)!["Title"] as! String?
let latitude = (snapshot.value as AnyObject?)!["Latitude"] as! String?
let longitude = (snapshot.value as AnyObject?)!["Longitude"] as! String?
let annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2D(latitude: (Double(latitude!))!, longitude: (Double(longitude!))!)
annotation.title = title
self.mapView.addAnnotation(annotation)
})
Check theExact Firebase Database
Right now the code has no error but it doesn't present the annotation!
Sample code:
let ref = Database.database().reference()
ref.child("name").observe(.childAdded, with: { (snapshot) in
let title = (snapshot.value as AnyObject!)!["Title"] as! String!
let latitude = (snapshot.value as AnyObject!)!["Latitude"] as! String!
let longitude = (snapshot.value as AnyObject!)!["Longitude"] as! String!
let annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2D(latitude: (Double(latitude!))!, longitude: (Double(longitude!))!)
annotation.title = title
self.map.addAnnotation(annotation)
})

Getting coordinates from Firebase to make annotations

I am currently trying to get my data from firebase and create annotations in my MKMapKitView. I believe that I am retrieving the data properly but not creating the annotations properly.
I think that because there are multiple users I cannot just set it up as a regular way of annotating.
let userLocation = CLLocationCoordinate2D(latitude: Double(userLatitude!), longitude: Double(userLongitude!))
let userAnnotation = MKPointAnnotation();
userAnnotation.coordinate = self.userLocation!;
//userAnnotation.title = "Riders Location";
map.addAnnotation(userAnnotation);
}
I'll also add in how I am retrieving the users.
func retrieveUsers(){
let ref = Database.database().reference()
ref.child("users").queryOrderedByKey().observeSingleEvent(of: .value, with: { snapshot in
let users = snapshot.value as! [String: AnyObject]
self.user.removeAll()
for (_,value) in users {
if let uid = value["uid"] as? String {
if uid != Auth.auth().currentUser!.uid {
let userToShow = User()
if let fullName = value["full name"] as? String,
let userLongitude = value["long"] as? Double,
let userLatitude = value["lat"] as? Double
{
userToShow.fullName = value["full name"] as? String
userToShow.imagePath = value["urlToImage"] as? String
userToShow.userID = value["uid"] as? String
userToShow.userLongitude = value["long"] as? String
userToShow.userLatitude = value["lat"] as? String
self.user.append(userToShow)
}
}
}
}
DispatchQueue.main.async {
self.map.reloadInputViews()
//not sure if this is right
}
})
Thank you!!
It's a hunch, but I think you are after a function like this - you were pretty close with you effort! NB - no semicolons in swift syntax.
private func addUserAnnotation(user: User) {
let userAnnotation = MKPointAnnotation()
let userLocation = CLLocationCoordinate2D(latitude: Double(user.userLatitude!),
longitude: Double(user.userLongitude!))
userAnnotation.coordinate = userLocation
//userAnnotation.title = "Riders Location"
self.map.addAnnotation(userAnnotation)
}
Call the function like this - let's say you want to add annotation for just the first user from your user array:
addUserAnnotation(user: user[0]) //addUserAnnotation(user[0]) also acceptable
Here is the OP's class for the user. I think this is also important
class User: NSObject {
var userID: String!
var fullName: String!
var imagePath: String!
var userLongitude: Double! // change from String!
var userLatitude: Double! // change from String!
}

Representing Coordinates from Firebase as Annotations Swift

I am having trouble putting annotations on my map from longitudinal and latitudinal degrees I uploaded to firebase. I want to add an annotation for all of my users but cannot get xcode to recognize my userLatitude and userLongitude variables in another function.
Anything will help!
func retrieveUsers(){
let ref = Database.database().reference()
ref.child("users").queryOrderedByKey().observeSingleEvent(of: .value, with: { snapshot in
let users = snapshot.value as! [String: AnyObject]
self.user.removeAll()
for (_,value) in users {
if let uid = value["uid"] as? String {
if uid != Auth.auth().currentUser!.uid {
let userToShow = User()
if let fullName = value["full name"] as? String,
let imagePath = value["urlToImage"] as? String,
//String(format: "%f", self.currentUserLocation?.longitude ?? 0),
let userLongitude = value["long"] as? CLLocationDegrees,
let userLatitude = value["lat"] as? CLLocationDegrees
//why isnt it recognizing the users degrees
{
userToShow.fullName = value["full name"] as? String
userToShow.imagePath = value["urlToImage"] as? String
userToShow.userID = value["uid"] as? String
userToShow.userLongitude = value["long"] as? CLLocationDegrees
userToShow.userLatitude = value["lat"] as? CLLocationDegrees
self.user.append(userToShow)
}
}
}
}
DispatchQueue.main.async {
self.map.reloadInputViews()
//not sure if this is right
}
})
}
func mapView(_ mapView: MKMapView, didAdd views: [MKAnnotationView]) {
let otherUserLocation: CLLocationCoordinate2D = CLLocationCoordinate2DMake(userLatitude, userLongitude)
let userAnnotation = MKPointAnnotation()
userAnnotation.coordinate = otherUserLocation
userAnnotation.title = "fullName"
}
And if you change as? CLLocationDegrees by as? String and manage the cast in CLLocationDegrees afterwards?
Because I think, in Firebase, data is stored as String and not as Double (that is the type of data a CLLocationDegrees type is referring to).

Swift 3 found nil when Firebase DB persistence is enabled

In my app I am using Firebase db to store my data, I have everything working as it should, with my tableview populating with the data from the db. However I have found that on initial start it is quite slow to download the data, so I enable persistence in AppDelegate. When I did this and ran the app it crashes on load with an unexpectedly found nil error.
FIRDatabase.database().persistenceEnabled = true
is the line I added to AppDelegate under didFinishLaunching section.
The error occurs in this class on the line I have marked
import Foundation
import FirebaseDatabase
import CoreLocation
struct newTracks {
//Declerations
var locationManager = CLLocationManager()
let name: String!
let lat: Double!
let lon: Double!
let countryImage: String!
let link: String!
let ref: FIRDatabaseReference?
let distance: Double
//Initialize
init(name: String, trackId: Int, postcode: String, trackType: String, trackURL: String, locID: Int, lat: Double, lon: Double, phoneNumber: String, email: String, rating: Double, numrating: Double, totalrating: Double, countryImage: String, link: String, distance: Double) {
self.name = name
self.ref = nil
self.lat = lat
self.lon = lon
self.countryImage = countryImage
self.link = link
self.distance = distance
}
//Initialize data from Firebase
init(snapshot: FIRDataSnapshot) {
let snapshotValue = snapshot.value as! [String: AnyObject]
name = snapshotValue["name"] as! String
lat = snapshotValue["lat"]as! Double
lon = snapshotValue["long"]as! Double
ref = snapshot.ref
countryImage = snapshotValue["country"] as! String <-- Unexpectedly found nil
link = snapshotValue["link"] as! String
let currentLat = self.locationManager.location!.coordinate.latitude
let currentLon = self.locationManager.location!.coordinate.longitude
let myLocation = CLLocation(latitude: currentLat, longitude: currentLon)
let loc = CLLocation(latitude: lat, longitude: lon)
let distanceInMiles = round(myLocation.distance(from: loc) / 1609.34)
distance = distanceInMiles
}
func toAnyObject() -> Any {
return [
"name": name,
"lat": lat,
"lon": lon,
"countryImage": countryImage,
"link": link,
"distance": distance
]
}
}
I don't get why this works ok when persistence isn't switched on, but when I enable it it crashes. Any help is much appreciated.
You have marked countryImage as Optional value. Your snapshot may not have value for "country".
Try to change:
let countryImage: String!
to:
let countryImage: String
EDIT:
Maybe, when you are turning persistance on, your application is taking old data from cache. And this data doesn't have ["country"] in snapshot value. So it founds nil -> throwing error. Check your snapshotValue through breakpoint. And check if it has value for country
Hope it helps
I have resolved this by uninstalling the app from my phone, this cleared away the old cached data, seems to load ok now