PFFile Image cache in Swift 2.0 - swift

I am getting image as PFFile. but when my table is scrolling every time PFFile is downloading and its take time.
I want to cache my file so if its will be downloaded then it will get from cache not from the Parse.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCellWithIdentifier("cell") as! RidesCustomCell!
if cell == nil {
cell = RidesCustomCell(style: UITableViewCellStyle.Default, reuseIdentifier: "cell")
}
let currentDic = dataArray.objectAtIndex(indexPath.row) as! NSDictionary
cell.backgroundColor = UIColor.clearColor()
cell.txtName.text = currentDic.valueForKey("firstName") as? String
cell.txtPrice.text = currentDic.valueForKey("price") as? String
cell.txtTime.text = currentDic.valueForKey("date")as? String
cell.txtFrom.text = currentDic.valueForKey("from") as? String
cell.txtTo.text = currentDic.valueForKey("to") as? String
print(currentDic)
cell.imgUser.image = UIImage(named: "noImg.png")
if (currentDic.valueForKey("imageFile") != nil){
// let userImageFile = currentDic.valueForKey("imageFile") as! PFFile
let queue : dispatch_queue_t = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0)
dispatch_async(queue, { () -> Void in
print(currentDic)
let pfuserGet = currentDic.valueForKey("user") as! PFUser
print(pfuserGet.username)
print(pfuserGet.email)
print(pfuserGet.password)
let userImageFile = pfuserGet.objectForKey("profilePicture") as! PFFile
userImageFile.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let imageData = imageData {
let image = UIImage(data:imageData)
cell.imgUser.image = image
}
}
}
})
}
return cell;
}

I had a look at your code. The issue is the PFFile.getDataInBackground() now since you are using dictionaries I would get the PFFile inside your PFQuery block and and store it in the dictionary as UImage() instead of a PFFile.
And all you have to do in the cellForRowAtIndexPath is load the image from the dictionary.
I think this will solve your problem

Related

Swift - No data displayed in TableView after Retrieving it from UserDefaults

I am trying to get the data I have saved and then load it into the TableView. Currently, if I am saving the object for the first time, the data gets encoded, saved, decoded, read and displayed correctly. However, if the key already exists and I am adding to the existing data, nothing gets displayed in the TableView.
This is currently how I am saving it in the first view controller:
let userEntry = UserEntries(date: String(todayDate), questions: [UserEntries.Question(question: q1Text, answer: q1Answer), UserEntries.Question(question: q2Text, answer: q2Answer)])
var allEntries : [UserEntries] = []
if doesKeyExist(key: "allEntries") == true {
let jsonDecoder = JSONDecoder()
if let data = UserDefaults.standard.data(forKey: "allEntries"),
let userEntries = try? jsonDecoder.decode(UserEntries.self, from: data) {
allEntries = [userEntries]
}
allEntries.insert(userEntry, at: 0)
let jsonEncoder = JSONEncoder()
if let value = try? jsonEncoder.encode(allEntries) {
UserDefaults.standard.set(value, forKey: "allEntries")
UserDefaults.standard.synchronize()
}
} else {
let jsonEncoder = JSONEncoder()
if let value = try? jsonEncoder.encode(userEntry) {
UserDefaults.standard.set(value, forKey: "allEntries")
UserDefaults.standard.synchronize()
}
}
let newViewController = storyboard?.instantiateViewController(withIdentifier: "tabViewController") as! UITabBarController
present(newViewController, animated: true, completion: nil)
}
This is how I'm displaying it in the TableView
var TableData : [UserEntries] = []
override func viewDidLoad() {
super.viewDidLoad()
let jsonDecoder = JSONDecoder()
if let data = UserDefaults.standard.data(forKey: "allEntries"),
let userEntries = try? jsonDecoder.decode(UserEntries.self, from: data) {
print(userEntries.date)
TableData = [userEntries]
}
// Uncomment the following line to preserve selection between presentations
// self.clearsSelectionOnViewWillAppear = false
// Uncomment the following line to display an Edit button in the navigation bar for this view controller.
// self.navigationItem.rightBarButtonItem = self.editButtonItem
}
override func numberOfSections(in tableView: UITableView) -> Int {
return 1
}
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return TableData.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "entryCell", for: indexPath)
cell.textLabel?.text = TableData[indexPath.row].date
cell.detailTextLabel?.text = TableData[indexPath.row].questions[0].answer
return cell
}
I have a feeling that this is a logic error, in getting/displaying the data in the TableView, but am unsure of what exactly it is/how to fix it. Thank you for your help!
I don't know what your doesKeyExists function does but you can improve your data manipulation by this way:
let userEntry = UserEntries(date: String(todayDate), questions: [UserEntries.Question(question: q1Text, answer: q1Answer), UserEntries.Question(question: q2Text, answer: q2Answer)])
var allEntries : [UserEntries] = []
let jsonDecoder = JSONDecoder()
if let data = UserDefaults.standard.data(forKey: "allEntries"),
let userEntries = try? jsonDecoder.decode([UserEntries].self, from: data) {
allEntries = userEntries
}
allEntries.insert(userEntry, at: 0)
let jsonEncoder = JSONEncoder()
if let value = try? jsonEncoder.encode(allEntries) {
UserDefaults.standard.set(value, forKey: "allEntries")
UserDefaults.standard.synchronize()
}
let newViewController = storyboard?.instantiateViewController(withIdentifier: "tabViewController") as! UITabBarController
present(newViewController, animated: true, completion: nil)
}
And the problem with your controller is that you are saving only one entry to UserDefaults. Try to change your code to
let jsonDecoder = JSONDecoder()
if let data = UserDefaults.standard.data(forKey: "allEntries"),
let userEntries = try? jsonDecoder.decode([UserEntries].self, from: data) {
print(userEntries)
TableData = userEntries
}

UISearchBar and Firebase Database

struct postStruct {
let title : String!
let author : String!
let bookRefCode : String!
let imageDownloadString : String!
let status : String!
let reserved : String!
let category : String!
let dueDate : String!
}
'Above is where i set up the structure for the post, and below, is how i reference and retrieve the data from the firebase database.
My problem is that when you set up the searcher, i do not know how to get it to search based off of the title of the post.'
class DirectoryTableView: UITableViewController {
var posts = [postStruct]()
override func viewDidLoad() {
let databaseRef = Database.database().reference()
databaseRef.child("Books").queryOrderedByKey().observe(.childAdded, with: {
snapshot in
var snapshotValue = snapshot.value as? NSDictionary
let title = snapshotValue!["title"] as? String
snapshotValue = snapshot.value as? NSDictionary
let author = snapshotValue!["author"] as? String
snapshotValue = snapshot.value as? NSDictionary
let bookRefCode = snapshotValue!["bookRefCode"] as? String
snapshotValue = snapshot.value as? NSDictionary
let status = snapshotValue!["status"] as? String
snapshotValue = snapshot.value as? NSDictionary
let reserved = snapshotValue!["reserved"] as? String
snapshotValue = snapshot.value as? NSDictionary
let category = snapshotValue!["category"] as? String
snapshotValue = snapshot.value as? NSDictionary
let dueDate = snapshotValue!["dueDate"] as? String
snapshotValue = snapshot.value as? NSDictionary
self.posts.insert(postStruct(title: title, author: author, bookRefCode: bookRefCode, status: status, reserved: reserved, category: category, dueDate: dueDate) , at: 0)
self.tableView.reloadData()
})
override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return posts.count
}
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
var cell = tableView.dequeueReusableCell(withIdentifier: "cell")
let databaseRef = Database.database().reference()
let label1 = cell?.viewWithTag(1) as! UILabel
label1.text = posts[indexPath.row].title
let label2 = cell?.viewWithTag(2) as! UILabel
label2.text = posts[indexPath.row].author
let label3 = cell?.viewWithTag(3) as! UILabel
label3.text = posts[indexPath.row].bookRefCode
let label4 = cell?.viewWithTag(4) as! UILabel
label4.text = posts[indexPath.row].status
let label5 = cell?.viewWithTag(5) as! UILabel
label5.text = posts[indexPath.row].category
let image1 = cell?.viewWithTag(6) as! UILabel
image1.text = posts[indexPath.row].imageDownloadString
let label6 = cell?.viewWithTag(7) as! UILabel
label6.text = posts[indexPath.row].reserved
let label9 = cell?.viewWithTag(9) as! UILabel
label9.text = posts[indexPath.row].dueDate
return cell!
}
'Also, does anyone know how to sort the tableview cells (posts in this case) alphabetically?'
You can get all data already ordered alphabetically
databaseRef.child("Books").queryOrdered(byChild: "title").observe(.childAdded, with: { snapshot in
var snapshotValue = snapshot.value as? NSDictionary
let title = snapshotValue!["title"] as? String
snapshotValue = snapshot.value as? NSDictionary
....
}
or sort your array before reload the tableView
var sortedArray = swiftArray.sorted { $0.title.localizedCaseInsensitiveCompare($1.title) == ComparisonResult.orderedAscending }
Sample structure
for sorting data according to searchBar I had used an dictionary that having all my snapshot and I compared my searchBar text in that dict and after sorting reloaded tableView here is code that you can have a look at
//method to get all user Details in a dict
func getEmail() {
let databaseRef = Database.database().reference().child("users")
databaseRef.observe(.value, with: { (snapshot) in
if snapshot.exists(){
self.postData = snapshot.value as! [String : AnyObject]
let dictValues = [AnyObject](self.postData.values)
self.sarchDict = dictValues
}
})
}
//search bar delegate
func searchBar(_ searchBar: UISearchBar, textDidChange searchText: String) {
if self.mySearchBar.text!.isEmpty {
// set searching false
self.isSearching = false
}else{
// set searghing true
self.isSearching = true
self.names.removeAll()
self.uidArray.removeAll()
self.imageUrl.removeAll()
for key in self.sarchDict {
let mainKey = key
//I am making query against email in snapshot dict
let str = key["email"] as? String
//taking value of email from my dict lowerCased to make query as case insensitive
let lowercaseString = str?.lowercased()
//checking do my any email have entered letter or not
if(lowercaseString?.hasPrefix(self.mySearchBar.text!.lowercased()))!{
//here I have a check so to remove value of current logged user
if ((key["uID"] as! String) != (Auth.auth().currentUser?.uid)!){
//If value is found append it in some arrays
self.imageUrl.append( key["profilePic"] as! String )
self.names.append( key["name"] as! String )
self.uidArray.append( key["uID"] as! String )
//you can check which values are being added from which key
print(mainKey)
}
}
}
//reload TableView here
}
}
//TableView
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
cell = self.myTableView.dequeueReusableCell(withIdentifier: "Cell")!
if self.isSearching == true {
let imageView = (cell.viewWithTag(1) as! UIImageView)
imageView.setRounded()
if imageUrl[indexPath.row] != "" {
self.lazyImage.showWithSpinner(imageView:imageView, url:imageUrl[indexPath.row])
}
else{
imageView.image = UIImage(named: "anonymous")
}
(cell.contentView.viewWithTag(2) as! UILabel).text = self.names[indexPath.row]
}
else {
}
return cell
}
I'm sure this will be helpful to some using FireStore. Here I'm just setting my reference to point to the right collection. "name" is my field I wish to search by and is greater than will be checked chronologically on my string. The further they type the more defined the search results are.
static func searchForProgramStartingWith(string: String) {
let programsRef = db.collection("programs")
programsRef.whereField("name", isGreaterThan: string).limit(to: 10).getDocuments { (snapshot, error) in
if error != nil {
print("there was an error")
} else {
let shots = snapshot?.documents
for each in shots! {
let data = each.data()
let name = data["name"]
print("The name is \(name!)")
}
}
}
}

Getting images from parse on correct order

I'm trying to get some strings and one photo from parse.com for tableview. I have an NSObject for this class and also an array of object to store them. I can get the newsTitle and the newsDetail in correct order by got fail when try to get newsPhoto. I suppose it lost its order when try get get images in block. Does anybody know what should I change on below code to fix it?
func getNews(){
let query = PFQuery(className: "bulletinOnParse")
query.orderByDescending("createdAt")
query.findObjectsInBackgroundWithBlock {
(allNews: [PFObject]?, error: NSError?) -> Void in
if error == nil {
var duyuru:News
for news in allNews! {
duyuru = News()
let nTitle = news.objectForKey("title") as! String
duyuru.newsTitle = nTitle
let nDetail = news.objectForKey("comment") as! String
duyuru.newsDetail = nDetail
let imageFile = news["newsphoto"] as! PFFile
imageFile.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let imageData = imageData {
let image = UIImage(data:imageData)
duyuru.newsPhoto = image!
}
}
}
self.bulletin += [duyuru]
}
} else {
// Log details of the failure
print("\(error!.userInfo)")
}
self.tableView.reloadData()
}
}
And cellForRowAtIndexPath method below
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath) as! DuyuruTableViewCell
self.tableView.rowHeight = 100
let cellInfo = bulletin[indexPath.row]
cell.newsTitle.text = cellInfo.newsTitle
cell.news.text = cellInfo.newsDetail
dispatch_async(dispatch_get_main_queue(), {
cell.newsPhoto.image = cellInfo.newsPhoto
})
return cell
}
Here is the answer of how I solve the problem;
First I created an image array from PFFile object
var resultUserImageFiles = [PFFile]()
Then I get the name and add the array on getNews() method
self.resultUserImageFiles.append(news.objectForKey("newsphoto") as! PFFile)
And I get each photo for cell on below method in cellForRowAtIndexPath method.
self.resultUserImageFiles[indexPath.row].getDataInBackgroundWithBlock { (imageData: NSData?, error:NSError?) -> Void in
if error == nil {
let image = UIImage(data: imageData!)
cell.newsPhoto.image = image
}
}

How do I fix this error"Could not cast value of type 'UIImageView' () to 'PFImageView' " to display images on query?

I'm trying to retrieve users profile pictures(File) from Parse onto my PFQueryTableViewController. The code I've written doesn't give me errors but every time I run my app crashes and says "Could not cast value of type UIImageView to PFImageView. I'm new to coding and don't know what that means. Is there something I have to fix in my code? I just want to retrieve the users image to display on my query.
Here is my code below for my FeedViewController:
override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?, object: PFObject!) -> PFTableViewCell? {
let cell = tableView!.dequeueReusableCellWithIdentifier("BubbleCell", forIndexPath: indexPath!) as! Bubbles
if let userPost : PFObject = self.posts.objectAtIndex(indexPath!.row) as! PFObject {
/ if let pic = object["photo"] as? PFFile {
// Make sure your imageView is a PFImageView
let imageView = cell.userImage as! PFImageView
// I assume this is the placeholder image while your load your image files from Parse
imageView.image = UIImage(named: "image.png")
imageView.file = pic
// Load the image file however you see fit
imageView.loadInBackground(nil)
}
Here is my code for my PostViewController:
#IBAction func postPressed(sender: AnyObject) {
let testObj = PFObject(className: "Test")
testObj["photo"] = PFUser.currentUser()?.valueForKey("photo") as! PFFile
testObj.saveInBackgroundWithBlock { (success:Bool, error :NSError?) -> Void in
if error == nil
{
print("***///detail is saved///***")
self.dismissViewControllerAnimated(true, completion: nil)
}
else {
self.alert()
}
}

Parse get data with block not assigning value to variable in scope

Having issues to assign uiImageFile = image!. If It try to assign self.myImage = image! where myImage is a global variable it works.
Is it something possible to be done?
The code is retrieving images ok, also the cell will take an image if pointed directly. Just this bridge that is not working. And it only do not work for the image.
Also the following test line println("TESTSTRING\(indexPath.row)") just above the return is being able to get and print value from testString = "\(indexPath.row)" that is inside getDataInBackgroundWithBlock.
Sorry about the question title. Not sure how to resume the issue in a single sentence.
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "Cell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! HobbieFeedTableViewCell
let object: PFObject = self.timelineData.objectAtIndex(indexPath.row) as! PFObject
var myText = object.objectForKey("postText") as? String
let userImageFile = object.objectForKey("postImage") as? PFFile
var uiImageFile = UIImage()
var testString = String()
println(userImageFile, indexPath.row)
if userImageFile != nil {
userImageFile?.getDataInBackgroundWithBlock({ (imageData:NSData?, error:NSError?) -> Void in
if error == nil {
if let myImageData = imageData {
let image = UIImage(data:myImageData)
self.myImage = image!
uiImageFile = image!
testString = "\(indexPath.row)"
}
}
}, progressBlock: { (percent: Int32) -> Void in
})
}
cell.cellTitle.text = myText
cell.cellImage.image = uiImageFile
// cell.cellImage.image = myImage
println("TESTSTRING\(indexPath.row)")
return cell
}
cell.cellImage.image = uiImageFile
gets executed before uiImageFile has been retrieved. This is because the
getDataInBackgroundWithBlock
returns right away before the retrieval is done.
You can fix it by: (1) Retrieve all images into a local array in the viewDidLoad function (2) Use push notification on completion on retrieval to trigger a tableview.reload