Why is the method being run at the end? - ios

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

Related

Retrieve multiple Parse Images(Swift)

Below is an example of how i would typically retrieve images from my Parse.com. I have now run into the situation where i would like to retrieve 20+ images from Parse but i am looking for a more efficient way to do so. Please can someone explain how to implement this in code and how i should store the 20+ PFFiles in Parse?
func loadData(){
let findDataParse = PFQuery(className: "JobListing")
findDataParse.findObjectsInBackgroundWithBlock{
(objects: [PFObject]?, error: NSError?) -> Void in
if (error == nil) {
for object in objects! {
let userImageFile = object["ImageOne"] as! PFFile
let userImageFile1 = object["ImageTwo"] as! PFFile
let userImageFile2 = object["ImageThree"] as! PFFile
userImageFile.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
let listingImage1 = UIImage(data:imageData!)
userImageFile1.getDataInBackgroundWithBlock {
(imageData1: NSData?, error1: NSError?) -> Void in
let listingImage2 = UIImage(data:imageData1!)
userImageFile2.getDataInBackgroundWithBlock {
(imageData2: NSData?, error1: NSError?) -> Void in
let listingImage3 = UIImage(data:imageData2!)
self.flyerImageLarge1.image = listingImage1
self.flyerImageLarge2.image = listingImage2
self.flyerImageLarge3.image = listingImage3
}}}}}}}
You can use ParseUI for cleaner and more efficient code.
To do so, add the ParseUI framework.
Then, click the image and change the "class" to PFImageView.
You can see this here.
Once you do that, you can easily set the PFImageView's image:
if let myServerImage = object.valueForKey("imageFromUser") as? PFFile {
self.myImage.file = myServerImage
self.myImage.loadInBackground()
}
Where myServerImage is the image you are retrieving from the Parse server and myImage is the image in your storyboard.
In your case it will be something like this:
func loadData(){
let findDataParse = PFQuery(className: "JobListing")
findDataParse.findObjectsInBackgroundWithBlock{
(objects: [PFObject]?, error: NSError?) -> Void in
if (error == nil) {
if let myServerImage = object.valueForKey("ImageOne") as? PFFile {
self.flyerImageLarge1.file = myServerImage
self.flyerImageLarge1.loadInBackground()
}
if let myServerImage = object.valueForKey("ImageTwo") as? PFFile {
self.flyerImageLarge2.file = myServerImage
self.flyerImageLarge2.loadInBackground()
}
if let myServerImage = object.valueForKey("ImageThree") as? PFFile {
self.flyerImageLarge3.file = myServerImage
self.flyerImageLarge3.loadInBackground()
}
}
}
I recommend adding the if let statement so that you don't get an error when an image doesn't exist.
Please forgive me if what I am saying is obvious or already considered. I'm not completely familiar with what you are doing, but it looks applicable.
Assuming you are not using it already, you will need to use a recursive function. Basically, a function that calls itself until the end condition.
I'm not familiar with your code so I'll demonstrate with a simple example in JavaScript:
/* A "public" function that your program will call */
function getStuff(total)
{
//quick positive check
if (total > 0)
{
//start and pass in an empty array
return _getStuffRecursion(total, []);
}
else
{
//total is not positive, return empty array
return [];
}
}
/* A "private" function that will do the recursion */
function _getStuffRecursion(total, resultsArray)
{
//do work this is where you would call your function that does the work.
var someResource = Math.random();
//add work to the array collected so far
resultsArray.push(someResource);
//change count
var newTotal = total - 1;
//check condition
if (newTotal > 0)
{
//recursive condition, go to the next level down and pass in what is collected so far
return _getStuffRecursion(newTotal, resultsArray)
}
else
{
//end condition met, just return the array with everything collected from the upper levels
return resultsArray;
}
}
/* Start */
//get started by calling the "public" function
var results = getStuff(20);
//print it to console
console.log(results);
If this solution works, I'm sure you can adapt it to Parse.

Swift Parse - Nested Calls Issue

In my app, I need to load user details in different view controllers. The details I load are not necessarily of the user that is currently signed in (which could be retrieved from currentUser, but sometimes are of other users depending on scenario.
So far I find myself whenever presented with the above need, I do two queries: First to load the details (e.g. name, phone, address, etc.) and second to load the profile image of that user.
So I end up with a nested call such as the below:
let query = PFQuery(className: "_User")
query.whereKey("appUsername", equalTo: self.friendObject.username!)
query.findObjectsInBackgroundWithBlock { (results, error) -> Void in
let data = results as [PFObject]!
if(error == nil)
{
self.friendObject.name = data[0]["name"] as? String
self.friendObject.profilePic = data[0]["ProfilePic"] as? UIImage
self.nameLabel.text = self.friendObject.name
self.friendObject.objectId = data[0].objectId! as String
self.getProfilePicture(self.friendObject.username!) { (result)->Void in
self.profilePicImageView.image = result
}
}else{
print("Error retrieving user details - try again")
}
}
and here is the definition of the getProfilePicture function:
func getProfilePicture(username: String, completion: (result: UIImage) -> Void)
{
var tempImage:UIImage? = UIImage(named: "sample-qr-code.png")!
let query: PFQuery = PFQuery(className: "_User")
query.whereKey("appUsername", equalTo: username)
query.findObjectsInBackgroundWithBlock {
(objects:[PFObject]?, error:NSError?) -> Void in
for object in objects! {
if(object["ProfilePic"] != nil)
{
let imageFiles = object["ProfilePic"] as! PFFile
imageFiles.getDataInBackgroundWithBlock({
(imageData: NSData?, error: NSError?) -> Void in
if (error == nil) {
tempImage = UIImage(data:imageData!)!
let temp2Image: UIImage = Toucan(image: tempImage!).resize(CGSize(width: 100, height: 150)).maskWithEllipse(borderWidth: 3, borderColor: UIColor.whiteColor()).image
completion(result: temp2Image)
}
})
}else{
let invalidImage = UIImage(named: "Contacts-100.png")
completion(result: invalidImage!)
}
}
}
}
The ProfilePic column in the User parse class is of File type. How can I optimize this so that I only do one call to load image and details (given that requests should be minimized as much as possible).
Thanks,
Make your imageView a PFImageView and then you can set the appropriate file of the PFImageView like self.friendObject.profilePic = data[0]["ProfilePic"] as? PFFile and then you can do self.profilePicImageView.file = self.friendObject.profilePic and then self.profilePicImageView.loadInBackground

Why is it in an infinite loop?

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

Check if a row exists in Parse, if it does update a column in the row instead of creating a new row each time. Swift

I have a className called SearchPreferences and it is empty until the current user makes a selection. When they make a selection a new row is created in this class with the updated info. The problem is if the user goes back and makes another selection I am creating a new row again instead of just updating the column. Here is the code that is saving the info but on a new row:`
let music = PFObject(className: "SearchPreferences")
music["music"] = table_data[indexPath.row]
// music["user"] = PFUser.currentUser()!.username!
music.saveInBackgroundWithBlock{(success, error) -> Void in
if error == nil {
music.saveInBackground()
print("success")
} else {
print("error")
}
}
`
All I can find is SQL and PHP online help. I tried the code below to call objId but I don't know it as its empty so it returns the below error.
The code below returns the error
No results matched the query. (Code: 101, Version: 1.7.5)
let query = PFQuery(className:"SearchPreferences")
query.getObjectInBackgroundWithId("musicSearch") {
(searchPreference: PFObject?, error: NSError?) -> Void in
if error != nil {
if let searchPreference = searchPreference {
searchPreference["musicSearch"] = self.table_data[indexPath.row]
searchPreference.saveInBackground()
if error == nil {
query.whereKeyDoesNotExist("musicSearch")
let searchPreference = PFObject(className: "SearchPreferences")
searchPreference["musicSearch"] = self.table_data[indexPath.row]
searchPreference.saveInBackgroundWithBlock{(success, error) -> Void in
The same can be send for this attempt:
var query = PFQuery(className:"SearchPreferences")
query.getObjectInBackgroundWithId("musicSearch") {
(searchPreference: PFObject?, error: NSError?) -> Void in
if error != nil {
print(error)
} else if let searchPreference = searchPreference {
searchPreference["musicSearch"] = self.table_data[indexPath.row]
searchPreference.saveInBackground()
}
}
I am trying to figure out how to either before running the query check if it is empty and if it is carry out my initial query. Parse docs only tell you how to save to classname _User not a second classname.
Here is an example on duplicated record update from parse community, you can use the same method to apply it with your code.
let adventureQuery = PFQuery(className: “Class Name“)
adventureQuery.limit = 1000
adventureQuery.addDescendingOrder(“Column Name”)
adventureQuery.getFirstObjectInBackground { (Success, error) in
Success?.setValue(self.toolsTitleTextField.text, forKey: "toolsTitle")
Success?.setValue(self.locationTextField.text, forKey: "location")
Success?.setValue(self.dateTextField.text, forKey: "createrDate")
Success?.saveInBackground(block: { (success, error) in
if (success){
Utility.showAlert("Success!", message: "Insert SuccessFully", viewController: self)
}
else{
let viewController = self.storyboard?.instantiateViewController(withIdentifier: "") as! ViewController
self.navigationController?.pushViewController(viewController, animated: true)
}
})
}

Retrieve object from Parse with Swift

I'm developing a simple iOS application. It's a simple quiz app. I'm using Parse to store my questions, answers etc. I've read all the documentation and cannot find why this code to retrieve an object is not working.
var query = PFQuery(className: "Test_Questions")
query.getObjectInBackgroundWithId("cJzv2JdMej", block: {
(questionObject: PFObject?, error: NSError?) -> Void in
let thisQuestion = questionObject["question"] as! String
//Error here: AnyObject is not convertible to String
})
Your help would be much appreciated!
Console Output:
Optional(What statement must come before an else statement?)
You should try this instead:
var query = PFQuery(className: "Test_Questions")
query.getObjectInBackgroundWithId("cJzv2JdMej", block: {
(questionObject: PFObject?, error: NSError?) -> Void in
if error == nil {
println(questionObject)
//if there's info in the console you know the object was retrieved
let thisQuestion = questionObject["question"] as? String
} else {
println("Error occurred retrieving object")
}
})
If that doesn't work you can also try let thisQuestion = questionObject.valueForKey("question"). Also make sure that Parse is actually returning an object by adding a print statement such as println(questionObject) after it has been retrieved so that you know Parse is returning an object.
Got it!
var query = PFQuery(className: "Test_Questions")
query.getObjectInBackgroundWithId("cJzv2JdMej", block: {
(questionObject: PFObject?, error: NSError?) -> Void in
let thisQuestion: AnyObject! = questionObject!.valueForKey("question")
self.question.text = thisQuestion as? String
})

Resources