Correctly populate table with data from parse - swift

So this isn't a question about actually populating a table with data because I already have that working properly.
I have a page that retrieves a list of nearby users - it grabs their name, distance away and profile image. The problem is that because some profile images are bigger than others the correct profile image isn't always put alongside the corresponding name and other data.
let profileImgFile = location["profilePicture"]
profileImgFile!.getDataInBackgroundWithBlock { (data: NSData?, error: NSError?) -> Void in
if error != nil {
print("\(__FUNCTION__) - Error: (error!.localizedDescription)")
} else {
let image = UIImage(data: data!)
self.userImages.append(image!)
self.userTable.reloadData()
}
}
This is how I'm retrieving the images, they're added to an array so that the table can be populated here:
cell.nameLabel.text = "\(userData[indexPath.row])"
cell.profileImage.image = userImages[indexPath.row]
cell.distanceLabel.text = ("\(userDistance[indexPath.row])")
So, I wondered if there was any way of retrieving the images one after the other. So that the next image doesn't begin downloading until the first one has finished.
If not is there any other way of fixing this problem?
Thanks!

I think you should try setting your data async with promises.
Promises in Swift
PromiseKit
And there's a lot of other resources out there.

Related

Fetching Download URL From Uploaded Image - Firebase and Swift

I currently have a view controller where the user uploads an image and that image is stored in the Firebase Storage, with folders of their user id and in there their uploaded image. I want to fetch that image's url and display it on the view controller. The question I have is, do i need to store that uploaded image's download url into the realtime database ie; Users - > User Id - > Media -> Image Title -> Download URL? Or is there a way for me to refer an image view to Firebase storage and accordingly into the User Id's folder containing all the images and display this? Would appreciate it a lot if someone could help me out. Thank you!
This is my code:
let imageName = (Auth.auth().currentUser?.uid)!+"/\(imageTitle.text!)"
let imageReference = Storage.storage().reference().child(MyKeys.imagesFolder).child(imageName)
imageReference.putData(data, metadata: nil) { (metadata, err) in
if let err = err {
print ("Error")
return
}
imageReference.downloadURL(completion: { (url, err) in
if let err = err {
print ("Error")
return
}
guard let url = url else {
print ("Error")
return
}
let dataReference = Firestore.firestore().collection(MyKeys.imagesCollection).document()
let documentUid = dataReference.documentID
let urlString = url.absoluteString
let data = [
MyKeys.uid:documentUid,
MyKeys.imageUrl: urlString,
MyKeys.imageTitle: self.imageTitle.text!,
] as [String : Any]
dataReference.setData(data) { (err) in
if let err = err {
print ("Error")
return
}
}
})
}
There are two ways to read the data from Storage:
Through the Firebase SDK by calling getData or write on a reference, as shown in the documentation on downloading data.
Through a download URL for the reference, which provides public read-only access.
To call getData, write or to get a download URL, you need to have a reference to the file as shown in creating a reference.
Since you store the files under the UID of the user, you can always create a reference to any for for a user if you known their UID. So you can always perform one of the two methods above to read the data for the file, even if you didn't store the download URL.
That said, it is fairly common to store the download URL in a database, as it means you can then treat it like any other image URL. By doing so the rest of your code won't have to know anything about Cloud Storage.
So: it can work without storing the download URL in the database, but it's also fine (and common) if you do store the download URL. The choice is really up to you.

Swift, Kingfisher Cache not working properly?

I am trying to cache images using the Cocoapod KingFisher, the code i am using does display the image from the database storage but it does no caching. I am curious as to know why?
The print always says "cache Result none". And i also notice that the images are not cached.
Code for calling the imageDownloader:
DownloadImage(imageId : nextUser.id, cardImage: secondProfilePic)
Code for downloading and caching, also for checking if cached.
func DownloadImage(imageId : String, cardImage : UIImageView){
let imagesStorageRef = Storage.storage().reference().child("profilepic/").child(imageId)
//Get URL For Cache
imagesStorageRef.downloadURL { url, error in
if let error = error {
// Handle any errors
cardImage.image = UIImage(named: "RentOutProfilePic")
print("Error")
} else {
// Get the download URL for '.jpg'
let pathURL = url
print("Sets Image")
cardImage.kf.indicatorType = .activity
cardImage.kf.setImage(with: pathURL,
options: [
.transition(.fade(0.3)),
.cacheOriginalImage
])
}
if let url = url{
let tempUrl:String = url.path
let cache = ImageCache.default
let cached = cache.imageCachedType(forKey: tempUrl)
print("cache Result \(cached)")
}
}
}
Kingfisher is using url.absoluteString as the cache key for an image by default. So in your code, url.path will always return you the result of "not cached".
You are trying to print the cache result as the same time when you set the image. At the first time, your image would be in download progress so you always get .none even you set the key correctly according to 1. In the following invocation of this method with the same id, you should get a cache result either as disk or memory.
I am not sure how did you get the conclusion of "the images are not cached". Kingfisher is doing cache based on url by default. If you have different urls every time (which is returned from your imagesStorageRef) you call the image view setting method, there would be no matching cache and downloading would happen. If this is your case, you can customize to use the imageId as the cache key instead. To do that, you need to specify another cache key. See this wiki section for more.

Finder Share Extension: Getting a preview image from NSItemProvider

I'm creating a Finder Share extension to be used when selecting a file. I'd like to show an image preview of the file that you are sharing (like you see in the Messages and Twitter Finder Share extensions, for example).
In the loadView method of the view controller for my share extension, I'm doing this:
let item = self.extensionContext!.inputItems[0] as! NSExtensionItem
if let attachments = item.attachments as? [NSItemProvider] {
if let attachment = attachments.first {
attachment.loadPreviewImage(options: nil, completionHandler: { (item, error) in
if error != nil {
//handle error...
} else if let img = item as? NSImage {
//put image preview in my "share sheet"...
}
})
}
}
The problem is that when I keep hitting the error condition. The error that I'm getting is:
Error Domain=NSItemProviderErrorDomain Code=-1000 "Cannot load preview." UserInfo={NSLocalizedDescription=Cannot load preview.}
For what it's worth, I am able to call attachment.loadItem() successfully and work with the file. But I don't really care to do anything with the file itself at this point, I just want a thumbnail image that represents the file which theoretically this method should give me...
Any ideas?
I am in the same situation. After read the manual, I think here is the reason.
"Loads the preview image for the item that is represented by the item provider."
However, it is not forced that the item provider must provides a preview. So the error simply says that there is no preview that the item provider can provide. You should do it yourself.

Difference between generate and append when querying and adding data to array?

var objectarray = [PFObject]()
func populateTable() {
query.findObjectsInBackgroundWithBlock { (objects, error) in
self.objectarray.removeAll(keepCapacity: true)
self.searchTableView.reloadData()
if error == nil {
Above is the query I am doing and the below 2 codes are what I can use to use the query to populate a array.
if let objects = objects as [PFObject]! {
self.objectarray = Array(objects.generate())
}
Is there any difference with running this code above to populate my array or running the code below?
for object in objects! {
self.objectarray.append(object)
}
Doing either works to load onto my tableView. Also another question regarding Parse. After doing the above, the user doesn't download PFFiles from the background until I run
getDataInBackgroundWithBlock
right? I want to know if it'd be beneficial to save smaller versions of images onto the server.

pointers in parse class with swift code

I'm trying to insert a text under description column, and it keep giving me error, I think I'm having this issue because I wasn't able to use the pointer properly.. is there any one can help, I watched a number of tutorial video's as to how to setup the pointer in Parse, and that didn't solve the issue.
Thanks.
let me = self.textField.text
let query = PFQuery(className: "Store")
query.getObjectInBackgroundWithId ("product", block: {
(object: PFObject?, error: NSError?) -> Void in
if error != nil
{
print(error)
}
else if let transaction = object {
transaction["discription"] = "\(me)"
transaction.saveInBackground()
print(object!.objectForKey("discription"))
}
})
I finally got the answer, the best possible answer I got was, not store any information to the DB till all the input is done from the last page. It means you carry over the input as temp to each pages, then save it at the end... :)