I'm trying to create annotations from Parse backend but I get an error '[PFObject]?' is not convertible to '[PFObject]'
I based my code on a question i found here Query a GeoPoint from Parse and add it to MapKit as MKAnnotation?
Heres a pic of my code and the error. code photo
{
mapView.showsUserLocation = true
mapView.delegate = self
mapView.setUserTrackingMode(MKUserTrackingMode.Follow, animated: true)
MapViewLocationManager.delegate = self
MapViewLocationManager.startUpdatingLocation()
var annotationQuery = PFQuery(className: "Movers")
currentLoc = PFGeoPoint(location: MapViewLocationManager.location)
annotationQuery.whereKey("ubicacion", nearGeoPoint: currentLoc, withinKilometers: 10)
annotationQuery.findObjectsInBackgroundWithBlock {
(movers, error) -> Void in
if error == nil {
// The find succeeded.
print("Successful query for annotations")
let myMovers = movers as [PFObject]
for mover in myMovers {
let point = movers["ubicacion"] as PFGeoPoint
let annotation = MKPointAnnotation()
annotation.coordinate = CLLocationCoordinate2DMake(point.latitude, point.longitude)
self.mapView.addAnnotation(annotation)
}
}else {
// Log details of the failure
print("Error: \(error)")
}
}
}
Thanks in advance
myMovers is a [PFObject]?, an optional array of PFObjects (as in maybe an array of PFObjects or maybe nil). Because it is an optional, it's not directly convertible to the non optional because you can't convert nil to [PFObject]. So what you really want is to do a conditional cast here using as? and put it in an if let statement. Like so
if let myMovers = movers as? [PFObject] {
// Use myMovers to do what you want
}
That will execute what is in the braces only when movers is a [PFObject] and not nil.
Related
I am trying to recover all the animal objects based on certain parameters. First I need to retrieve their location from parse as well as the name, but since I am importing more than one and using geocoder, I am using strings, and not an array. So instead of appending the imported information into an array, I am mutating a variable. What I though would happen is the query would go through the first object then run the retrieveLocation method, then proceed to the next object imported from parse, but instead it imports everything then runs the method, so in the end I only get 1 object instead of how many are supposed to be imported.
let query = PFQuery(className: "Animals")
query.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error: NSError?) in
if(error == nil){
for object in objects!{
if let addressobj = object["Add"] as? NSDictionary{
if let address = addressobj["address"] as? String{
self.addr = address
print("sdfadsf \(self.addr)")
}
}
if let name = object["Name"] as? String{
self.impname = name
print("rturrty \(self.impname)")
self.retrieveLocation()
}
}
}
}
func retrieveLocation(){
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(addr, completionHandler: {(placemarks, error) -> Void in
if((error) != nil){
print("Error", error)
}
if let placemark = placemarks?.first {
let coordinates = PFGeoPoint(location: placemark.location)
if(whatever is true){
append the name and address into an array. This is the part where I just get repeats of the LATEST imported object.
}
}
})
}
This should work if you use a local variable and pass this local variable to an implementation of retrieveLocation that takes a string as a parameter retrieveLocation(address: String)
let query = PFQuery(className: "Animals")
query.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error: NSError?) in
if(error == nil){
for object in objects!{
if let addressobj = object["Add"] as? NSDictionary{
if let address = addressobj["address"] as? String{
let newAddress = address
print("sdfadsf \(self.addr)")
}
}
if let name = object["Name"] as? String{
self.impname = name
print("rturrty \(self.impname)")
self.retrieveLocation(newAdress)
}
}
}
}
func retrieveLocation(address: String){
let geocoder = CLGeocoder()
geocoder.geocodeAddressString(address, completionHandler: {(placemarks, error) -> Void in
if((error) != nil){
print("Error", error)
}
if let placemark = placemarks?.first {
let coordinates = PFGeoPoint(location: placemark.location)
if(whatever is true){
append the name and address into an array. This is the part where I just get repeats of the LATEST imported object.
}
}
})
}
Problem seems to be that by the time self.addr is being used in the geocodeAddresString method, the for-loop has finished and thus overwritten all the previous values that were at one point individually held by self.addr. By using a local variable, it will be sure to use a unique value to geocodeAddressString each time it is executed
I am trying to capture the data from a json page, and store it in a database. I can currently get the artist and title from the json page. I am filling a array in my program with the values captured from data to avoid repeats. When run the for loop and get the values and put them in the Parsearray, an infinite loop starts. It does not end, a it's always being called. I know this because i see "called" being printed in the console multiple times and it does not stop. How do I fix this?
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated);
let query = PFQuery(className: "Pointer")
query.findObjectsInBackgroundWithBlock({
(objects: [AnyObject]?, error: NSError?) -> Void in
var objectIDs = objects as! [PFObject]
for i in 0...objectIDs.count-1{
self.Parsearray.append((objectIDs[i].valueForKey("title") as? String)!)
print(self.Parsearray)
print("called")
}
})
self.getSpotify()
}
func getSpotify(){
let searchTerm = "tgirish10"
var endpoint = NSURL(string: "<api URL>")
var data = NSData(contentsOfURL: endpoint!)
let task = NSURLSession.sharedSession().dataTaskWithURL(endpoint!) {(data, response, error) -> Void in
do {
if let jsonData = data,
let dict = try NSJSONSerialization.JSONObjectWithData(jsonData, options: []) as? NSDictionary,
let recent = dict["recenttracks"] as? NSDictionary,
let items = recent["track"] as? NSArray {
for item in items {
if let spotifytitle = item["name"] as? String {
print("title: \(spotifytitle)")
if let spotifyartist = item["artist"]!!["#text"] as? String{
print("artist: \(spotifyartist)")
let query = PFQuery(className: "Pointer")
query.findObjectsInBackgroundWithBlock({
(objects: [AnyObject]?, error: NSError?) -> Void in
var objectIDs = objects as! [PFObject]
for i in 0...objectIDs.count-1{
self.Parsearray.append((objectIDs[i].valueForKey("title") as? String)!)
print(self.Parsearray)
print("called")
}
if self.Parsearray.contains(spotifytitle){
print("already in db")
}else{
let objectPointer = PFObject(className: "Pointer")
objectPointer["title"] = spotifytitle
objectPointer["user"] = PFUser.currentUser()
objectPointer["artist"] = spotifyartist
objectPointer.saveInBackgroundWithBlock({ (success: Bool, error: NSError?) -> Void in
if(error != nil){
print(error)
}else{
print("saved")
}
})
}
})
}
}
}
}
} catch let jsonError as NSError {
print(jsonError)
}
}
task.resume()
}
Beyond the issues in the comments above, the main problem is that your code does the following:
get some data from Spotify
for each track, get all Pointer objects from Parse
for each of those objects, add title to parseArray (so after a while, the list will contain multiple copies of all titles)
then check if title of the track is in this list
So the loop does not seem to be infinite, but you are unnecessarily adding a lot of data.
Two options:
move the loading of the titles outside of the loop on tracks
or use a PFQuery which only returns Pointer objects which match, and just check if the returned list is empty or not
Well I'm trying to save the results of a query and when I try to save it in an array it just doesn't do it.
Here's my code:
let query = PFUser.query()
query?.orderByDescending("puntaje")
query?.limit = 50
query?.findObjectsInBackgroundWithBlock({
(users, error) -> Void in
if let objects = users {
for object in objects {
self.usernames.removeAll(keepCapacity: true)
self.scores.removeAll(keepCapacity: true)
if let user = object as? PFUser {
print(user.username)
self.usernames.append(user.username!)
self.scores.append((user["puntaje"] as? Int)!)
}
}
}
print(self.usernames.count)
})
while printing user.username appears all the usernames.
and in the print it shows that I have 0 usernames.
You need to move
self.usernames.removeAll(keepCapacity: true)
self.scores.removeAll(keepCapacity: true)
above the "for" loop. Right under
if let objects = users {
I found another way.
I used:
let query = PFUser.query()
query?.orderByDescending("puntaje")
query?.limit = 50
do {
if let users = try query?.findObjects() {
for user in users as! [PFUser] {
let name = user.username!
self.usernames.append(name)
self.scores.append((user["puntaje"] as? Int)!)
}
}
}catch {
print(error)
}
this is a part of code I found on StackOverflow.
It was working in Swift 1.2
Why this code is not working anymore in swift 2:
geoCoder.reverseGeocodeLocation(location, completionHandler: { (placemarks, error) -> Void in
let placeArray = placemarks as [CLPlacemark] // !!! ERROR HERE !!!
// Place details
var placeMark: CLPlacemark!
placeMark = placeArray[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)
}
})
Error is GetLocationViewController.swift:67:41: '[CLPlacemark]?' is not convertible to '[CLPlacemark]'
Looks like you need to unwrap the placemarks array (implicitly or optional chaining) before assigning it to a type of [CLPlacemarks]
For your example, you should use optional chaining so
if let validPlacemark = placemarks?[0]{
let placemark = validPlacemark as? CLPlacemark;
}
Than place all your logic inside the braces so if it finds a valid placemark array, it will execute your desired commands. If not, it will do nothing or you can handle it however you please
placemarks is not guaranteed to have a value, you could do this:
let placeArray: [CLPlacemark] = placemarks as? [CLPlacemark] ?? []
Which reads as,
if placemarks can be casted as [CLPlacemark], then do so. Otherwise, assign an empty array.
Here's this code in practice:
I'm realizing now that you're on Xcode 7! It's even easier there, all you need is this:
let placeArray = placemarks ?? []
In Xcode 7.0, Objective-C has generic arrays, so your placemarks array is no longer [AnyObject]? but is now [CLLocation]?. You don't need to cast the array, you can just unwrap the optional. With the addition of guard statements, your completion block is now as simple as:
geocoder.reverseGeocodeLocation(location) { placemarks, error in
guard let placemarks = placemarks else { print(error); return; }
guard let placemark = placemarks.first else { return; }
// ...
}
if let pm = placemarks?[0]{
// Example: get the country
print("Your country: \(pm.country!)")
}
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.)