The code below works expectedly but I was wondering if I was retrieving multiple parse images the best way possible. What the code below does is retrieve 3 different PFFile columns by calling findObjectsInBackgroundWithBlock 3 separate times. Can this be condensed? If so, how can I better refine my function?
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
}}}}}}}
It would be better if you used an array column to store your files in Parse because then you could use a loop, but you can retrieve the images in parallel.
The code below shows this, but doesn't actually do anything with the images because it isn't clear what you are trying to do; your code in your question assigns all of the retrieved images to a single UIImageView, so only the last image that is retrieved is actually used.
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!)
}
}
}
Related
Below the code is retrieving from parse.com. The code is working properly but does not query my data in order. Can someone explain why?
func loadData(){
let findDataParse = PFQuery(className: "JobListing")
findDataParse.whereKey("user", equalTo: PFUser.currentUser()!)
findDataParse.orderByAscending("createdAt")
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!)
let offers = Information(objectIDParam: object.objectId!, titleOfProjectParam: object["TitleOfProject"] as! String, categoryParam: object["Category"] as! String, viewsParam: object["Views"] as! String, image1Param: listingImage1!, image2Param: listingImage2!, image3Param: listingImage3!)
self.arrayOfOffers.append(offers)
for interests in object["InterestsArray"] as! NSArray{
self.arrayOfInterests.append(interests as! String)
}
self.i++
self.tableView.reloadData()
}}}}}}}
Here's what I would do:
Create a mutableArray
Fetch all the items at the same time
When a request is finished add it's object to the mutableArray and check mutableArray.count == numberOfRequests
When all downloads are finished sort the mutableArray
This would be much faster + easy to understand what's going on.
I am appending the username column and the image column to two different arrays from parse. I am then putting them into the collection view. I am anticipating that the username in the nameArray corresponds to the imageArray, but majority of the time they are in the wrong order. How do I get them to append into the array in the right order? i.e. User 1 has picture 1, user 2 has picture 2. username array = [User 1, User 2]. image array = [picture 1, picture 2].
func getFriendPicandName(){
let imagequery = PFQuery(className: "_User")
imagequery.findObjectsInBackgroundWithBlock {( objects: [AnyObject]?, error: NSError?) -> Void in
// for object in objects!{
var user = PFUser.currentUser()
let relation = user!.relationForKey("Friendship")
relation.query()!.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
for object in objects!{
let userPic = object["ProPic"] as! PFFile
userPic.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in
if(error == nil){
let image = UIImage(data: imageData!)
self.arrayOfFriends.append(image!)
print(self.arrayOfFriends)
}
dispatch_async(dispatch_get_main_queue()) {
self.collectionView.reloadData()
}
})
}
}
}
var query = PFQuery(className: "_User")
query.findObjectsInBackgroundWithBlock({
(objects: [AnyObject]?, error: NSError?) -> Void in
var user = PFUser.currentUser()
let relations = user!.relationForKey("Friendship")
relations.query()!.findObjectsInBackgroundWithBlock{
(objects: [AnyObject]?, error: NSError?) -> Void in
var objectIDs = objects as! [PFObject]
for i in 0...(objectIDs.count){
self.arrayOfFriendsNames.append(objectIDs[i].valueForKey("username") as! String)
print(self.arrayOfFriendsNames)
}
dispatch_async(dispatch_get_main_queue()) {
self.collectionView.reloadData()
}
}
})
}
func collectionView(collectionView: UICollectionView, numberOfItemsInSection section: Int) -> Int {
return arrayOfFriendsNames.count
}
func collectionView(collectionView: UICollectionView, cellForItemAtIndexPath indexPath: NSIndexPath) -> UICollectionViewCell {
let cell: friendcellView = collectionView.dequeueReusableCellWithReuseIdentifier("friendcell", forIndexPath: indexPath) as! friendcellView
cell.friendname.text = arrayOfFriendsNames[indexPath.item]
cell.friendpic.image = arrayOfFriends[indexPath.item]
cell.friendpic.layer.cornerRadius = cell.friendpic.frame.size.width/2;
cell.friendpic.clipsToBounds = true
return cell
}
You should not call your query twice, I would imagine your for loop to look something like this:
for object in objects!{
let userPic = object["ProPic"] as! PFFile
userPic.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in
if(error == nil){
let image = UIImage(data: imageData!)
self.arrayOfFriends.append(image!) // Add image here
print(self.arrayOfFriends)
}
self.arrayOfFriendsNames.append(object.valueForKey("username") as! String) // Add Name here
}
I don't know much about swift but I combined you both function into one...i hope it will help you...
func getFriendPic(){
let imagequery = PFQuery(className: "_User")
imagequery.findObjectsInBackgroundWithBlock {( objects: [AnyObject]?, error: NSError?) -> Void in
// for object in objects!{
var user = PFUser.currentUser()
let relation = user!.relationForKey("Friendship")
relation.query()!.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
var objectIDs = objects as! [PFObject]
for i in 0...(objectIDs.count){
self.arrayOfFriendsNames.append(objectIDs[i].valueForKey("username") as! String)
print(self.arrayOfFriendsNames)
}
for object in objects!{
let userPic = object["ProPic"] as! PFFile
userPic.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in
if(error == nil){
let image = UIImage(data: imageData!)
self.arrayOfFriends.append(image!)
print(self.arrayOfFriends)
}
dispatch_async(dispatch_get_main_queue()) {
self.collectionView.reloadData()
}
})
}
}
}
}
I am attempting to fetch data from parse.com into my custom cell which is full of strings and images. I believe I am either retrieving my PFFile incorrectly from parse.com or I am retrieving the PFFile correctly but converting the file to UIImage improperly. The error i am receiving is going on within the loadData() function. It reads as follows: could not find an overload for 'init' that accepts the supplied arguments
Information
//Used to set custom cell
class Information {
var partyName = ""
var promoterName = ""
var partyCost = ""
var flyerImage: UIImage
var promoterImage: UIImage
init(partyName: String, promoterName: String, partyCost: String, flyerImage: UIImage, promoterImage: UIImage) {
self.partyName = partyName
self.promoterName = promoterName
self.partyCost = partyCost
self.flyerImage = flyerImage
self.promoterImage = promoterImage
}
}
Parse fetch function
func loadData() {
var findDataParse:PFQuery = PFQuery(className: "flyerDataFetch")
findDataParse.findObjectsInBackgroundWithBlock{
(objects: [AnyObject]?, error: NSError?) -> Void in
if (error == nil) {
for object in objects! {
var eventImage0 : UIImage
var eventImage10 : UIImage
let userImageFile = object["partyFlyerImage"] as! PFFile
userImageFile.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
let eventImage = UIImage(data:imageData!)
eventImage0 = eventImage!
}
}
let userImageFile1 = object["partyPromoterImage"] as! PFFile
userImageFile1.getDataInBackgroundWithBlock {
(imageData1: NSData?, error1: NSError?) -> Void in
if error1 == nil {
let eventImage1 = UIImage(data:imageData1!)
eventImage10 = eventImage1!
}
}
//Error below
var party1 = Information(partyName: (object["partyName"] as? String)!, promoterName: (object["partyPromoterName"] as? String)!,partyCost: (object["partyCost"] as? String)!, flyerImage: UIImage(data: eventImage0)!, promoterImage: UIImage(data: eventImage10)!)
self.arrayOfParties.append(party1)
}
}
self.tableView.reloadData()
}
}
You are fetching data from Parse in background, but processing on main thread. Try this:
func loadData() {
var findDataParse:PFQuery = PFQuery(className: "flyerDataFetch")
findDataParse.findObjectsInBackgroundWithBlock{
(objects: [AnyObject]?, error: NSError?) -> Void in
if (error == nil) {
for object in objects! {
var eventImage0 : UIImage
var eventImage10 : UIImage
let userImageFile = object["partyFlyerImage"] as! PFFile
userImageFile.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
let eventImage = UIImage(data:imageData!)
eventImage0 = eventImage!
let userImageFile1 = object["partyPromoterImage"] as! PFFile
userImageFile1.getDataInBackgroundWithBlock {
(imageData1: NSData?, error1: NSError?) -> Void in
if error1 == nil {
let eventImage1 = UIImage(data:imageData1!)
eventImage10 = eventImage1!
var party1 = Information(partyName: (object["partyName"] as? String)!, promoterName: (object["partyPromoterName"] as? String)!, partyCost: (object["partyCost"] as? String)!, flyerImage: UIImage(data: eventImage0)!, promoterImage: UIImage(data: eventImage10)!)
self.arrayOfParties.append(party1)
}
}
}
}
}
}
self.tableView.reloadData()
}
}
Your fetching your images Asynchronously and creating your Information cell Synchronously. So when you create the cell, the images are likely not loaded and your are in effect sending nil to the constructor for the cell.
In the following code you are retrieving the image data async:
userImageFile.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
let eventImage = UIImage(data:imageData!)
eventImage0 = eventImage!
}
}
So when you assign the image data to eventImage0, the call to the Information cell initializer has probably already happened.
You need to modify the code to instead of passing the image into the cell view initializer, allow you to access the UIImageview from the Information cell, so that when the background image loads complete you can simply set the loaded image into that UI/PF/ImageView.
I am getting a fatal error: unexpectedly found nil while unwrapping an optional value. I get this error after the code gets to self.imageArray.append(image!) from this code:
func retrieveImages()
{
var query = PFQuery(className: "Maps")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil && objects != nil {
let objects = objects as! [PFObject]
for object in objects {
let imageFile = object["imageFile"] as! PFFile
imageFile.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if imageData != nil {
let imageData:NSData! = imageData
let image = UIImage(data: imageData)
self.imageArray.append(image!)
}
}
}
}
}
}
}
I have checked each line of code and image does not become nil until i try to append it to the end of the imageArray.
like previous mentioned, you are reassigning a lot of variables. Try you if-let statements on the optional variables like this:
func retrieveImages(){
var query = PFQuery(className: "Maps")
query.findObjectsInBackgroundWithBlock { (objects: [AnyObject]?, error: NSError?) in
if let objects = objects as? [PFObject] where error == nil {
for object in objects {
if let imageFile = object["imageFile"] as? PFFile {
imageFile.getDataInBackgroundWithBlock { (imageData: NSData?, error: NSError?) in
if let imageData = imageData, let image = UIImage(data: imageData) {
self.imageArray.append(image)
}
}
}
}
}
}
}
All,
I have a simple class in the parse backend - which feeds a uitableview. The data is stored in Arrays. I guess because all parse data is done in the background sometimes data gets downloaded before others. I have a very mixed up tableview. With images in the wrong cell etc. Also the custom cells are sometimes not showing up at all and I need to do a refresh.
Here is my code that I used to download all the data from parse and add into the arrays.
Can you have a look please and suggest a way to do this.
Also how can I add a placeholder image , before the original image comes up.
Also this code is in my ViewDidLoad, would it be better in an earlier function, hopefully so i don't have to relsoaddata on the tableview..
var query = PFQuery(className:"TableViewData")
query.includeKey("EventLoc")
query.findObjectsInBackgroundWithBlock{
(objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
for object in objects {
let thumbNail = object["backgroundImage"] as PFFile
thumbNail.getDataInBackgroundWithBlock({
(imageData: NSData!, error: NSError!) -> Void in
if (error == nil) {
dispatch_async(dispatch_get_main_queue()) {
let image = UIImage(data:imageData)
self.CellBackgroundImage.append(image!)
}
}
})
var VenueLocation = object["EventLoc"] as PFObject!
VenueLocation.fetchIfNeededInBackgroundWithBlock {
(VenueLocation: PFObject!, error: NSError!) -> Void in
dispatch_async(dispatch_get_main_queue()) {
let VenueLocationTitle = VenueLocation["EventLocation"] as NSString
self.EventLocationArray.append(VenueLocationTitle)
}
}
let eventiconimage = object["EventIcon"] as PFFile
eventiconimage.getDataInBackgroundWithBlock({
(imageData: NSData!, error: NSError!) -> Void in
if (error == nil) {
dispatch_async(dispatch_get_main_queue()) {
let image = UIImage(data:imageData)
self.EventIconImageArray.append(image!)
}
}
})
dispatch_async(dispatch_get_main_queue()) {
self.TitleArray.append(object["EventTitle"] as String)
self.EventPriceArray.append(object["EventPrice"] as String)
self.EventStartDate.append(object["EventStartDate"] as NSDate)
self.EventEndDate.append(object["EventEndDate"] as NSDate)
self.tableView.reloadData()
}
You need to use serial queue then your array data will be in order. Whats happening because of concurrent task the data is not appended in order
var backgroundQueue:dispatch_queue_t = dispatch_queue_create("SerialQueue", DISPATCH_QUEUE_SERIAL)
var query = PFQuery(className:"TableViewData")
query.includeKey("EventLoc")
query.findObjectsInBackgroundWithBlock{
(objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
for object in objects {
dispatch_async(backgroundQueue, { () -> () in
let thumbNail = object["backgroundImage"] as PFFile
thumbNail.getDataInBackgroundWithBlock({
(imageData: NSData!, error: NSError!) -> Void in
if (error == nil) {
dispatch_async(dispatch_get_main_queue()) {
let image = UIImage(data:imageData)
self.CellBackgroundImage.append(image!)
}
}
})
var VenueLocation = object["EventLoc"] as PFObject!
VenueLocation.fetchIfNeededInBackgroundWithBlock {
(VenueLocation: PFObject!, error: NSError!) -> Void in
dispatch_async(dispatch_get_main_queue()) {
let VenueLocationTitle = VenueLocation["EventLocation"] as NSString
self.EventLocationArray.append(VenueLocationTitle)
}
}
let eventiconimage = object["EventIcon"] as PFFile
eventiconimage.getDataInBackgroundWithBlock({
(imageData: NSData!, error: NSError!) -> Void in
if (error == nil) {
dispatch_async(dispatch_get_main_queue()) {
let image = UIImage(data:imageData)
self.EventIconImageArray.append(image!)
}
}
})
dispatch_async(dispatch_get_main_queue()) {
self.TitleArray.append(object["EventTitle"] as String)
self.EventPriceArray.append(object["EventPrice"] as String)
self.EventStartDate.append(object["EventStartDate"] as NSDate)
self.EventEndDate.append(object["EventEndDate"] as NSDate)
self.tableView.reloadData()
}
});
}
}