I am trying to fetch data from Parse and populate a table view controller. I have the following defined in the VC:
class OrdersViewController: UITableViewController{
/*************************Global Objects************************/
var userObject = UserClass()
var utilities = Utilities()
var orderObject = OrderClass()
var objectsArray:[PFObject]!
/*************************UI Components************************/
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
tableView.delegate = self
tableView.dataSource = self
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
SwiftSpinner.show("Please Wait...Populating Table")
let query = PFQuery(className:"Orders")
query.whereKey("appUsername", equalTo:PFUser.currentUser()!["appUsername"])
query.findObjectsInBackgroundWithBlock {
(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
SwiftSpinner.hide()
self.objectsArray = objects
} else {
SwiftSpinner.hide()
// Log details of the failure
print("Error: \(error!) \(error!.userInfo)")
}
}
SwiftSpinner.hide()
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return objectsArray.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cellIdentifier = "cell"
let cell = tableView.dequeueReusableCellWithIdentifier(cellIdentifier, forIndexPath: indexPath) as! OrderCell
let row = indexPath.row
cell.orderRetailerName.text = objectsArray[row]["nameRetailer"] as? String
cell.status.text = objectsArray[row]["status"] as? String
cell.dueDate.text = objectsArray[row]["dueDate"] as? String
return cell
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
/*tableView.deselectRowAtIndexPath(indexPath, animated: true)
let row = indexPath.row
print("")*/
}
}
in the viewWillAppear I am trying to fetch the data from the Parse backend and populate the table with it. When I run the program, I am getting the following error:
fatal error: unexpectedly found nil while unwrapping an Optional value
the error is caused by objectsArray.count line in numberOfRowsInSection. Fair enough...It is trying to get the count but clearly the array is empty because the job of fetching data is running in background and isn't completed yet. This is what I need help with. Am I placing the fetching code in the right location (i.e. viewWillAppear)? If not, where should I put it instead to ensure it executes before the table actually attempts loading.
Thanks,
You have to initialize the PFObject array like the others.
var objectsArray = [PFObject]()
and you have to call reloadData() on the table view instance on the main thread right after populating objectsArray.
query.findObjectsInBackgroundWithBlock {(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
self.objectsArray = objects!
dispatch_async(dispatch_get_main_queue()) {
self.tableView.reloadData()
}
} else {
// Log details of the failure
print("Error: \(error!) \(error!.userInfo)")
}
SwiftSpinner.hide()
}
Related
Im new in Parse(parse.com). I have such kind of table in parse.com:
And I wanna retrieve these 3 images and put are in table view row. And here is my code:
class LeaguesTableViewController: UITableViewController {
var leagues = [PFObject]() {
didSet {
tableView.reloadData()
}
}
var leaguesImage = [NSData]() {
didSet {
tableView.reloadData()
}
}
override func viewDidLoad() {
super.viewDidLoad()
loadData()
tableView.registerClass(LeaguesTableViewCell.self, forCellReuseIdentifier: "ReusableCell")
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return leagues.count
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return 160
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("ReusableCell", forIndexPath: indexPath) as! LeaguesTableViewCell
cell.leagueImage.image = UIImage(data: leaguesImage[indexPath.row])
cell.leagueNameLabel.text = leagues[indexPath.row]["name"] as? String
return cell
}
// MARK: Parse
func loadData() {
let query = PFQuery(className: "Leagues")
query.findObjectsInBackgroundWithBlock { (objects, error) in
if( objects != nil && error == nil) {
// List of leagues
for i in objects! {
self.leagues.append(i)
// Retrieve images
let imageFile = i["image"] as? PFFile
imageFile!.getDataInBackgroundWithBlock { (imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let imageData = imageData {
self.leaguesImage.append(imageData)
}
}
}
}
} else if error != nil {
print("Error is: \(error)")
}
}
}
}
Here is my code and from my point of view is everything is ok. But I have error: Index out of the range. My leaguesImages array is empty. Thank you.
Your problem is that leagues and leaguesImages are getting out of sync. Once you retrieve the array from Parse, you are adding the leagues immediately, but leaguesImages are only being added after getDataInBackgroundWithBlock completes.
Instead of downloading the image data right away and storing it in a separate array, I would add a leagues property to your custom cell, and in there I would download the data and apply the image.
Populating an array like you are populating the leaguesImages array is a bad idea when the order matters, because you don't know which one will finish downloading first, maybe the second league image is the smallest, and it will be set as the image for the first league. (PS: image size is not the only thing that dictates how long a download will take)
I have my tableview returning titles, their descriptions and now I am trying to return images. It currently returns only one image for all of my cells. Is this because I'm storing it in a UIImage?
Here's my code:
import UIKit
import Parse
import Bolts
import ParseUI
class YourEvents: UIViewController, UITableViewDataSource, UITableViewDelegate {
#IBOutlet weak var tableView: UITableView!
var currentuser = PFUser.currentUser()?.username
//array
var testArray = [String]()
var testdecr = [String]()
var image = UIImage()
// var imagestored = UIImage()
override func viewDidLoad() {
super.viewDidLoad()
var query = PFQuery(className:"Companies")
let pUserName = PFUser.currentUser()?["username"] as? String
query.whereKey("createdby", equalTo:"\(pUserName)")
// let runkey = query.orderByAscending("companyname")
query.findObjectsInBackgroundWithBlock{
(objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
//do something with the found objects
if let objects = objects as [PFObject]! {
for object in objects {
let load = object.objectForKey("companyname") as! String
self.testArray .append(load)
print(self.testArray)
let load2 = object.objectForKey("companydescription") as! String
self.testdecr.append(load2)
print(self.testdecr)
if let userImageFile = object["imagefile"] as? PFFile {
userImageFile.getDataInBackgroundWithBlock {
(imageData: NSData?, error: NSError?) -> Void in
if error == nil {
if let imageData = imageData {
self.image = UIImage(data:imageData)!
print("done!")
self.do_table_refresh()
}
}
}
}
}
}
} else {
//log details of failure
print("Error: \(error!) \(error?.userInfo) ")
}
}
// reload UIViewController and UITabkeView
sleep(3)
do_table_refresh()
}
func do_table_refresh () {
dispatch_async(dispatch_get_main_queue(), {
self.tableView.reloadData()
return
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return testArray.count
}
func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("yourstartups", forIndexPath: indexPath) as! YourStartupsCell
cell.lbTitle!.text = self.testArray[indexPath.row]
cell.lbDescription!.text = self.testdecr[indexPath.row]
cell.logo!.image = self.image
return cell
}
}
I would recommend making an array of PFImage objects, and then in your table view delegate method you can simply access the element at the current row in your index path. Right now your method to get the data is being called once and therefore setting your image to the last fetched object, but since the tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) is being called each time a cell is loaded, you need to keep the images in an array, as you are doing with the text labels.
I'd like to append the 'userVotes' column in the following parse table into an array using Swift -
Here is my code -
import UIKit
import Parse
class MusicPlaylistTableViewController: UITableViewController {
var usernames = [String]()
var songs = [String]()
var voters = [String]()
var numVotes = 0
override func viewDidLoad() {
super.viewDidLoad()
tableView.separatorColor = UIColor.grayColor()
let query = PFQuery(className:"PlaylistData")
query.findObjectsInBackgroundWithBlock { (objects: [PFObject]?, error: NSError?) -> Void in
if error == nil {
if let objects = objects! as? [PFObject] {
self.usernames.removeAll()
self.songs.removeAll()
self.voters.removeAll()
for object in objects {
let username = object["username"] as? String
self.usernames.append(username!)
let track = object["song"] as? String
self.songs.append(track!)
let title = object["userVotes"]! as? String
self.voters.append(title!)
print("Array: \(self.voters)")
}
self.tableView.reloadData()
}
} else {
print(error)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: - Table view data source
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Incomplete implementation, return the number of sections
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete implementation, return the number of rows
return usernames.count
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("CellTrack", forIndexPath: indexPath) as! TrackTableViewCell
//cell.username.text = usernames[indexPath.row]
cell.username.text = usernames[indexPath.row]
cell.songTitle.text = songs[indexPath.row]
cell.votes.text = "\(numVotes)"
cell.selectionStyle = UITableViewCellSelectionStyle.None
return cell
}
override func tableView(tableView: UITableView, didDeselectRowAtIndexPath indexPath: NSIndexPath) {
}
}
I would like the parse array column to append as follows -
[["user1,"user5,"user9"],["user1,"user2,"user3"],["user4,"user5,"user6"],...]
At this point, I'm getting the following runtime error - fatal error: unexpectedly found nil while unwrapping an Optional value
Since each object that is in your "userVotes" is an array and your you've declared
var voters = [String]()
which is not right because you're saying that there will be one element being appended which is not the case.
So, you should declare voters as...
var voters = Array<Array<String>>()
then as you are downloading it,
for object in objects {
let title = object["userVotes"]! as? [String]
self.voters.append(title!)
print("Array: \(self.voters)")
}
I have a PFQuery with includeKey and then I pass the object to an array named 'queryArray'. In the function cellForRowAtIndexPath I'm trying to access the array without success.
import UIKit
import Parse
class OrdensCompraTableViewController: UITableViewController {
var queryArray: [PFObject] = [PFObject]()
override func viewDidLoad() {
super.viewDidLoad()
var query = PFQuery(className:"Transacao")
query.includeKey("pointerUser")
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
println("Successfully retrieved \(objects!.count) pedidos.")
// Do something with the found objects
if let objects = objects as? [PFObject] {
self.queryArray = objects
}
} else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
}
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return 1
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> OrdensCompraTableViewCell {
let cell = tableView.dequeueReusableCellWithIdentifier("OrdensCompraCell", forIndexPath: indexPath) as! OrdensCompraTableViewCell
println(queryArray)
// let transacao = queryArray[indexPath.row] as PFObject
// cell.tituloCecula.text = transacao.objectForKey("objectId") as! String
// var ola = transacao.relationForKey("pointerUser.username")
//println( transacao.objectForKey("aceite"))
return cell
}
You never actually reload the tableview after the Parse data comes in. Also your numberOfRowsInSection is hard coded which is why the tableView thinks there is data in the array when there is not.
if let objects = objects as? [PFObject] {
self.queryArray = objects
// RELOAD TABLEVIEW
self.tableView.reloadData()
}
And To fix the number of rows in section you should return self.queryData.count
First things first, if your going to add your parse objects to an array, you should add it to an array:
var queryArray:NSMutableArray = []
override func viewDidLoad() {
self.queryArray = NSMutableArray()
..findObjectsInBackgroundWithBlock {
(objects: [AnyObject]!, error: NSError!) -> Void in
if error == nil {
for object in objects {
self.queryArray.addObject(object)
//reload UI components
}
Then your numberOfSectionsInTableView should always reflect the number in that array :
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return self.queryArray.count
}
Your problem is this line method should return the self.queryArray.count*
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int
And your forgot to reloadData() after you assign the query data to your array of PFObject
And cellForRowAtPath you need to assign the cell object to the array of data at each indexPath.row
I can retrieve the information from parse, also I can print that array, but when I try to put those array element in table view cell nothing shows up
the array.count also return 4
I don't know why table cell is empty
import UIKit
import Parse
class friendsViewController: UITableViewController {
var user = PFUser.currentUser()
var friends = [String]()
var profileFile = [PFFile]()
var height : CGFloat = UIScreen.mainScreen().bounds.size.height
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
//friends
var query = PFQuery(className:"Requests")
//query.orderByDescending("requestBy")
query.whereKeyExists("requestTo")
query.whereKey("requestBy", equalTo: PFUser.currentUser()!.username!)
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
println("Friends recieved \(objects!.count) scores.")
// Do something with the found objects
if let objects = objects as? [PFObject] {
for object in objects {
println(object)
self.friends.append(object["requestTo"] as! String)
println(self.friends)
}
} else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
println(self.friends.count)
}
}
}
override func numberOfSectionsInTableView(tableView: UITableView) -> Int {
// #warning Potentially incomplete method implementation.
// Return the number of sections.
return 1
}
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
// #warning Incomplete method implementation.
// Return the number of rows in the section.
return friends.count
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
return (height/7)
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let myCell1 = tableView.dequeueReusableCellWithIdentifier("friendCell") as! friendsTableViewCell
myCell1.friendLabel.text = friends[indexPath.row]
return myCell1
}
}
friends is populared after the table view loads. After you've received your data, on the main thread, call tableView.reloadData().
When your block is finished, please call your table to reload data.
Fixed it...
query.findObjectsInBackgroundWithBlock {
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil {
// The find succeeded.
println("Friends recieved \(objects!.count) scores.")
// Do something with the found objects
if let objects = objects as? [PFObject] {
for object in objects {
println(object)
self.friends.append(object["requestTo"] as! String)
println(self.friends)
}
} else {
// Log details of the failure
println("Error: \(error!) \(error!.userInfo!)")
}
println(self.friends.count)
yourTableView.reloadData()
}
}