PFFile to UIImage in Parse (ios) - ios

I am trying to convert a PFFile to a UIImage. It works, however, it doesn't seem to run until the very end. The line after the image is called appends the image to the array (photosArray.append(image!)), but when I do this, it doesnt work, and stores nothing. I believe this is because since the image hasnt been fully retrieved yet because it is grabbing the image in the background. How do I not have it save in the background, but wait until all the data is loaded, THEN append to the photosArray image, so the array isnt empty?
var photosArray = [UIImage]()
let photosValue = log["Images"] as! PFFile
photosValue.getDataInBackgroundWithBlock({
(imageData: NSData?, error:NSError?) -> Void in
if (error == nil) {
let image = UIImage(data:imageData!)
//line below appends nothing! photosArray is an empty array, []
photosArray.append(image!)
//following line of code doesnt run until the end. Is the last line to be printed in console.
println("Our Image: \(image)")
}
})
//prints "[]", no value
println(photosArray)
println("Our Image Array^^")
}
//after all the code is ran, the println("Our Image: \(image)") will run,
//and say "Our Image: Optional(<UIImage: 0x16f0ead0>, {394, 256})".
//This is the last line of the console logged, so it IS saving, but not until the end.

That is not a correct assumption, the whole point of the getDataInBackgroundWithBlock is to handle the response data. You also wouldn't be able to assign
let image = UIImage(data: imageData!)
if it were nil.
I'm not sure about what println statements you're referring to, however printing out the array while data is still being collected in the background may not give you a result, and just
[]
But you should see
println("Our image: \(image)")
If you plan to display these images inside a UIImageView I suggest taking a look at the ParseUI class PFImageView, which has a property you can assign a PFFile to and then a method in which to load it.

Related

SDWebImage giving nil with URL

I've a string in which I've comma separated links of images. Here is how I'm splitting it into an array: let imagesLinks = imageLins.components(separatedBy: ","). Then I've used for loop to get one link, download the image and storing it in a UIImage array in this way:
for imag in imagesLinks
{
let img = UIImageView()
print("\(baseReportImageURL)\(imag)")
img.sd_setImage(with: URL(string: "\(baseReportImageURL)\(imag)"), placeholderImage: nil)
imagesArray.append(img.image!)
}
The print statement is giving me the correct URL which when I open on browser downloads the image. The problem is on the line where I'm appending the array i.e. imagesArray.append(img.image!). I get:
fatal error: unexpectedly found nil while unwrapping an Optional value
and
fatal error: unexpectedly found nil while unwrapping an Optional value
So what would be the correct solution for this?
UPDATE
My question is different because I'm using SDWebImage and when I use completion block there is a strange behaviour of the app:
img.sd_setImage(with: imgURL, placeholderImage: nil,options: SDWebImageOptions(rawValue: 0), completed: { (image, error, cacheType, imageURL) in
activityIndicator.stopAnimating()
imagesArray.append(image!)
self.photoCollection.reloadData()
})
So it keeps on rotating the activity indicator and when I go back and push the view again it load the images instantly. So I think that the completion block is not called when the image is downloaded but why is that?
I think that is not the proper way to get downloaded images (set images to an UIImageView and then get the images from there).
You should use the image downloader, provided by SDWebImage:
SDWebImageManager.shared().imageDownloader?.downloadImage(with: <YOUR_URL>, options: [], progress: { (received, expected, nil) in
print(received,expected)
}, completed: { (image, data, error, true) in
yourArray.append(image)
})
If you have indexes, you can update the current row in the tableview every time when an image downloaded, or just reload() the whole, but I recommend the first.
It is because
imagesArray.append(img.image!)
Image take some times for downloading. When you do imagesArray.append(img.image!) at that time it is possible that your image is not set to your imageview and that means you are trying to add nil in your imageArray!
Second thing why are you storing image in an array ? You have array of urls and you are using SDWebImage then every time when you want to display image use SDWebImage. No need to store in array!
And if you want images in array anyhow than use NSUrlSession asynchronous requests with completion handlers and from completion handler add image to your array!
Append the image into the images array in the sd_setImage completion block.
The image is not yet downloaded when you are trying to add it into the array, and check if the image is not equal nil before adding it.
Use this Block For Image Download After download Perform Operation(append Image)
cell.appIcon.sd_setImage(with: url!, placeholderImage: UIImage(named: "App-Default"),options: SDWebImageOptions(rawValue: 0), completed: { (image, error, cacheType, imageURL) in
// Perform operation.
//append Image Here
})

comparing two PFFiles

Is it possible to compare to PFFiles?
I am trying to check whether a downloaded PFFile:
let imageFile = object["groupImage"] as PFFile
is equal to a mock data created by me like this:
let imageData = UIImagePNGRepresentation(UIImage(named: "dot.png")!)
uploadMock = PFFile(name: "mock", data: imageData!)
Now it happens when I call the comparison, it will not work.
if (mock?.isEqual(image))!{
print(true)
} else{
print(false)
}
will always give false, even though the images are the same.
It seems like it would be necessary, to download the image before. I tried to work around with checking the filename (it worked used to work until I transferred to another database).
Any ideas?

Parse array images saving and fetching

I have a mosaic app that takes multiple size photos and breaks them into smaller photos. Depending on the size of the photo, the amount of smaller photos could vary. Now I have an NSMutableArray named imageNameList2 that holds all of the smaller images taken from the larger image. For this example I showed an example with the images being called from the image assets list to make it easier to answer this question.
Here is the imageNameList (NSMutableArray that holds all the smaller images)
var imageNameList: [String] {
var imageNameList2:[String] = [] //[NSMutableArray]()
for i in 0...149 {
let imageName = String(format: "pic_%03d", Int(i))
imageNameList2.append(imageName)
}
return imageNameList2
}
What I'd like to do is have a continue button that will save all these images in order as piffles or any other format to parse that works best and have another button called retrieve that will retrieve all these photos from parse. I basically have a parse server that utilizes parse frameworks to help speed up the backend process. Can you please show me how I would save and retrieve this NSMutableArray if there are different numbers of stored images each time?
I think you're trying to do something like this. This is just an example. There's a lot of work to be done but hopefully this will get you started. I did not run or test this code.
The idea is to save your images as PFFiles, and create a 'tile' PFObject for each file. Then save all the 'tile' PFObjects to a 'tiles' key of the image PFObject. Then recall the image when you need it by objectId.
Good luck.
let appleTiles = ["apple1, apple2, apple3"]
let orangeTiles = ["orange1, orange2, orange3, orange4, orange5"]
func usage() {
//dont literally run these synchronously like this
post(appleTiles)
post(orangeTiles)
download()
}
func post(_ tileNames: [String]) {
let image = PFObject(className: "Image")
let tilesPF = tileNames.map({ name in
let data = UIImagePNGRepresentation(UIImage(named: name))!
let file = PFFile(data: data)
let tile = PFObject(className: "Tile")
tile["tile"] = file
})
image["tiles"] = tilesPF
image?.saveInBackground(block: { responseObject, error in
//you'll want to save the object ID of the PFObject if you want to retrieve a specific image later
})
}
func download() {
let query = PFQuery(className: "image")
//add this if you have a specific image you want to get
query.whereKey("objectId", equalTo: "someObjectId")
query.findObjectsInBackground({ result, error in
//this is probably close to how you'd unwrap everything but again, I didn't test it so...
if let objects = result as? [PFObject], let first = objects.first, let image = first["image"] as? PFObject, let tiles = image["tiles"] as? [PFObject] {
tiles.forEach({ tile in
let file = tile["tile"]
//now you have an individual PFFile for a tile, do something with it
})
}
})
}

Adding image from Firebase to UITableViewCell

I want to retrieve the image that is stored in the storage of an user and place it next to his name in a custom UITableViewCell. The problem now is that the tableview will load when the images aren't done downloading (I think?), causing the application to crash because the image array is nil. So what is the correct way to load the tableview? I think, for the user experience, it is important that the tableviewcell image should be shown even if the images aren't done downloading, and present them a default image that is saved in the assists. I thought about making an array with UIImages that links to the default asset of loading an image and changing the image to the profile picture when it is done downloading. But I really have no clue how to do that. This is what I got so far about downloading the image:
let storage = FIRStorage.storage()
let storageRef = storage.reference(forURL: "link.appspot.com")
channelRef?.observeSingleEvent(of: .value, with: { (snapshot) in
if let snapDict = snapshot.value as? [String:AnyObject]{
for each in snapDict{
let UIDs = each.value["userID"] as? String
if let allUIDS = UIDs{
let profilePicRef = storageRef.child((allUIDS)+"/profile_picture.png")
profilePicRef.data(withMaxSize: 1 * 500 * 500) { data, error in
if let error = error {
}
if (data != nil)
{
self.playerImages.append(UIImage (data: data!)!)
}
}
}
let userNames = each.value["username"] as? String
if let users = userNames{
self.players.append(users)
}
}
}
self.tableView.reloadData()
})
This is in the cellForRow
cell.playersImage.image = playerImages[indexPath.row] as UIImage
My rules, haven't changed it from the default rules:
service firebase.storage {
match /b/omega-towers-f5beb.appspot.com/o {
match /{allPaths=**} {
allow read, write: if request.auth != null;
}
}
}
Thank you.
Regarding user experience, you are correct. It is standard to have some sort of default image when loading an image from a URL. A great library to use for image caching and using default assets in its' place is AlamofireImage
Vandan Patel's answer is correct in saying you need to ensure your array is not nil when loading the tableview. You will be given a completion block to handle any extra work you would like to do with your image, using the AlamofireImage library.
This is all assuming you are getting a correct image URL back for your Firebase users.
You should call tableView.reloadData() when the images are done downloading. One important thing, initialize your playerImages as playerImages = [UIImage]() instead of playerImages: [UIImage]!. if it's empty, it wouldn't show your array is nil.
Update:
if let players = playerImages {
//code
}

How do i change this code to query by creation date with swift from parse data?

I have this code to retrieve images from parse data.
How do I query it by creation date? I tried using findObjectsInBackgroundWithBlock but it wont work with a PFFile. What do i do?
Also, as a side question, sometimes when i place the images in a UIImageView they are upside down or sideways. Why?
if let objects = objects as? [PFObject] {
for object in objects {
if let userPicture = object.valueForKey("Image") as? PFFile {
userPicture.getDataInBackgroundWithBlock({ (imageData: NSData?, error: NSError?) -> Void in
if (error == nil)
{
let image = UIImage(data:imageData!)
self.ImageArray.insert(image!, atIndex: 0)
}
else {
self.alert("Error: \(error!) \(error!.userInfo!)", Message: "Make sure you have a secure internet connection")
}
dispatch_async(dispatch_get_main_queue())
{
self.collectionView.reloadData()
println("Finished Pictures")
}
})
}
}}
You have a set of objects which all reference an image file, but probably a set of other things too, and the image can likely be changed. So, the object creation date isn't the same as the image creation date and the image file doesn't know (or at least doesn't expose) it's creation date. You're also currently always adding the images to the start of the array so the position will be set by how big (and therefore how long it takes to download) each image is. Also, trying to download lots of images at the same time could just mean you get lots of timeouts.
So, really you should have a column on your object which holds the date at which the image was updated, and sort the objects by that date. As you download the images you place them into the image array in the same index as the owning object in its array (pad the array out with NSNull so you know what's going on).

Resources