Ambiguous use of 'subscript' and Cannot call value of non-function type 'AnyObject' errors retrieving data from Firebase in SWIFT 4.1 - ios

I'm changing the way I'm posting and retrieving firebase CLLocationCoordinated2D, from one post per value to one post with all values, so I found this post and I would like to implement it in my own code. Retrieving data from Firebase and storing as annotations on a map .
Im having the errors mentioned in the title on the constants date, time, latitude, longitude and desc.
I'm still learning Firebase so any explanation will be very helpful. Tis is the function where I get the errors.
func displayAnnotations() {
let ref = Database.database().reference()
ref.child("Sightings").observe(.childAdded, with: { (snapshot) in
let date = (snapshot.value as AnyObject?)!("Date") as! String?
let time = (snapshot.value as AnyObject)!("Time") as! String?
let latitude = (snapshot.value as AnyObject)!("Latitude") as! String?
let longitude = (snapshot.value as AnyObject?)!("Longitude") as! String?
let desc = (snapshot.value as AnyObject?)!("Description") as! String?
let annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2D(latitude: (Double(latitude!))!, longitude: (Double(longitude!))!)
annotation.title = date
annotation.subtitle = time
self.mapView.addAnnotation(annotation)
})}
and this is the posting function:
func post() {
let date = dateLabel.text
let time = timeLabel.text
let latitude = latitudeLabel.text
let longitude = longitudeLabel.text
let sightingDescription = descriptionLabel.text
let post: [String:String] = ["Date" : date as AnyObject,
"Time" : time as AnyObject,
"Latitude" : latitude as AnyObject,
"Longitude" : longitude as AnyObject,
"Description" : sightingDescription as AnyObject]
var ref: DatabaseReference!
ref = Database.database().reference()
ref.child("Sightings").childByAutoId().setValue(post)
}
Is it just because it's written for a version previous to swift 4.1?

You changed [] to ()
let dic = snapshot.value as! [String:String]
let date = dic["Date"]
let time = dic["Time"]
let latitude = dic["Latitude"]
let longitude = dic["Longitude"]
let desc = dic["Description"]

Related

SWIFT 4.1 Cannot invoke initializer for type 'Double' with an argument list of type '(String?)'

I'm retrieving mapView annotations posted in Firebase to show them on map, but while converting String values for latitude and longitude to recombine them into CLLocationCoordinates2D I get the error. I don't understand why, because in another function I use the same method but getting the values from arrays but I don't get the error. Also on retrieving the data I would like to also use the key value from firebase as initialiser for my annotations. But I get two more errors Use of unresolved identifier 'firebaseKey' and Use of unresolved identifier 'recombinedCoordinate' for initialisers. Here're the function:
func displayAlerts() {
// FIREBASE: Reference
ref = Database.database().reference()
// FIREBASE:Retrieve posts and listen for changes
databaseHandle = ref?.child("Community").child("Alert Notifications").observe(.childAdded, with: { (snapshot) in
let data = snapshot.value as? [String:String]
if let actualData = data {
let dataLatitude = data!["Latitude"]
let dataLongitude = data!["Longitude"]
self.alertIconToDisplay = data!["Description"]
let doubledLatitude = Double(dataLatitude)
let doubledLongitude = Double(dataLongitude)
var recombinedCoordinate = CLLocationCoordinate2D(latitude: doubledLatitude!, longitude: doubledLongitude!)
print("Firebase post retrieved !")
self.dummyFunctionToFoolFirebaseObservers()
}
let dataKey = snapshot.key as? String
if let firebaseKey = dataKey {
print("Longitude DataKey is \(String(describing: dataKey))")
print("Longitude Actual DataKey is \(String(describing: firebaseKey))")
self.dummyFunctionToFoolFirebaseObservers()
}
print("fir long \((snapshot.value!, snapshot.key))")
userAlertAnnotation = UserAlert(type: self.alertIconToDisplay, coordinate: recombinedCoordinate, firebaseKey: firebaseKey)
self.mapView.addAnnotation(self.userAlertAnnotation)
})
}
Here's the annotation model :
class UserAlert: NSObject , MKAnnotation {
var type: String?
var firebaseKey: String?
var coordinate:CLLocationCoordinate2D
init(type:String, coordinate:CLLocationCoordinate2D, firebaseKey: String) {
self.type = type
self.firebaseKey = firebaseKey
self.coordinate = coordinate
}
}
What am I doing wrong here? I understand that the error on the initialisers are because initialisation occurs in key closures, but how then I incorporate all data into initialiser ?
func mapView(_ mapView: MKMapView, viewFor annotation: MKAnnotation) -> MKAnnotationView? {
let annotationView = MKAnnotationView(annotation: userAlertAnnotation, reuseIdentifier: "") // CHANGE FOR NEW ANNOTATION : FULL DATA
//added if statement for displaying user location blue dot
if annotation is MKUserLocation{
return nil
} else {
annotationView.image = UIImage(named: alertIconToDisplay!) // choose the image to load
let transform = CGAffineTransform(scaleX: 0.27, y: 0.27)
annotationView.transform = transform
return annotationView
}
}
func postAlertNotification() {
// to set next notification id as the position it will have in array ( because first position is 0 ) we use the array.count as value
let latitude = alertNotificationLatitude
let longitude = alertNotificationLongitude
let alertType = alertNotificationType
let post: [String:String] = [//"Date" : date as! String,
//"Time" : time as! String,
"Latitude" : latitude as! String,
"Longitude" : longitude as! String,
"Description" : alertType as! String]
var ref: DatabaseReference!
ref = Database.database().reference()
ref.child("Community").child("Alert Notifications").childByAutoId().setValue(post)
}
The error in the topic says that you can't create a Double from an optional String which is true.
To solve it force unwrap the values for Latitude and Longitude.
But the main issue is a scope issue, all variables used in the initializer must be in the same scope. You can flatten the scope with guard statements:
...
databaseHandle = ref?.child("Community").child("Alert Notifications").observe(.childAdded, with: { (snapshot) in
defer { self.dummyFunctionToFoolFirebaseObservers() }
guard let data = snapshot.value as? [String:String] else { return }
guard let firebaseKey = snapshot.key as? String else { return }
// let date = data!["Date"]
// let time = data!["Time"]
let dataLatitude = data["Latitude"]!
let dataLongitude = data["Longitude"]!
self.alertIconToDisplay = data["Description"]!
let doubledLatitude = Double(dataLatitude)
let doubledLongitude = Double(dataLongitude)
let recombinedCoordinate = CLLocationCoordinate2D(latitude: doubledLatitude!, longitude: doubledLongitude!)
print("Firebase post retrieved !")
// self .keyaLon = dataKey
// self.keyaLonArray.append(firebaseKey)
print("Longitude Actual DataKey is \(String(describing: firebaseKey))")
print("fir long \((snapshot.value!, snapshot.key))")
self.userAlertAnnotation = UserAlert(type: self.alertIconToDisplay, coordinate: recombinedCoordinate, firebaseKey: firebaseKey)
self.mapView.addAnnotation(self.userAlertAnnotation)
})

Place multiple markers on MKMapView from Firebase Database

I'm creating an iOS app that uses Apple Maps to display markers for Garage/Yard Sales in the local area. So far I've been able to figure out how to place one marker on Apple Maps from the Firebase Database, but I'm not sure how to do it with multiple markers. I've done similar tasks using cells to display different content in a UITableView from Firebase Database but this is my first time doing it map-wise. I was reading an article here that showed how it was possible with JSON data, but due to the fact the marker information will be live, it wouldn't be possible that way for my app. What would be the best way to add multiple markers to a MKMapView?
Snippet from ViewController (Only setup for one marker)
Database.database().reference().child("posts").observeSingleEvent(of: .value, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let artwork = Artwork(title: dictionary["title"] as! String,
locationName: dictionary["location"] as! String,
discipline: dictionary["category"] as! String,
coordinate: CLLocationCoordinate2D(latitude: dictionary["lat"] as! Double, longitude: dictionary["long"] as! Double))
self.mapView.addAnnotation(artwork)
}
})
Artwork.swift
class Artwork: NSObject, MKAnnotation {
let title: String?
let locationName: String
let discipline: String
let coordinate: CLLocationCoordinate2D
init(title: String, locationName: String, discipline: String, coordinate: CLLocationCoordinate2D) {
self.title = title
self.locationName = locationName
self.discipline = discipline
self.coordinate = coordinate
super.init()
}
var subtitle: String? {
return locationName
}
}
I was able to figure out a way to this thanks to the link #kosuke-ogawa posted.
Snippet within ViewController.swift
override func viewDidLoad() {
super.viewDidLoad()
let ref = Database.database().reference()
let postsRef = ref.child("posts")
postsRef.observeSingleEvent(of: .value, with: { (snapshot) in
for snap in snapshot.children {
let postSnap = snap as! DataSnapshot
if let dict = postSnap.value as? [String:AnyObject] {
let title = dict["title"] as! String
let locationName = dict["location"] as! String
let discipline = dict["category"] as! String
let coordinate = CLLocationCoordinate2D(latitude: dict["lat"] as! Double, longitude: dict["long"] as! Double)
let artwork = Artwork(title: title, locationName: locationName, discipline: discipline, coordinate: coordinate)
self.mapView.addAnnotation(artwork)
}
}
})
}
If there is a cleaner way to do this feel free to edit my code to help others in the future.
for multiple place marker on MKMapView you can take array of ArtWork That you Have created, please try this bellow solution
var arrArtworks: [Artwork] = []
override func viewDidLoad() {
super.viewDidLoad()
intializeData()
PlaceMarker()
}
func intializeData(){
Database.database().reference().child("posts").observeSingleEvent(of: .value, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
let artwork = Artwork(title: dictionary["title"] as! String,
locationName: dictionary["location"] as! String,
discipline: dictionary["category"] as! String,
coordinate: CLLocationCoordinate2D(latitude: dictionary["lat"] as! Double, longitude: dictionary["long"] as! Double))
arrArtworks.append(Artwork)
}
})
}
func PlaceMarker() {
mapView.addAnnotations(arrArtworks)
}
For example, you can use for loop:
Database.database().reference().child("posts").observeSingleEvent(of: .value, with: { (snapshot) in
for child in snapshot.children.allObjects as! [DataSnapshot] {
if let dictionary = child.value as? [String: AnyObject] {
let artwork = Artwork(title: dictionary["title"] as! String,
locationName: dictionary["location"] as! String,
discipline: dictionary["category"] as! String,
coordinate: CLLocationCoordinate2D(latitude: dictionary["lat"] as! Double, longitude: dictionary["long"] as! Double))
self.mapView.addAnnotation(artwork)
}
}
})
cf. http://takeip.com/swift-3-firebase-to-mapkit.html

Cannot read CLLocationDegrees from plist

I am having some trouble with this following struct:
struct EmployeeDetails {
let functionary: String
let imageFace: String
let phone: String
let latitude: CLLocationDegrees
let longitude: CLLocationDegrees
init(dictionary: [String: Any]) {
self.functionary = (dictionary["Functionary"] as? String) ?? ""
self.imageFace = (dictionary["ImageFace"] as? String) ?? ""
self.phone = (dictionary["Phone"] as? String) ?? ""
self.latitude = (dictionary["Latitude"] as! CLLocationDegrees)
self.longitude = (dictionary["Longitude"] as! CLLocationDegrees)
I have no compiling errors but, when I run the app, I get this runtime error:
It's important to say that I am loading data from a plist. Anyone could show me what am I doing wrong?
EDIT:
Now I am having these errors:
The error is pretty clear: you are casting a string-typed value to a NSNumber.
Try this instead:
let latitudeStr = dictionary["Latitude"] as! String
self.latitude = CLLocationDegrees(latitudeStr)!
and you should do the same thing to the "Longitude" property as well ;)
You also may be running into localized numbers issues. Try this:
let numberFormatter = NumberFormatter()
numberFormatter.decimalSeparator = ","
numberFormatter.thousandSeparator = "."
...
self.latitude = numberFormatter.number(from: latitudeStr)!.doubleValue

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.

Could not cast value of type 'NSTaggedPointerString' to 'NSArray in Google Place Api

i integrated Google Place Api to fetch user location, i have to fetch "area","state" and "City" while i try to get value my App gets crash. Please post ur answer
here my sample code
func mapView(mapView: GMSMapView, idleAtCameraPosition position: GMSCameraPosition)
{
dispatch_async(dispatch_get_main_queue(), { () -> Void in
let centerLocation = CLLocation(latitude: position.target.latitude, longitude: position.target.longitude)
CLGeocoder().reverseGeocodeLocation(centerLocation, completionHandler:
{(placemarks, error) in
if (error == nil && placemarks!.count>0)
{
let placemark = CLPlacemark(placemark: placemarksArray[0] as! CLPlacemark)
let latitude = String(format: "%.8f",position.target.latitude)
let longitude = String(format: "%.8f",position.target.longitude)
if let addrList = placemark.addressDictionary
{
print("address==\(address)")
let addStr = address?["FormattedAddressLines"] as! [String]
let addStr1 = address?["City"] as! [String]
let addStr2 = address?["State"] as! [String]
let addStr3 = address?["SubLocality"] as! [String]
NSLog("%#\n%#\n%#", addStr1,addStr2,addStr3)
self.addressLabel.text = addStr.joinWithSeparator(",")
}
}
})
})
}
my output :
address=={
City = "New Delhi";
Country = India;
CountryCode = IN;
FormattedAddressLines = (
"Mayur Vihar",
"New Delhi",
"Delhi 110092",
India
);
Name = 110092;
State = Delhi;
SubAdministrativeArea = Delhi;
SubLocality = "Mayur Vihar";
ZIP = 110092;
}
and my Crash Report :
Could not cast value of type 'NSTaggedPointerString' (0x1064cb860) to 'NSArray' (0x1064cb900).
City and State has String value and FormattedAddressLines has Arry.
Use like this
let addStr1 = address["City"] as! String
let addStr2 = address["State"] as! String
let FormattedAddressLines = address["FormattedAddressLines"] as![String]

Resources