The line in question is "let productImageFile = productData!["productImage"] as! PFFile" which gives me the error "fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)". The only answers I've found have involved making sure I am not trying to unwrap explicitly defined optionals (I think that's the term), but I messed around with the optionals, and which I unbind and when, but I'm having no luck. No other source has been able to solve this specific issue for me and I'm stuck. Please help.
override func viewDidLoad() {
super.viewDidLoad()
//Create new PFQuery to retrieve info from Parse
var query: PFQuery = PFQuery(className: "MyProduct")
//function to get the data
func getProductData (){
//call function to get the data from parse by specifyng an objectId
query.getObjectInBackgroundWithId("XXXXXXXXXX") {
(productData:PFObject?, error:NSError?) -> Void in
if error == nil && productData != nil {
//Extract values from the productData PFObject and store them in constants
let dayOfTheWeek = productData!.objectForKey("day") as! String
let productTitle = productData!.objectForKey("productTitle") as! String
//-----start image loading
let productImageFile = productData!["productImage"] as! PFFile
productImageFile.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let imageData = imageData {
let image = UIImage(data:imageData)
self.productImageView.image = image!
} else {println("Could not load image.")}
}
}
//-----end image loading
let productPrice = productData!.objectForKey("productPrice") as! String
let productDescription = productData!.objectForKey("productDescription") as! String
//take the saved constants and assign their values to the labels and UIImage on screen
self.productTitleLabel.text = productTitle
self.dayOfTheWeekLabel.text = dayOfTheWeek
self.productPriceLabel.text = productPrice
self.productDescriptionLabel.text = productDescription
} else if error != nil {
println("Could not load data from Parse")
}
}
}
I assume not for all products you have an image, so you need to update your code as following:
if let productImageFile = productData!["productImage"] as? PFFile {
productImageFile.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let imageData = imageData {
let image = UIImage(data:imageData)
self.productImageView.image = image!
} else {println("Could not load image.")}
}
}
}
This will guarantee that productImageFile will be processed only if response of productImage is PFFile.
Ensure you are getting valid data if your error is not printing anything useful. Check the data length and see if it corresponds to the file size.
Also, check to see if the imageView is setting the image on the main thread, it will not show if called from a background thread.
dispatch_async(dispatch_get_main_queue(), ^ {
self.productImageView.image = image!
}
Related
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.
I get a warming of "Conditional cast from PFFile toPFFile always succeed" which highlighted if let tempProductPicture = self.askProductImageArray[0] as? PFFile. What is the best way to solve it? Thanks
var askProductImageArray = [PFFile]()
override func viewDidLoad() {
let query = PFQuery(className: "products")
query.whereKey("title", equalTo: labelTitleText)
query.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error:NSError?) -> Void in
if error != nil {
}
for object in objects! {
self.askProductImageArray.append(object.objectForKey("detailsImage") as! PFFile)
self.askTable.reloadData()
}
if let tempProductPicture = self.askProductImageArray[0] as? PFFile {
tempProductPicture.getDataInBackgroundWithBlock { data, error in
if data == nil {
} else if error != nil {
} else {
self.productPicture.image = UIImage(data: data!)
}
}
}
}
Your self.askProductImageArray is an array of type PFFIle so there is no need to do as? PFFile because swift is strongly typed language and it will hold only PFFile so remover as? PFFile at the end.
In your case you don't need to do if let at all, you just need to check if item already exist.
you can do something like this
if self.askProductImageArray.count > 0 {
let tempProduct = self.askProductImageArray[0]
// and do rest
}
I've got an image in Parse that I want to load as the image for a button.
Here's my code:
let myData = PFObject(className: "Headliner")
if let theImageFileINeed = myData["ImageFile"] as! PFFile? {
theImageFileINeed.getDataInBackgroundWithBlock { (imageData: NSData?, error: NSError?) -> Void in
if (error == nil) {
print("loadingimage?")
if let imageData = imageData {
let image = UIImage(data: imageData)
self.headlinerImage.setImage(image, forState: UIControlState.Normal)
}
} else {
print("error")
}
}
}
Here's the code I'm referencing from the Parse documentation:
let userImageFile = anotherPhoto["imageFile"] as PFFile
userImageFile.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let imageData = imageData {
let image = UIImage(data:imageData)
}
}
}
When I use that exact code (I'm putting this in viewDidLoad, but am not sure if that's correct), swapping out the name of my table for "anotherPhoto" in the example (imageFile is the name of my field, too, so I didn't have to change that), I get the following error message: "Use of unresolved identifier "Headliner". So, then I assumed that maybe this goes inside a query? Or I need to specify the table somehow I want to pull data from, so I added the myData variable to pull that in.
When I run this, I don't get an error message, but my button doesn't update the image from parse.
I suspect it is related to types, probably in that "let my data = PFObject(className: "headliner") line... But I don't know how to fix it...
Any help would be appreciated! I bake cookies, so I'll send you some if you help me fix this!!!
Mali
Load image in tableView from Parse using PFFile
First step, make sure you import parse library:
import Parse
second step, declare a PFFile array, something like that:
var imageFiles = [PFFile]()
third, store all the images in the array:
let query = PFQuery(className:"your_class")
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil{
for writerData in objects! {
self.imageFiles.append(writerData["avatar"] as! PFFile)
}
/*** reload the table ***/
self.yourTableView.reloadData()
} else {
print(error)
}
}
Fourth, in order to show it (in my case I am displaying the images in UITableView), so in cellForRowAtIndexPath:
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! YourClassTableViewCell
imageFiles[indexPath.row].getDataInBackgroundWithBlock{
(imageData: NSData?, error: NSError?) -> Void in
if imageData != nil{
let image = UIImage(data: imageData!)
cell.cellAvatarImage.image = image
} else {
print(error)
}
}
Pretty sure this code should work. You may have to change the button settings in the interface builder to 'Custom'
Or maybe just create the button programmatically... See here:
How to create a button programmatically?
You need to try update your image in a different thread. ( This got me many times too)
Also I generally change the name unwrapped version of my variables so I can distinguish them easily.
dispatch_async(dispatch_get_main_queue(), { () -> Void in
//Image update code goes here
if let unWrappedimageData = imageData {
let image = UIImage(data: unWrappedimageData)
self.headlinerImage.setImage(unWrappedimageData, forState: UIControlState.Normal)
}
})
I have a PFQueryTableViewController populated by comments from different users. Each user has a profile picture stored on the Parse database. After loading each comment into a cell, I query the PFUser class to retrieve the profile picture of the user who posted the comment and add it to the cell. I also use PFCachePolicy to cache the profile picture to the device's memory so that displaying new cells with new profile pictures is a smoother transition.
However this is not the case. When a user posts a new comment and a new cell is added, the profile pictures shuffle around and takes about two seconds or so to update with the right image (probably because the table is re-queried and updated). I am trying to achieve something similar to iMessage or WhatsApp where the profile picture remained 'fixed' in the cell.
I am not sure what the problem is or if there is a better way to do this?
// get objectId of the user who posted a comment
let senderId = object?["Users"]!.objectId as String!
// query PFUser class using senderId to retrieve profile picture
var senderImage:PFQuery = PFUser.query()!
senderImage.cachePolicy = PFCachePolicy.CacheThenNetwork
senderImage.getObjectInBackgroundWithId(senderId){
(sender: PFObject?, error: NSError?) -> Void in
if error == nil && sender?.objectForKey("profilePicture") != nil {
let thumbnail = sender?.objectForKey("profilePicture") as? PFFile
thumbnail?.getDataInBackgroundWithBlock({
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
imageView.image = UIImage(data:imageData!)
} else {
println(error)
}
})
}
}
That's because you're not waiting until the images are finished loading when you update the UIImageView. Try Using this code:
var query = PFQuery(className:"Users")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
self.scored = objects!.count
// Do something with the found objects
if let objects = objects as? [PFObject] {
for object in objects {
let userImageFile = object["Image"] as! PFFile
userImageFile.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let imageData = imageData {
let image = UIImage(data:imageData)
self.imageArray.append(image!)
}
}
dispatch_async(dispatch_get_main_queue()) {
//don't reload image view here!
}
}
}
}
} else {
// Log details of the failure
print("Error: \(error!) \(error!.userInfo)")
}
dispatch_async(dispatch_get_main_queue()) {
//wait until here to reload the image view
if self.imageArray.isEmpty == false {
//image array is not empty
self.ImageView.image = imageArray.first
}
else {
//no images found in parse
}
}
I am wondering if I have done anything wrong here.
I have a subclass of PFUser which has a property of profileImage, which is a PFFile
There are some cases when I don't save the profileImage right away to Parse and I only pin it to the localDatastore.
But when I tried to retrieve it back from the localDataStore and use the getDataInBackgroundWithBlock. It does not return any error, but the NSData returned by the callback is always nil.
if let profileImage = PGUser.currentUser()?.profileImage {
profileImage.getDataInBackgroundWithBlock { (data: NSData?, error: NSError?) -> Void in
if error == nil {
if data != nil {
println("IMAGE DATA FOUND")
let image = UIImage(data: data!);
self.profileImageView.image = image;
}
else {
//DATA IS ALWAYS NIL
println("NO IMAGE DATA FOUND")
}
}
}
}
The PGUser.currentUser()?.profileImage is NOT NIL
No error returned by the getDataInBackgroundWithBlock function.
BUT the data is always
NIL.
Any thoughts on what am I doing wrong?
Thank you!!
Try this. I solved my problem with this
let profileImage = userPhoto["imageFile"] as PFFile
userImageFile.getDataInBackgroundWithBlock {
(imageData: NSData!, error: NSError!) -> Void in
if !error {
let image = UIImage(data:imageData)
self.profileImageView.image = image
}
}