trying to retrieve image from parse.com but getting error - ios

func loadImages() {
var query = PFQuery(className: "CollegeCover")
query.findObjectsInBackgroundWithBlock { (objects, error) -> Void in
if (error == nil) {
let imageObjects = objects! as [PFObject]
for object in objects! {
let thumbNail = object["image"] as! PFFile
thumbNail.getDataInBackgroundWithBlock{ (objects, error) -> Void in
if (error == nil) {
let imageObjects = objects as? [NSData?]
let image = UIImage(data:objects!)
self.userImageView.image = image
print(image)
}}}
}
else{
print("Error in retrieving \(error)")
}
}//findObjectsInBackgroundWithblock - end
}
am trying to retrieve an image which is stored in parse.com's server but i don't know why am getting an error , and am not sure that my approach for getting the image is right or not so please help me if any body knows how to do it rightly or what am missing or doing wrong the error am getting is
fatal error: unexpectedly found nil while unwrapping an Optional value
(lldb)

I might be wrong, but I think that the only reason you get a nil result is because you are using an incorrect column name, i.e. "image". Are you sure this is the exact naming of your column in Parse?
Once you're past that error, you should be able to get the image to load fine since your code seems OK. You could however also consider PFImageView and save yourself the trouble of the extra coding since you can specify a PFFile object to the its file property. See here for an example.

Related

Firebase reference is 'variable not available' when downloading picture in Swift

Title says everything. I'm just unable to download an image from Firebase Storage dir. Here is the snippet of the code which calls the function for setting data and it also calls the function which tries to download the picture:
for element in Dict {
if let itemDict = element.value as? [String:AnyObject]{
let name = itemDict["name"] as! String
let price = itemDict["price"] as! Float
let imageObject = itemDict["image"] as! NSDictionary
let hash = imageObject["hash"] as! String
let storageDir = imageObject["storageDir"] as! String
let image:UIImage = self.downloadImageProductFromFirebase(append: hash)!
let product = Product(name: name, image: image, imageName:hash, price: price, storageDir : storageDir)
self.productList.append(product)
}
}
print(Dict)
self.myTable.reloadData()
And here is the code which tries to download the image:
func downloadImageProductFromFirebase(append:String) -> UIImage?{
let gsReference = Storage.storage().reference(forURL: "gs://fridgeapp-3e2c6.appspot.com/productImages/productImages/" + append)
var image : UIImage?
gsReference.downloadURL(completion: { (url, error) in
if error != nil {
print(error.debugDescription)
return
}
URLSession.shared.dataTask(with: url!, completionHandler: { (data, response, error) in
if error != nil {
print(error.debugDescription)
return
}
guard let imageData = UIImage(data: data!) else { return }
DispatchQueue.main.async {
image = imageData
}
}).resume()
})
return image
}
But, for some reason, it crashes just when calling this last function, saying that "fatal error: unexpectedly found nil while unwrapping an Optional value". I tried to use the debugger, and I found out that Firebase reference to Storage variable says "variable not available".
Could someone of you guys help me with this? I think I read the Firebase doc about a hundred times, and still can't get the point.
Thank you!
Downloading an image from a remote server is an asynchronous task, that means that the result is not immediately available. This is the reason that gsReference.downloadURL accepts a completion callback as an argument, and has no return value.
Since your function (downloadImageProductFromFirebase) is simply a wrapper to gsReference.downloadURL, it should also accept a completion callback as an argument, and should not have a return value (i.e. remove the -> UIImage?).
When you call self.downloadImageProductFromFirebase pass in a closure that receives the image, finds the index of the corresponding product in productList, and sets itself as the cell's image (assuming you're showing the image in the cell).
See this answer for how to asynchronously set cell images.

Parse Messenger Chat Swift : Messages are out of order

Recently I have been having a little trouble with my ios messenger. I fetched only text messages at first and everything worked perfectly. When I tried to fetch an image from parse I succeeded; however, the feed was not in the right order.
It seems like its ignoring "query.orderByAscending" entirely...
fetchMessages()
{
currentUser = PFUser.currentUser()!
let query = PFQuery(className:"Messages")
query.whereKey("convoid", equalTo:convoid)
query.orderByAscending("createdAt")
query.cachePolicy = .NetworkElseCache
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
dispatch_async(dispatch_get_main_queue()) {
if let objects = objects {
for object in objects {
if(object["fileType"] as? String == "photo"){
if(object["senderId"] as? String == self.currentUser.objectId!){
let userImageFile = object["file"] as! PFFile
userImageFile.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
let imageddata = UIImage(data:imageData!)
let chatBubbleData = ChatBubbleData(text: "", image:imageddata, date: object.createdAt, type: .Mine)
self.addChatBubble(chatBubbleData)
self.chatBubbleDatas.append(chatBubbleData)
}
}
}else{
let userImagefile = object["file"] as! PFFile
userImagefile.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
let imageddata = UIImage(data:imageData!)
let chatBubbleData = ChatBubbleData(text: "", image:imageddata, date: object.createdAt, type: .Opponent)
self.addChatBubble(chatBubbleData)
self.chatBubbleDatas.append(chatBubbleData)
}
}
}
}else{
if(object["senderId"] as? String == self.currentUser.objectId!){
let chatBubbleData = ChatBubbleData(text: object["text"] as? String, image:nil, date: object.createdAt, type: .Mine)
self.addChatBubble(chatBubbleData)
self.chatBubbleDatas.append(chatBubbleData)
}else{
let chatBubbleData = ChatBubbleData(text: object["text"] as? String, image:nil, date: object.createdAt, type: .Opponent)
self.addChatBubble(chatBubbleData)
self.chatBubbleDatas.append(chatBubbleData)
}
}
}
}
}
} else {
print("Error: \(error!) \(error!.userInfo)")
}
}
self.messageCointainerScroll.contentSize = CGSizeMake(CGRectGetWidth(messageCointainerScroll.frame), lastChatBubbleY + internalPadding)
self.addKeyboardNotifications()
}
Everything works well besides the fact that the message view is not presenting all the messages in the right order. In fact, all the text messages are in the right order but an message image always comes after no matter what the case, regardless of the date createdAt. I think it has to do something with loading; however I am knew to swift and I am not completely sure. Any insights on a fix or a reference please share!
My first guess is that it has to do with the placement of your call to do operations on the main queue:
dispatch_async(dispatch_get_main_queue()) {
You probably only want to do that when you need to update the user interface at the end. The issue is that the operations that take longer may get processed after the operations that take less time since the entire block is operating in the background to begin with (on a different queue, so you don't know what the scheduling is like on the main queue...).
Upon further inspection it appears you have a bunch of other background calls inside your block e.g.:
userImagefile.getDataInBackgroundWithBlock
I have written code like this using Parse and JSQMessagesViewController which I assume is what you're doing. I strongly suggest you find ways to decouple your user interface updates from your model updates over the network. What you have right now is not only hard to debug but also probably causing your problem in some kind of hard to detect asynchronous way.
It is actually using query.orderByAscending("createdAt") to get the objects. Just add print(objects) inside query.findObjectsInBackgroundWithBlock and you should see all your objects printed in the right order.
The incorrect order is a result of loading the images. When you get a text object, you immediately append the data. But when the object is an image, you call userImageFile.getDataInBackgroundWithBlock which downloads the image before calling the block which is where you append the data.
What you would need to do is append a placeholder for the image so it is in the correct position then update it when the image has finished downloading.
As a side note, you may want to look into subclassing PFObject, it will get rid of all the as? if statements and get rid of the Pyramid of Doom.

Parse & Swift- Randomizing images

First, to be simple, how do I change a blank UIImage view to an image I have stored in Parse? This is my code so far
var query = PFQuery(className:"Content")
query.getObjectInBackgroundWithId("mlwVJLH7pa") {
(post: PFObject?, error: NSError?) -> Void in
if error == nil && post != nil {
//content.image = UIImage
} else {
println(error)
}
}
On top of just replacing the blank UIImageView, how may I make the image that it is replaced with random? I assume I can't use an objectId anymore, because that is specific to the row that it represents.
I would first retreive the objectIds from parse with getObjectsInBackgroundWithBlock, and then select a random objectId from that array in a variable called objectId. That way you save the user from querying every object from parse and using a lot of data doing it.
Second I would
getObjectInBackgroundWithId(objectId)
if error == nil {
if let image: PFFile = objectRow["image"] as? PFFile{
image.getDataInBackgroundWithBlock {
(imageData: NSObject?, error: NSError?) Void in ->
if let imageData = imageData {
let imageView = UIImageView(image: UIImage(data: imageData))
}
}
}
At least this works for me.
First off, you'll want to use query.findObjectsInBackgroundWithBlock instead of using query.getObjectInBackgroundWithId.
This will get you all of your PFObjects. Grab a random PFObject from the array it returns and now you have a single random PFObject that you can set the content.image to.
Let's say your PFObject has an 'image' attribute as a PFFile (what you should be using to store images with Parse.)
Simply convert the PFFile into a UIImage by doing something similar to the following:
if let anImage = yourRandomPFObject["image"] as? PFFile {
anImage.getDataInBackgroundWithBlock { (imageData: NSData?, error: NSError?) -> Void in
let image = UIImage(data:imageData)
content.image = image
}
}

Issues retrieving PFFile from Parse

I am creating an app in parse in which the user has an option to choose a profile picture when they sign up. This is the code for that.
var profilePictures = PFObject(className: "ProfilePictures")
let imageData = UIImagePNGRepresentation(self.profileImage.image)
let imageFile = PFFile(name:"image.png", data:imageData)
profilePictures["profilePhoto"] = imageFile
profilePictures["user"] = usernameField.text
profilePictures.save()
Later I have a screen in which a UIImageView needs to be populated with the chosen profile picture.
This works until the application itself is stopped completely and restarted.
The PFFile is then found as nil and I get the error "unexpectedly found nil while unwrapping an optional value".
Here is the code for displaying the picture.
override func viewDidAppear(animated: Bool) {
var query = PFQuery(className: "ProfilePictures")
query.whereKey("user", equalTo: PFUser.currentUser()?.username)
query.findObjectsInBackgroundWithBlock({
(success, error) -> Void in
let userImageFile = profilePictures["profilePhoto"] as! PFFile
//error is on the above line
userImageFile.getDataInBackgroundWithBlock({
(imageData: NSData?, error) -> Void in
var image = UIImage(data: imageData!)
self.profileImage.image = image
})
})
}
For some reason you are not getting userImageFile correctly set. It appears to be a nil. I would check the Parse console to confirm that you have an image in the PFile. In any case it may be smarter to use 'if let' to avoid the unwrapping problem. This will not solve the problem if there if PFile is not saved since as pointed below you should use saveInBackground and use notifications to confirm that you are ready for a retrieval.
if let userImageFile = profilePictures["profilePhoto"] as! PFFile {
//error is on the above line
userImageFile.getDataInBackgroundWithBlock({
(imageData: NSData?, error) -> Void in
var image = UIImage(data: imageData!)
self.profileImage.image = image
})
}
Your error is probably on saving:
let imageFile = PFFile(name:"image.png", data:imageData)
profilePictures["profilePhoto"] = imageFile
profilePictures.save()
You are saving an object with a pointer to a new unsaved PFFile, which leads to error. You should first do imageFile.saveInBackground, and use callback to assign imageFile on profilePictures, then save profilePictures.
You can confirme that by seeing on Parse's datastore that there is no value for key 'profilePhoto' on your profilePictures object

iOS - Retrieve and display an image from Parse in UIImageView (Swift 1.2 Error)

I have previously been retrieving an image form my Parse backend to display in my app inside a UIImageView using the following lines of code:
let userPicture = PFUser.currentUser()["picture"] as PFFile
userPicture.getDataInBackgroundWithBlock { (imageData:NSData, error:NSError) -> Void in
if (error == nil) {
self.dpImage.image = UIImage(data:imageData)
}
}
But I get the error:
'AnyObject?' is not convertible to 'PFFile'; did you mean to use 'as!'
to force downcast?
The 'helpful' Apple fix-it tip suggests the "as!" change so I add in the !, but then I get the error:
'AnyObject?' is not convertible to 'PFFile'
With the 'getDataInBackgroundWithBlock' section, I also get the error:
Cannot invoke 'getDataInBackgroundWithBlock' with an argument list of type '((NSData, NSError) -> Void)'
Can someone please explain how to correctly retrieve a photo from Parse and display it in a UIImageView using Swift 1.2?
PFUser.currentUser() returns optional type (Self?). So you should unwrap return value to access element by subscript.
PFUser.currentUser()?["picture"]
And the value got by subscript is also optional type. So you should use optional binding to cast the value because type casting may fail.
if let userPicture = PFUser.currentUser()?["picture"] as? PFFile {
And parameters of the result block of getDataInBackgroundWithBlock() method are both optional type (NSData? and NSError?). So you should specify optional type for the parameters, instead NSData and NSError.
userPicture.getDataInBackgroundWithBlock { (imageData: NSData?, error: NSError?) -> Void in
The code modified the all above problems is below:
if let userPicture = PFUser.currentUser()?["picture"] as? PFFile {
userPicture.getDataInBackgroundWithBlock { (imageData: NSData?, error: NSError?) -> Void in
if (error == nil) {
self.dpImage.image = UIImage(data:imageData)
}
}
}

Resources