I have a function that is called in the viewDidLoad of my collectionView that loads content from my server into the view:
// Load Books Class
func loadBooks() {
var query = PFQuery(className: "Books")
query.findObjectsInBackgroundWithBlock { (objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
let bookObjects = objects as! [PFObject]
for (index, object) in enumerate(bookObjects) {
self.books.append(Book(pfBook: object))
}
}else if let secondMessage = error?.userInfo?["error"] as? String
where secondMessage == "The Internet connection appears to be offline." {
self.failedMessage(secondMessage)
}
dispatch_async(dispatch_get_main_queue()){
self.collectionView!.reloadData()
}
}
}
And here is my function that is called when the collectionView is refreshed, I basically am attempting to just reload the items back to the view:
func startRefresh(){
dispatch_async(dispatch_get_main_queue()) {
self.loadBooks()
println("loading complete!")
self.refreshControl.endRefreshing()
}
}
However when I refresh, rather than loading the same content again, it loads twice the amount of content.. sort of like calling the load method on top of the load method, rather than re-executing it.. looking at my logic, what should I do to fix the issue?
You need to clear your array before you start adding items. Add self.books.removeAll() to your code, like following:
func loadBooks() {
var query = PFQuery(className: "Books")
query.findObjectsInBackgroundWithBlock { (objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
self.books.removeAll()
let bookObjects = objects as! [PFObject]
for (index, object) in enumerate(bookObjects) {
self.books.append(Book(pfBook: object))
}
}else if let secondMessage = error?.userInfo?["error"] as? String
where secondMessage == "The Internet connection appears to be offline." {
self.failedMessage(secondMessage)
}
self.collectionView!.reloadData()
}
}
Edit: Since Parse callbacks are executed on main thread, you don't need the dispatch_async part of your code.
You should remove all children of self.books
self.books.removeAll() before self.books.append(Book(pfBook: object))
Related
each cell displays the according comments and photo of the user logged-on. They are loaded with parse.
Now you want to Löschen the button deletes the photo and the comments.
Unfortunately this does not work. Wen I click on the button nothing happens
Unfortunately I understand little of swift and can't get on the solution
The query works, and the app displays the photos and Commons.The query and post code:
override func viewDidLoad() {
super.viewDidLoad()
super.viewDidLoad()
let query = PFQuery(className: "Post")
query.whereKey("username", equalTo: PFUser.current()?.username)
query.findObjectsInBackground(block: { (object, error) in
if let posts = object {
for post in posts{
print(posts)
self.comments.append(post["message"] as! String)
self.imageFile.append(post["imageFile"] as! PFFile)
self.tableView.reloadData()
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
})
}
And here of the "delete"function code that I have tried:
#IBAction func remove(_ sender: Any) {
print("Entered remove")
let query = PFQuery(className: "Post")
query.whereKey("username", equalTo: PFUser.current()?.username)
query.findObjectsInBackground(block: { (object, error) in
if let posts = object {
print(posts)
for post in posts{
print(posts)
if let message = post["message"] as? String, let image = post["imageFile"] as? PFFile {
print("message and image read", message, image)
if let messageIndex = self.comments.index(of: message), let imageIndex = self.imageFile.index(of:image) {
self.comments.remove(at: messageIndex)
self.imageFile.remove(at: imageIndex)
}
}
self.tableView.reloadData()
}
DispatchQueue.main.async {
self.tableView.reloadData()
}
}
})
}
The output:
I don't get an error message and nothing is deleted.
Thank you for your help
You do not have access to your current index where and object ids.
So based on that you can remove easy.
The more easy way to implement the delete function is to have an array of objectId for your messages:
self.ids.append(post.objectId)
And when you want to delete it:
let query = PFQuery(className: "Post")
query.whereKey("objectId", equalTo: self.ids.index(of: indexPath.row))
// Make a query in background to only get the object that you want to delete
query.getFirstObjectInBackgroundWithBlock {
(object: PFObject?, error: NSError?) -> Void in
if error != nil || object == nil {
print("The getFirstObject request failed.")
} else if let object = object {
print("Successfully retrieved the object.")
object.deleteInBackground()
}
}
Having different arrays representing the same object is not really good to do. So a better way to handle you problem is have only one array for your post
When you fetch it you can do something like that:
guard let user = PFUser.current() else { return }
let query = PFQuery(className: "Post")
query.whereKey("username", equalTo: user.username)
query.findObjectsInBackground(block: { (posts, error) in
if let posts = posts {
self.posts = posts
}
})
With this way when you want to delete it in the remove function:
if indexPath.row < self.posts.count {
self.posts[indexPath.row].deleteInBackground()
}
new programmer trying to learn Swift and I'm trying to set my app up to only show the current users post on a parse photosharing database. The first method here in theory should add the current user to the "followingWho" array.
override func viewDidLoad() {
super.viewDidLoad()
let userQuery = PFUser.query()
userQuery?.whereKey("username", equalTo: PFUser.currentUser()!.username!)
userQuery?.findObjectsInBackgroundWithBlock ({
(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
//no error
if let objects = objects {
for object in objects {
let followingWho = object["followingWho"] as! NSArray!
self.loadData(followingWho)
}
}
} else {
//error
NSLog("Error")
}
})
self.tableView.reloadData()
}
then my second method here should display the posts and filter out all but the current users
func loadData(followingWho: NSArray) {
let query = PFQuery(className: "Posts")
query.whereKey("addedBy", containedIn: followingWho as! [PFObject])
query.orderByDescending("createdAt")
query.findObjectsInBackgroundWithBlock {
(posts: [PFObject]?, error: NSError?) -> Void in
if (error == nil) {
//No error
//let posts = posts as! [PFObject]
if let posts = posts {
for post in posts {
self.images.append(post["Image"] as! PFFile)
self.imageCaptions.append(post["Caption"] as! String)
self.imageDates.append(post["date"] as! String)
self.imageUsers.append(post["addedby"] as! String)
}
self.tableView.reloadData()
}
} else {
//error
NSLog("Error")
}
}
}
But nothing seems to be working. The code compiles and runs but it seems to only pull up a blank screen loading none of the posts.
This could be the problem, although if it is not the problem, it is certainly a problem: you are trying to update the UI from a background thread. findObjectsInBackgroundWithBlock calls its completion block in the background, and updating UI from the background is a big no-no. Instead, call your UI updates on the main thread:
dispatch_async(dispatch_get_main_queue(), {
//Do your UI updates here
})
This is my initialisation of the array:
var restaurantsArray = [String]()
Here is the query function:
func loadRestaurants(){
let fetchRestaurantsQuery = PFQuery(className: "Restaurants")
fetchRestaurantsQuery.findObjectsInBackgroundWithBlock{ (objects: [PFObject]?, error: NSError?) -> Void in
if error == nil{
//after successfull fetch
print("b")
if let fetchedRestaurants = objects{
print("c")
for fetchedRestaurant in fetchedRestaurants{
print("a")
self.restaurantsArray.append(fetchedRestaurant.objectForKey("Name") as! String)
print(fetchedRestaurant.objectForKey("Name") as! String)
}
}
}else{
// Log details of the failure
print("Error: \(error!) \(error!.userInfo)")
}
}
}
For some reason, the code in the for-loop is never called. Any suggestions to fix this?
Since objects is declared as optional [PFObject]? the proper optional binding syntax is just
if let fetchedRestaurants = objects {
and fetchedRestaurant in fetchedRestaurant is pretty weird.
for fetchedRestaurant in fetchedRestaurants {
I am trying to build a chat application, but I have a problem with this code:
func loadData() {
let FindTimeLineData: PFQuery = PFQuery(className: "Message")
FindTimeLineData.findObjectsInBackgroundWithBlock { (objects: [AnyObject]!, NSError) -> Void in
self.MessagesArray = [String]()
for MessageObject in objects {
let messageText: String? = (MessageObject as! PFObject) ["Text"] as? String
if messageText != nil {
self.MessagesArray.append(messageText!)
}
}
}
}
I need to retrieve data from Parse, but the .findObjectsInBackgroundWithBlock method tells me that it cannot convert a value of type AnyObject into Void in. How can I resolve this problem? Thanks in advance.
Try it like this instead:
var query = PFQuery(className: "Message")
query.findObjectsInBackgroundWithBlock {
(remoteObjects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
print("Retrieved \(remoteObjects!.count) messages from server.")
self.MessagesArray = [String]() // By convention, you should name this "messagesArray" instead, and initialize it outside this method
for messageObject in remoteObjects {
if let messageText: String? = messageObject["Text"] as? String {
self.messagesArray.append(messageText)
}
}
} else {
print("Error: \(error!) \(error!.userInfo)")
}
}
(not properly proof-read, but you should be able to get it to work from this)
For the record, there are LOTS of duplicate questions with this problem - i know, as I had the same problem after converting Parse code to Swift 2.1.
So, please do a little more research before you post a question. Often, SO even hints at you similar questions as you are typing...
As for the answer, the Parse API doesn't force you to cast the object as AnyObject anymore in the completion block of a query, so it can look just like this:
query?.findObjectsInBackgroundWithBlock({ (objects, error) -> Void in
if let messages = objects {
for message in messages {
.... etc
I'm writing a function to query the class Photos with a given Object Id in order to download a photo and set it to the UIImageView "background." I've narrowed the issue down to "for object in objects!" which I've commented in the code below. This seems like standard practice for casting, but the code won't run past this point. It compiles and no errors are thrown, but it fails to print anything past the "for" line, much less set the background.
// set new background image
func imageSet(objId: String) {
var query : PFQuery = PFQuery(className: "Photos")
query.whereKey("objId", equalTo:objId)
query.findObjectsInBackgroundWithBlock {
(objects:[AnyObject]?, error:NSError?) -> Void in
if error == nil {
println("First query")
// last working line
for object in objects! {
println("Won't print here")
// won't pass this point
let userImageFile = object["image"] as! PFFile
userImageFile.getDataInBackgroundWithBlock {
(imageData: NSData?, error:NSError?) -> Void in
if error == nil {
println("Or here")
self.background.image = UIImage(data:imageData!)
}
}
}
}
else {
println("\(error)")
}
}
}
Any tips are greatly appreciated! Thanks!