This is the craziest error I've encountered ever. I am trying to get the value from a column called nameRetailer in the Orders table, but it keeps getting nil.
Other columns of same String type are returning properly including the status column shown below.
What could lead to this? The spelling is certainly correct. Please help....
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let date = object?.objectForKey("dueDate") as! NSDate
let strDate = dateFormatter.stringFromDate(date)
cell.retailerName.text = object?.objectForKey("nameRetailer") as? String
cell.orderDueDate.text = strDate
cell.orderStatus.text = object?.objectForKey("status") as? String
When I tried to print the value of object?.objectForKey("nameRetailer"), it shows nil in console. In the parse data browser, column has data and was refreshed.
Update: Adding additional code:
The entire class code responsible for the table view:
class OrderViewController: PFQueryTableViewController {
override func queryForTable() -> PFQuery {
let query = PFQuery(className: "Orders")
query.cachePolicy = .CacheElseNetwork
query.orderByAscending("createdAt")
return query
}
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath, object: PFObject?) -> PFTableViewCell? {
let cell = tableView.dequeueReusableCellWithIdentifier("cell", forIndexPath: indexPath) as! OrdersTableViewCell
let dateFormatter = NSDateFormatter()
dateFormatter.dateFormat = "yyyy-MM-dd"
let date = object?.objectForKey("dueDate") as! NSDate
let strDate = dateFormatter.stringFromDate(date)
cell.retailerName.text = object?.objectForKey("nameRetailer") as? String
cell.orderDueDate.text = strDate
cell.orderStatus.text = object?.objectForKey("status") as? String
print(object)
//let imageFile = object?.objectForKey("image") as PFFile
//cell.cellImageView.image = UIImage(named:"placeholder")
//cell.cellImageView.file = imageFile
//cell.cellImageView.loadInBackground()
return cell
}
override func tableView(tableView: UITableView, heightForRowAtIndexPath indexPath: NSIndexPath) -> CGFloat {
if indexPath.row + 1 > self.objects?.count
{
return 44
}
let height = super.tableView(tableView, heightForRowAtIndexPath: indexPath)
return height
}
override func tableView(tableView: UITableView, didSelectRowAtIndexPath indexPath: NSIndexPath) {
if indexPath.row + 1 > self.objects?.count
{
self.loadNextPage()
tableView.deselectRowAtIndexPath(indexPath, animated: true)
self.performSegueWithIdentifier("showDetail", sender: self)
}
}
An image of the table of orders:
and here is the log snapshot of printing PFObject:
And here is the updated snapshots showing the two rows
You have set your cache policy to start by looking up data in the local cache, which may be stale.
Change:
query.cachePolicy = .CacheElseNetwork
to
query.cachePolicy = .NetworkOnly // ignores cache on reading, but saves to cache
or
query.cachePolicy = .IgnoreCache // no cache at all -- this is the default
(or other appropriate value based on your specific context and use case)
Related
I am trying to use Firestore pagination with swift TableView. Here is my code which loads the first 4 posts from firestore.
func loadMessages(){
let postDocs = db
.collectionGroup("userPosts")
.order(by: "postTime", descending: false)
.limit(to: 4)
postDocs.addSnapshotListener { [weak self](querySnapshot, error) in
self?.q.async{
self!.posts = []
guard let snapshot = querySnapshot else {
if let error = error {
print(error)
}
return
}
guard let lastSnapshot = snapshot.documents.last else {
// The collection is empty.
return
}
let nextDocs = Firestore.firestore()
.collectionGroup("userPosts")
.order(by: "postTime", descending: false)
.start(afterDocument: lastSnapshot)
if let postsTemp = self?.createPost(snapshot){
DispatchQueue.main.async {
self!.posts = postsTemp
self!.tableView.reloadData()
}
}
}
}
}
func createPost(_ snapshot: QuerySnapshot) ->[Post]{
var postsTemp = [Post]()
for doc in snapshot.documents{
if let firstImage = doc.get(K.FStore.firstImageField) as? String,
let firstTitle = doc.get(K.FStore.firstTitleField) as? String,
let secondImage = doc.get(K.FStore.secondImageField) as? String,
let secondTitle = doc.get(K.FStore.secondTitleField) as? String,
let userName = doc.get(K.FStore.poster) as? String,
let uID = doc.get(K.FStore.userID) as? String,
let postDate = doc.get("postTime") as? String,
let votesForLeft = doc.get("votesForLeft") as? Int,
let votesForRight = doc.get("votesForRight") as? Int,
let endDate = doc.get("endDate") as? Int{
let post = Post(firstImageUrl: firstImage,
secondImageUrl: secondImage,
firstTitle: firstTitle,
secondTitle: secondTitle,
poster: userName,
uid: uID,
postDate: postDate,
votesForLeft: votesForLeft,
votesForRight:votesForRight,
endDate: endDate)
postsTemp.insert(post, at: 0)
}else{
}
}
return postsTemp
}
Here is my delegate which also detects the end of the TableView:
override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let post = posts[indexPath.row]
let cell = tableView.dequeueReusableCell(withIdentifier: K.cellIdentifier, for: indexPath) as! PostCell
cell.delegate = self
let seconds = post.endDate
let date = NSDate(timeIntervalSince1970: Double(seconds))
let formatter = DateFormatter()
formatter.dateFormat = "M/d h:mm"
if(seconds <= Int(Date().timeIntervalSince1970)){
cell.timerLabel?.text = "Voting Done!"
}else{
cell.timerLabel?.text = formatter.string(from: date as Date)
}
let firstReference = storageRef.child(post.firstImageUrl)
let secondReference = storageRef.child(post.secondImageUrl)
cell.firstTitle.setTitle(post.firstTitle, for: .normal)
cell.secondTitle.setTitle(post.secondTitle, for: .normal)
cell.firstImageView.sd_setImage(with: firstReference)
cell.secondImageView.sd_setImage(with: secondReference)
cell.userName.setTitle(post.poster, for: .normal)
cell.firstImageView.layer.cornerRadius = 8.0
cell.secondImageView.layer.cornerRadius = 8.0
if(indexPath.row + 1 == posts.count){
print("Reached the end")
}
return cell
}
Previously I had an addSnapshotListener without a limit on the Query and just pulled down all posts as they came. However I would like to limit how many posts are being pulled down at a time. I do not know where I should be loading the data into my model. Previously it was being loaded at the end of the addSnapshotListener and I could still do that, but when do I use the next Query? Thank you for any help and please let me know if I can expand on my question any more.
There is a UITableViewDelegate method called tableView(_ tableView: UITableView, willDisplay cell: UITableViewCell, forRowAt indexPath: IndexPath) that will be called just before a cell is loading.
You could use this one to check if the row at IndexPath is in fact the cell of the last object in your tableview's datasource. Something like datasource.count - 1 == IndexPath.row (The -1 is to account for item 0 being the first item in an array, where as it already counts as 1).
If that object is indeed the last one in your datasource, you could make a call to Firebase and add items to the datasource. Before mutating the datasource, make sure to check the new number of objects the show (the ones already loaded + new ones) has to be larger than the current number of objects in the datasource, otherwise the app will crash.
You also might want to give your user a heads up that you're fetching data. You can trigger that heads up also in the delegate method.
I have a detail view that shows the details of an event, the people who participate and the people who asked to participate. I have created two arrays of different types but they have the same fields, only that a first structure represents the users with the 'status_confirm' field equal to 1 (therefore Accepted Users), while the other has as 'status_confirm' equal to 0 (Users awaiting acceptance). I declared two arrays, the first one: var arrayUserAccepted = [User_accepted] ().
The second one: var arrayUserWaiting = [User_waiting] (). Struct Image
Next step: I populate these structures via a php script
func getData(){
let url = URL(string: “MYURL”)
URLSession.shared.dataTask(with:url!, completionHandler: {(data, response, error) in
guard let data = data, error == nil else { return }
do {
let json = try JSONSerialization.jsonObject(with: data, options: []) as! [String:AnyObject]
print("JSON: \n\(json)\n")
let waiting = json["waiting"] as! [AnyObject]
let accepted = json["accepted"] as! [AnyObject]
DispatchQueue.main.async {
for list_user_waiting in waiting {
let id_user_waiting = list_user_waiting["id_user”] as! String
let name_user_waiting = list_user_waiting[“name_user”] as! String
let email_user_waiting = list_user_waiting["email"] as! String
var photo_user_waiting = list_user_waiting[“photo”]
let status_user_waiting = list_user_waiting["status”] as! String
if photo_user_waiting is NSNull {
photo_user_waiting = ""
}
let listUserWaiting = User_waiting(id_user_waiting: id_user_waiting, name_user_waiting: name_user_waiting, email_user_waiting: email_utente_attesa, foto_waiting: photo_user_waiting as! String, status_waiting: status_user_waiting)
self.arrayUserWaiting.append(listUserWaiting)
self.tableViewListUserWaiting.reloadData()
}
for list_user_accepted in accepted {
let id_user_accepted = list_user_accepted["id_utente"] as! String
let name_user_accepted = list_user_accepted["name_utente"] as! String
let email_user_accepted = list_user_accepted["email"] as! String
var photo_user_accepted = list_user_accepted[“photo"]
let status_user_accepted = list_user_accepted["status”] as! String
if photo_user_accepted is NSNull {
photo_user_accepted = ""
}
let listUserAccepted = User_accepted(id_user: id_user_accepted, nome_utente: name_user_accepted, email: email_user_accepted, foto: photo_user_accepted as! String, stato: status_user_accepted)
self.arrayUserAccepted.append(listUserAccepted)
self.tableViewListUserAccepted.reloadData()
}
}
} catch let error as NSError {
print(error)
}
}).resume()}
This above is a function that I call in the viewDidLoad(). The next step would be to use the functions of the table view and it is here that I think there is the injunction
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var count: Int?
if tableView == self.tableViewListUserAccepted {
count = arrayUserAccepted.count
}
if tableView == self.tableViewListUserWaiting {
count = arrayUserWaiting.count
}
return count!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
if tableView == self.tableViewListUserAccepted {
cell.imageProfileUserAccepted.image = UIImage(named: "imageDefault")
cell.valueSliderUserAccepted.value = Float(50) //JUST FOR POPULATE THE INTERFACE
cell.name_user_accepted.text = arrayUserAccepted[indexPath.row].name_user
}
if tableView == self.tableViewListUserWaiting {
cell.imageProfileUserWaiting.image = UIImage(named: "imageDefault")
cell.valueSliderUserWaiting.value = Float(23) //JUST FOR POPULATE THE INTERFACE
cell.name_user_waiting.text = arrayUserWaiting[indexPath.row].name_user_waiting
}
return cell
}
Once done all this round, I start the application but nothing. The tables are empty. In the console the script answers me correctly and so I can not figure out where the error could be. Needless to say, I have declared the .delegate and .dataSource of both tables, both in the Main.Storyboard and in the code.
Everything is fine just change the format of IF condition and it will work.
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
var count: Int?
if tableView == self.tableViewListUserAccepted {
count = arrayUserAccepted.count
} else {
count = arrayUserWaiting.count
}
return count!
}
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
if tableView == self.tableViewListUserAccepted {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.imageProfileUserAccepted.image = UIImage(named: "imageDefault")
cell.valueSliderUserAccepted.value = Float(50) //JUST FOR POPULATE THE INTERFACE
cell.name_user_accepted.text = arrayUserAccepted[indexPath.row].name_user
return cell
} else {
let cell = tableView.dequeueReusableCell(withIdentifier: "cell", for: indexPath) as! TableViewCell
cell.imageProfileUserWaiting.image = UIImage(named: "imageDefault")
cell.valueSliderUserWaiting.value = Float(23) //JUST FOR POPULATE THE INTERFACE
cell.name_user_waiting.text = arrayUserWaiting[indexPath.row].name_user_waiting
return cell
}
}
Also check if the datasource and delegate of both of your tableView are set. Finally call the tableView.reloadTable() method on both of your tableviews after you populate your arrays in the viewDidLoad() method.
I've been struggling with this for several days already. There are similar problems in this website, but not very the same. And I didn't manage to go forward. I will try to simplify it with one variable.
Problem:
After filtering records in UITableView (records are taken from core data) and trying to push data to another viewcontroller, I get unfiltered index for data, so incorrect data is pushed to new view controller.
My code is below:
I set global variable for core data:
var events : [Event] = []
#objc func textFieldDidChange(_ textField: UITextField) {
if searchField.text == "" {
filterAdded = false
} else {
filterAdded = true
let request:NSFetchRequest<Event> = Event.fetchRequest()
let predicate = NSPredicate(format: "name CONTAINS[c] %# AND nearestDate >= %#", searchField.text!, currentCorrectDate! as CVarArg)
request.predicate = predicate
let sortDescriptor = NSSortDescriptor(key: "nearestDate", ascending: true)
request.sortDescriptors = [sortDescriptor]
do {
events = try DatabaseController.getContext().fetch(request)
}
catch {
print("Error: \(error)")
}
mainListOfDates.reloadData()
}
}
}
It is triggered every time some character is added to search field. UITableView name is "mainListOfDates".
This function works properly and calculated only filtered events:
func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
return events.count }
This function shows all records from core data in UITableView cells:
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
let cell = tableView.dequeueReusableCell(withIdentifier: "eventCell", for: indexPath) as! EventTableViewCell
let event = events[indexPath.row]
cell.eventNameLabel.text = event.value(forKeyPath: "name") as? String
return cell
}
And with "didSelectRowAt" I would like to push filtered or unfiltered (works perfectly with unfiltered) data to new view controller:
let Storyboard = UIStoryboard(name: "Main", bundle: nil)
let eventStoryboard = Storyboard.instantiateViewController(withIdentifier: "EventViewController") as! EventViewController
let cell = tableView.dequeueReusableCell(withIdentifier: "eventCell", for: indexPath) as! EventTableViewCell
eventStoryboard.getEventName = events[indexPath.row].name ?? "nil"
self.navigationController?.pushViewController(eventStoryboard, animated: false) }
How to solve this issue and send filtered correct data to new view controller?
Thanks in advance.
I have a an entity in core data that represents a workout model. Each entity has a DayID attribute that represents which day the item falls into as shown in the image.
The code to create and save a specific item is shown below in Swift
#IBAction func doneButtonPressed(sender: AnyObject) {
let appDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
let managedObjectContextCreation = appDelegate.managedObjectContext
let entityDescription = NSEntityDescription.entityForName("ExerciseItemModel", inManagedObjectContext: managedObjectContextCreation)
//Sunday
let exerciseItemtoAdd = ExerciseItemModel(entity: entityDescription!, insertIntoManagedObjectContext: managedObjectContextCreation)
exerciseItemtoAdd.dayID = dayName //DayName could be Wednesday
exerciseItemtoAdd.exerciseType = "Cardio"
exerciseItemtoAdd.exerciseName = self.exerciseNameTextField.text!
exerciseItemtoAdd.durationOrSets = durationLabelTextArea.text!
exerciseItemtoAdd.distanceOrReps = distanceLabelTextArea.text!
exerciseItemtoAdd.weight = ""
exerciseItemtoAdd.date = currentDate
appDelegate.saveContext() //Save the elements I just created.
let request: NSFetchRequest = NSFetchRequest(entityName: "ExerciseItemModel")
do {
_ = try managedObjectContextCreation.executeFetchRequest(request)
// success ...
} catch let error as NSError {
// failure
print("Fetch failed: \(error.localizedDescription)")
}
self.dismissViewControllerAnimated(true, completion: nil)
}
I can fetch items specific to a day e.g. Wednesday in cellForRowAtIndexPath using the following code:
override func tableView(tableView: UITableView, cellForRowAtIndexPath indexPath: NSIndexPath) -> UITableViewCell {
let exerciseItem = fetchedResultController.objectAtIndexPath(indexPath) as! ExerciseItemModel
//Check if the DayID is the same as a specific WeekDay e.g. Wednesday
if exerciseItem.dayID == self.weekDayModel.dayName {
//All the code here
}
return Cell!
}
The problem I am hence facing is how do I specify the numbeOfRows in section function to only return the number of items related to a specific dayName. eg. I want only the number of items whose DayID is Wednesday.
At the moment the function return all the entities including those with other dayID's from Sunday to Saturday and hence distorts the TableView.
Here is my numberOfRowsInSection function:
override func tableView(tableView: UITableView, numberOfRowsInSection section: Int) -> Int {
//let exerciseItem = fetchedResultController.
//if exerciseItem.dayID == self.weekDayModel.dayName {
//}
numberOfExerciseItems = fetchedResultController.sections![section].numberOfObjects
return fetchedResultController.sections![section].numberOfObjects
}
Any thoughts or help is greatly appreciated. :)
You want to add a predicate to your NSFetchRequest like so:
let request: NSFetchRequest = NSFetchRequest(entityName: "ExerciseItemModel")
request.predicate = NSPredicate("dayID == %#",self.weekDayModel.dayName)
I am working on an app that allows you to 'Like' posts. I was implementing the like button, but I got an error that I cannot seem to fix.
I searched in another posts, but I'm unsure of how to fix it.
This is the code I'm using to implement the like button. Do I need to import something into my project? Or unwrap at certain point?
Any help is appreciate it.
override func tableView(tableView: UITableView?, cellForRowAtIndexPath indexPath: NSIndexPath?) -> UITableViewCell {
let cell:PostTableViewCell = tableView!.dequeueReusableCellWithIdentifier("Cell", forIndexPath: indexPath!) as! PostTableViewCell
let post = self.objectAtIndexPath(indexPath!) as PFObject
cell.postTextView.alpha = 0
cell.usernameLabel.alpha = 0
cell.timestampLabel.alpha = 0
cell.postTextView.text = post.objectForKey("content") as! String
var dataFormatter:NSDateFormatter = NSDateFormatter()
dataFormatter.dateFormat = "yyyy-MM-dd HH:mm"
cell.timestampLabel.text = dataFormatter.stringFromDate(post.createdAt!)
// to get username from the post
var showUsername:PFQuery = PFUser.query()!
//the objectID is the same as the user in the two different tables
showUsername.whereKey("objectId", equalTo: post.objectForKey("user")!.objectId!!)
showUsername.findObjectsInBackgroundWithBlock{
(objects: [AnyObject]?, error: NSError?) -> Void in
if error == nil{
let user = (objects as! [PFUser]).last
cell.usernameLabel.text = user!.username
UIView.animateWithDuration(0.5, animations: {
cell.postTextView.alpha = 1
cell.usernameLabel.alpha = 1
cell.timestampLabel.alpha = 1
})
}
}
return cell
}
func objectAtIndexPath(indexPath: NSIndexPath) -> PFObject {
return self.timelineData[indexPath.row] as! PFObject
}
#IBAction func likeButton(sender: UIButton) {
//disables the like button so it can't be pressed again
sender.enabled = false
sender.userInteractionEnabled = false
sender.alpha = 0.5
//get the point in the table view that corresponds to the button that was pressed
//in my case these were a bunch of cells each with their own like button
let hitPoint = sender.convertPoint(CGPointZero, toView: self.tableView)
let hitIndex = self.tableView.indexPathForRowAtPoint(hitPoint)
let object = self.objectAtIndexPath(hitIndex!) as PFObject
//this is where I incremented the key for the object
object.incrementKey("likes")
object.saveInBackground() //still gives me error here
self.tableView.reloadData()
NSLog("Top Index Path \(hitIndex?.row)")
}
Update2: Added a photo of the error
Since the view controller is neither an NSFetchedResultsController or a PFQueryTableViewController, you'll have to implement objectAtIndexPath: yourself.
A hint about the code you need is in cellForRowAtIndexPath':
let post:PFObject = self.timelineData.objectAtIndex(indexPath!.row) as! PFObject
Dispensing with the objectAtIndex method on array, just index into the array at the row:
func objectAtIndexPath(indexPath: NSIndexPath) -> PFObject {
return self.timelineData[indexPath.row] as! PFObject
}
Call it wherever the old code appears like this (in likeButton)...
let object = self.objectAtIndexPath(hitIndex) as! PFObject
or, in cellForRowAtIndexPath:...
let post = self.objectAtIndexPath(indexPath) as!PFObject
etc.