Parse Query not working at first properly iOS Swift - ios

Im retrieving parse data to my app depending on the user location.
When i start app first sometimes it Prints "No City" but if i go to another view and comeback then it works properly.
also sometimes it shows me the result the otherway around
example : Only show results that where city name is "sydney" but it shows result that city name is not "sydney"
here is my code
func loadData(){
LiveFeedData.removeAllObjects()
var findLiveFeedData:PFQuery = PFQuery(className: "Spreads")
findLiveFeedData.whereKey("location", equalTo: city)
findLiveFeedData.whereKey("country", equalTo: country)
if city != ""{
findLiveFeedData.findObjectsInBackgroundWithBlock{
(objects:[AnyObject]?, error:NSError?)->Void in
if error == nil{
if let objects = objects as? [PFObject] {
for object in objects{
self.LiveFeedData.addObject(object)
}
}
let array:NSArray = self.LiveFeedData.reverseObjectEnumerator().allObjects
self.LiveFeedData = NSMutableArray(array: array)
self.tableView.reloadData()
}else{
println("Error")
}
}
}else{
println("No City")
}
}
override func viewDidAppear(animated: Bool) {
spinner.startAnimating()
//Location
self.locationManager.requestWhenInUseAuthorization()
self.locationManager.startUpdatingLocation()
self.loadData()
}

What you probably want to do is cal your loadData method in your delegate method that handles the reception of a new location, that way you can know that when the query gets called, it's only getting run when you know that both the city and the state are populated and not nil.

Related

Swift Get the Country and State of user and then change text on View Controller or Store in Core Data

Hello I am trying to assign an array of Strings to display in a scrollView based off of where a user is located.
I am working on an iOS app that sometimes displays info about THC(Weed). Based on certain states in the United States I can not display certain things about THC.
To my knowledge I know this is the only way I can get the users Country and State/Administrative area.
func storeStateAndCountry() {
let locationManager = CLLocationManager()
CLGeocoder().reverseGeocodeLocation(locationManager.location!) { (placemark, error) in
if error != nil {
print("error")
} else {
let place = placemark! as [CLPlacemark]
if place.count > 0 {
let place = placemark![0]
let display = self.displayTHC(country: place.country! , adminArea: place.administrativeArea!)
let phone = Phone(context: PersistenceServce.context)
phone.canDisplayTHC = display
PersistenceServce.saveContext()
}
}
}
}
Below I call store storeStateAndCounty() on a login screen but, I believe the closure is not letting me finish this but, I am not sure how to get around it.
override func viewDidLoad() {
super.viewDidLoad()
self.storeStateAndCountry()
self.navigationController?.navigationBar.isHidden = true
self.hideKeyboardWhenTappedAround()
}
Below you can see I am trying to store it in Core Data and then take the data back out in viewDidLoad. But nothing is being stored. This HomeVC is where the app crashes.
override func viewDidLoad() {
super.viewDidLoad()
self.navigationController?.interactivePopGestureRecognizer?.isEnabled = false
self.homeTableView.estimatedRowHeight = 50
let fetchRequest: NSFetchRequest<Phone> = Phone.fetchRequest()
do {
print("Starting a fetch")
let phone = try PersistenceServce.context.fetch(fetchRequest)
self.phone = phone
if self.phone[0].canDisplayTHC == true{//runtime error invalid index
self.titleArray = ["THC should display here", "THC should display here", "THC should display here", "THC should display here"]
}else{
self.titleArray = ["Is Microdosing Cannabis Really Effective?", "Another article with a different title","San Francisco hires Head of Cannabis","Another article with a different title"]
}
}catch{}
}
Any Ideas?

What to use instead of indexPath.row? for non-table?

I'm working with a mapview not a tableview, but I don't know what to use to replace indexPath.row.
I have a mapview with annotations, when the info button of an annotation is pressed I then query my CK database and return the record that has a name field matching the name of the annotation pressed. This returns an [CKRecord] with a single record, as there are no matching names.
At this point, with a tableview I would do the following to access the data...
let placeInfo = selectedData[indexPath.row]
let placeName = placeInfo.objectForKey("Name") as! String
let placeCity = placeInfo.objectForKey("City") as! String
However, since I'm not using a tableview, I don't have an indexPath to use. Since my [CKRecord] object only contains a single record, I thought I could replace indexPath.row with the array location of the record...
let placeInfo = selectedPlace[0] //also tried 1
That lines produces an Index out of range error.
I've tried everything that I know, and as you may imagine, I'm not exactly great at swift or programming in general at this point.
Here is the full mapView function I am using...
func mapView(mapView: MKMapView, annotationView: MKAnnotationView, calloutAccessoryControlTapped control: UIControl) {
let cloudContainer = CKContainer.defaultContainer()
let publicData = cloudContainer.publicCloudDatabase
let tappedPlace = annotationView.annotation!.title!! as String
let predi = NSPredicate(format: "Name = %#", tappedPlace)
let iquery = CKQuery(recordType: "Locations", predicate: predi)
publicData.performQuery(iquery, inZoneWithID: nil, completionHandler: {
(results, error) -> Void in
if error != nil {
print(error)
return
}
if let results = results {
print("Downloaded data for selected location for \(tappedPlace)")
NSOperationQueue.mainQueue().addOperationWithBlock() {
self.selectedPlace = results
}
}
})
let placeInfo = selectedPlace[0]
let placeName = placeInfo.objectForKey("Name") as! String
//returns Index out of range error for placeInfo line
//need data before segue
//performSegueWithIdentifier("fromMap", sender: self)
}
Your problem is that, you try to access selectedPlace before it is actually signed by your completion handler. Your 'publicData.performQuery' seems to be an asynchronous operation, and this means that, the control will come out from this call even before the completion handler gets executed(this is expected in case of an asynchronous call). And you reach the line immediately-
let placeInfo = selectedPlace[0]
But the data is not ready yet, and you get the exception. Now to solve this, move place info extraction, and perform segue code inside the completion handler as shown-
publicData.performQuery(iquery, inZoneWithID: nil, completionHandler: {
(results, error) -> Void in
if error != nil {
print(error)
return
}
if let results = results {
print("Downloaded data for selected location for \(tappedPlace)")
NSOperationQueue.mainQueue().addOperationWithBlock() {
self.selectedPlace = results
if(results.count > 0){
let placeInfo = selectedPlace[0]
let placeName = placeInfo.objectForKey("Name") as! String
//Do any other computations as needed.
performSegueWithIdentifier("fromMap", sender: self)
}
}
}
})
This should fix your problem.

how to properly check for an existing object in parse with swift

I've made a function that creates a "favorite" object back in parse with the tap of a favorite button on the UI :
//MARK: Create the favorite object
func createFavorite(){
let currentUser = PFUser.currentUser()
let currentBook = PFObject(withoutDataWithClassName: "Books", objectId: objectIdSelected)
let favorite = PFObject(className:"Favorites")
favorite["user"] = currentUser
favorite["books"] = currentBook
favorite.saveInBackgroundWithBlock {
(success: Bool, error: NSError?) -> Void in
if (success) {
// success
} else {
// There was a problem, check error.description
}
}
}
Now I am attempting to create a query that checks to see if a favorite object with those exact properties exists using the following logic, I've placed it in the viewDidLoad of a VC that shows a specific book:
//MARK: Check if there is a favorite object available
func isFavoritedByUser(){
let currentUser = PFUser.currentUser()
let currentBook = PFObject(withoutDataWithClassName: "Books", objectId: objectIdSelected)
let query = PFQuery(className:"Favorites")
query.whereKey("user", equalTo: currentUser!)
query.whereKey("books", equalTo: currentBook)
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if objects != nil {
print("this book was favorited by this user")
} else if objects == nil {
print("this book WAS NOT favorited by this user")
} else if error == nil {
// succeed
} else if error != nil {
// There was a problem, check error.description
}
}
}
However, even if no favorite object with those properties exists back in my parse class, it still prints "this book was favorited by this user".. what am i missing here?
The list existing is not proof. That simply proves that some kinda of error weren't observed.
You should check that there was no error first, then you should check that the list of objects has one item in it.
If the list has more than one item then you're creating duplicates and you should fix that...
What you're getting returned is an array, as long as there isn't an error. So, rather than checking for nil, you should be checking the content of the returned array (objects) to see if there is anything in there.
In Parse's guide, you can see what they recommend for this function call:
https://parse.com/docs/ios/guide#queries
Additionally, Parse recommends considering getFirstObjectInBackground instead if this query only ever needs to return a single object, so that might be something to consider as an alternative.
So the problem was i was checking to see if object was nil rather then checking for the count, here is the correct code:
//MARK: Check if there is a favorite object available
func isFavoritedByUser(){
let currentUser = PFUser.currentUser()
let currentBook = PFObject(withoutDataWithClassName: "Books", objectId: objectIdSelected)
let query = PFQuery(className:"Favorites")
query.whereKey("user", equalTo: currentUser!)
query.whereKey("books", equalTo: currentBook)
query.findObjectsInBackgroundWithBlock { (object, error) -> Void in
if error == nil {
if object?.count > 0{
print("this book was favorited by this user")
} else {
print("this book WAS NOT favorited by this user")
}
} else {
}
}
}
For iOS, you can just check the dictionary on an object like so:
rideParse["driver"] == nil
If it exists, then you will get the pointer. If it doesn't/you did not query to include the pointer, then it will just return nil.

Swift Parse - local datastore and displaying objects in a tableview

I am building and app that saves an object in the local datastore with parse. I then run a query to retrieve the objects that are in the local datastore and it is working fine. however, I would like to grab the object, and the contents in it, and set some labels in a table view cell based on the items that are stored in the parse local data store object. for example, i make an object with attributes like "objectID", "name", "date", "location". what i'd like to do is to have a table view on the home screen that displays the name, date, location ...etc. of each item that was saved in local datastore in labels in each cell.
i know that im saving it correctly:
// parse location object
let parseLighthouse = PFObject(className: "ParseLighthouse")
parseLighthouse.setObject(PFUser.currentUser()!, forKey: "User")
parseLighthouse["Name"] = self.placeTitle.text
parseLighthouse["Note"] = self.placeNote.text
parseLighthouse["Locality"] = self.placeDisplay.text!
parseLighthouse["Latt"] = self.map.region.center.latitude
parseLighthouse["Longi"] = self.map.region.center.longitude
parseLighthouse["LattDelta"] = 0.5
parseLighthouse["LongiDelta"] = 0.5
parseLighthouse["Date"] = dateInFormat
parseLighthouse.pinInBackground()
parseLighthouse.saveInBackgroundWithBlock { (success: Bool, error: NSError?) -> Void in
println("Object has been saved. ID = \(parseLighthouse.objectId)")
}
and when i run the query, im able to access the attributes by running println(object.objectForKey("Name"))
func performQuery() {
let query = PFQuery(className: "ParseLighthouse")
query.fromLocalDatastore()
query.whereKey("User", equalTo: PFUser.currentUser()!)
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error == nil {
// The find succeeded.
println("Successfully retrieved \(objects!.count) lighthouses.")
// Do something with the found objects
if let light = objects as? [PFObject] {
for object in light {
println(object.objectId)
println(object.objectForKey("Name"))
}
}
} else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
}
because when running the query, i get back the object id and name as expected.
Successfully retrieved 2 lighthouses.
Optional("A3OROVAMIj")
Optional(happy)
Optional("bbyqPZDg8W")
Optional(date test)
what I would like to do is grab the name field within the parse object local data store, and that be the name of the label on a cell in a table view controller.
i dont know how to access that info from the object, and set the label correctly.
does anyone know how this is possible?
It's always a good idea to avoid pointer lol ... so why not saving the userid or username with the specific object..
so change this line:
parseLighthouse.setObject(PFUser.currentUser()!, forKey: "User")
TO
parseLighthouse["username"] = PFUser.currentUser().username
Answer
NOW let's create a struct that contains the objectID and the Name outside of your Controller Class.
struct Data
{
var Name:String!
var id:String!
}
then inside of the Controller class, declare the following line of code globally
var ArrayToPopulateCells = [Data]()
Then your query function will look like :
func performQuery() {
let query = PFQuery(className: "ParseLighthouse")
query.fromLocalDatastore()
query.whereKey("User", equalTo: PFUser.currentUser()!)
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if error == nil {
// The find succeeded.
print("Successfully retrieved \(objects!.count) lighthouses.")
// Do something with the found objects
if let light = objects as? [PFObject] {
for object in light {
print(object.objectId)
print(object.objectForKey("Name"))
var singleData = Data()
singleData.id = object.objectId
singleData.Name = object["Name"] as! String
self.ArrayToPopulateCells.append(singleData)
}
}
} else {
// Log details of the failure
print("Error: \(error!) \(error!.userInfo)")
}
}
In the tableView numberOfRowinSection()
return ArrayToPopulateCells.count
In the cellForRowAtIndexPath()
var data = ArrayToPopulateCells[indexPath.row]
cell.textlabel.text = data.objectID
cell.detailLabel.text = data.Name
VOila that should be it

Parse findObjectsInBackgroundWithBlock error part not working

in my parse table there are columns. location and country. and if it is true data will load to the application.but the problem is, its never going to else part in
findObjectsInBackgroundWithBlock even wherekeys wrong. always print out This section calling line.
var findLiveFeedData:PFQuery = PFQuery(className: "Spreads")
findLiveFeedData.whereKey("location", equalTo: city)
findLiveFeedData.whereKey("country", equalTo: country)
findLiveFeedData.findObjectsInBackgroundWithBlock{
(objects:[AnyObject]?, error:NSError?)->Void in
if error == nil{
if let objects = objects as? [PFObject] {
for object in objects{
self.LiveFeedData.addObject(object)
}
}
println("This section calling")
let array:NSArray = self.LiveFeedData.reverseObjectEnumerator().allObjects
self.LiveFeedData = NSMutableArray(array: array)
self.tableView.reloadData()
}else{
println("Error") // This section never gets called
}
}
Even if you make a query with the wrong key it won't return an error, it will return an array with 0 entries. So the way to find out if nothing has been found it to use:
if objects != nil {
if objects!.count > 0 {
//You are safe to use the array
} else {
//Nothing was returned
}
}

Resources