I have a friendslist view controller that allows the user to see pending friend requests. The isPending method is first used to see if a particular user has a pending friend request with the current user.
-(BOOL)isPending:(PFUser *)user
{
for (PFUser *pending in self.Pending){
if ([pending.objectId isEqualToString:user.objectId]){
return YES;
}
}
return NO;
}
This method is called within cellForRowAtIndexPath method inside the TableViewController. THe problem that I am experiencing is that If I run the query inside the tableViewController users who are pending are properly displayed. When I moved the query to a singleton datasource class, I have setup a delegate method that is called when data is returned from the query.
-(void)pendingFriendsQuarryDidFinishWithData{
self.Pending = [NSMutableArray arrayWithArray:dataHolder.getPendingFriendsList];
[self.tableView reloadData];
}
Calling reloadData does not cause the checkmarks to appear next to the users names.
EDIT: Here is the code for cellForRowAtIndexPath. It does not change with the singleton.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *cellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier forIndexPath:indexPath];
// Configure the cell...
PFUser *user = [self.allUsers objectAtIndex:indexPath.row];
cell.textLabel.text = user.username;
cell.detailTextLabel.text = user.email;
cell.imageView.image = [UIImage imageNamed:#"Treehouse.png"];
if([self isPending:user]){
cell.accessoryType = UITableViewCellAccessoryCheckmark;
}
return cell;
}
What does change is how the data is obtained. Without the singleton here is the code
-(void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
/*
self.currentUser = [PFUser currentUser];
if (self.currentUser != nil){
PFQuery *userQuery = [PFUser query];
PFQuery *pendingUser = [PFUser query];
PFRelation *friendRelation = [self.currentUser relationForKey:#"friendRelation"];
PFQuery *existingFriends = [friendRelation query];
PFQuery *pendingFriends = [PFQuery queryWithClassName:#"FriendRequest"];
userQuery.cachePolicy = kPFCachePolicyCacheThenNetwork;
pendingFriends.cachePolicy = kPFCachePolicyCacheThenNetwork;
[pendingFriends whereKey:#"fromUser" equalTo:self.currentUser.objectId];
[pendingFriends whereKey:#"status" equalTo:#"Pending"];
[userQuery whereKey:#"objectId"doesNotMatchKey:#"toUser" inQuery:pendingFriends];
[userQuery whereKey:#"objectId" doesNotMatchKey:#"objectId" inQuery:existingFriends];
[userQuery orderByAscending:#"username"];
[userQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error){
NSLog(#" Error %# %#", error, [error userInfo] );
}else{
NSLog(#"All Users Array Starts Here: %#", objects);
self.allUsers = objects;
[self.tableView reloadData];
}
}];
[pendingUser whereKey:#"objectId" matchesKey:#"toUser" inQuery:pendingFriends];
[pendingUser orderByAscending:#"username"];
[pendingUser findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (error){
NSLog(#"%# %#", error, [error userInfo]);
}else{
[self.Pending addObjectsFromArray:objects];
NSLog(#"Pending Friends Array Stars here: %#", self.Pending);
}
}];
}
*/
}
With singleton
- (void)viewDidLoad
{
dataHolder = [DataHolder sharedInstance];
dataHolder.delegate = self;
self.friends = [NSMutableArray new];
self.allUsers = [NSMutableArray new];
self.allUsers = [NSMutableArray arrayWithArray:dataHolder.getAllUsersWithQuarry];
self.Pending = [NSMutableArray new];
self.Pending = [NSMutableArray arrayWithArray:dataHolder.getPendingFriendsListWithQuarry];
[super viewDidLoad];
NSLog(#"Now in Connection Editor");
}
-(void)allUsersQuarryDidFinishWithData{
self.allUsers = [NSMutableArray arrayWithArray:dataHolder.getAllUsers];
[self.tableView reloadData];
}
-(void)pendingFriendsQuarryDidFinishWithData{
self.Pending = [NSMutableArray arrayWithArray:dataHolder.getPendingFriendsList];
[self.tableView reloadData];
}
Silly question, did you set the datasource of your tableviewcontroller to point to your singleton datasource?
tableview.datasource = [your singleton class instance]
and make sure that you step into it in the debugger.
Hope this helps.
Related
I am building a social app and am currently building a PFQueryTable View Controller that I would like to display a list of followers for the user selected in a previous screen.
under queryForTable i have the below query set:
-(void) queryForTable {
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
[query whereKey:#"type" equalTo:#"follow"];
[query whereKey:#"fromUser" equalTo:self.user];
[query whereKey:#"toUser" notEqualTo:self.user];
[query whereKeyExists:#"toUser"];
[query includeKey:#"toUser"];
[query orderByDescending:#"createdAt"];
[query setCachePolicy:kPFCachePolicyNetworkOnly];
return query;
}
When I perform a NSLog for object under cellForRowAtIndexPath:
- (FollowViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
static NSString *FollowCellIdentifier = #"FriendCell";
FollowViewCell *cell = [tableView dequeueReusableCellWithIdentifier:FollowCellIdentifier];
if (cell == nil) {
cell = [[FollowViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:FollowCellIdentifier];
}
NSLog(#"OBJECT IS %#",object);
cell.userLabel.text = [[object objectForKey:#"toUser"] objectForKey:#"username"];
return cell;
}
the result is
OBJECT IS {
ACL = "";
fromUser = "";
toUser = "";
type = follow; }
If I try to then create:
cell.userLabel.text = [[object objectForKey:#"toUser"] objectForKey:#"username"];
The console gives me an error of:
'Key "username" has no data. Call fetchIfNeeded before getting its
value.'
If I step the process with breaks,the userLabel.text populates fine. but if I run it without the breaks I always get the error. Is there something I am missing here?
Any help would be great as I have been trying to fix this for the past 4 days.
Try:
PFUser *user = (PFUser *)[object objectForKey:#"toUser"];
[user fetchIfNeededInBackgroundWithBlock:^(PFObject *object, NSError *error) {
cell.userLabel.text = [object objectForKey:#"username"];
}];
This question already has an answer here:
Pass data from Parse tableview to WatchKit
(1 answer)
Closed 8 years ago.
I'm using Parse to create this table view, and am trying to figure out how to get the Parse table data into an array, so I can pass it into the WatchKit InterfaceController to show the exact same thing?
So I want to show in the WatchKit interface exactly what shows in the iPhone interface.
Here is what I have, let me know if I can add anything that would be helpful:
TableVC.m:
- (id)initWithCoder:(NSCoder *)aCoder
{
self = [super initWithCoder:aCoder];
if (self) {
self.parseClassName = #"na";
self.textKey = #"dateTime";
self.pullToRefreshEnabled = YES;
self.paginationEnabled = NO;
}
return self;
}
- (PFQuery *)queryForTable
{
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
return query;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object
{
static NSString *simpleTableIdentifier = #"RecipeCell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:simpleTableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:simpleTableIdentifier];
}
UILabel *homeLabel = (UILabel*) [cell viewWithTag:101];
homeLabel.text = [object objectForKey:#"test"];
UILabel *dateLabel = (UILabel*) [cell viewWithTag:102];
dateLabel.text = [object objectForKey:#"dateTime"];
return cell;
}
Parse data:
TableVC.m:
I already have the basic WatchKit files and Storyboard set up. I hard coded an array to test that it was generally working. But now I just need to get the data from Parse into there, and not sure if I need to do a query and then turn that into a public array?
EDIT:
Here is my query:
PFQuery *query2 = [PFQuery queryWithClassName:#"nba"];
[query2 findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// The find succeeded.
NSLog(#"Objects 2: %#", objects);
}
} else {
// Log details of the failure
NSLog(#"Error 2: %# %#", error, [error userInfo]);
}
}];
Here is my NSLog:
NSLog(#"Objects 2: %#", objects);
Console:
2015-02-09 21:06:30.845 SimpleTable[8373:1284663] Objects 2: (
"<na: 0x7ff3f8e40880, objectId: cOrjeAmwJh, localId: (null)> {\n away = Cav;\n date = \"04/19/2015\";\n dateTime = \"April 19, 2015, 16:00\";\n gNumber = 1;\n home = Bul;\n matup = \"Ca\";\n ro = \"Ro \";\n test = \"Test 2\";\n tv = T;\n}",
If you need the array, fetch it asynchronously in a method outside of the queryForTable method, get it like this:
PFQuery *query = [self queryForTable];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
// objects is the array for this table
NSMutableArray *array = [#[] mutableCopy];
for (PFObject *object in objects) {
NSLog(#"we got an object with dateTime = %#", [object objectForKey:#"dateTime"]);
[array addObject:[object objectForKey:#"dateTime"]];
// you can prove this with any of your keys: away, number, home, mat up, etc.
}
}
}];
if you just want to pass the objects into an array you can do so like this or a variation of:
- (PFQuery *)queryForTable
{
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
self.someArray = [query findObjects];
return query;
}
REFERNECE
I currently have my system set up through parse.com
What Im trying to do is to be able to in a tableView or a PFQuerTableViewController be able to query the post which is a PFObject that my user has posted. Currently I have this code below and its only saying "Loading.." instead of displaying their postings. Why is it not displaying my user's last posts?
This is the query in which Im trying to fetch the users post:
- (PFQuery *)queryForTable {
PFQuery *postQuery = [PFQuery queryWithClassName:#"New"];
[postQuery whereKey:#"author" equalTo:[PFUser currentUser]];
// Run the query
[postQuery findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
//Save results and update the table
postArray = objects;
[self.tableView reloadData];
}
}];
}
And here is its cell:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
// If you want a custom cell, create a new subclass of PFTableViewCell, set the cell identifier in IB, then change this string to match
// You can access any IBOutlets declared in the .h file from here and set the values accordingly
// "Cell" is the default cell identifier in a new Master-Detail Project
static NSString *CellIdentifier = #"Cell";
PFTableViewCell *cell = (PFTableViewCell *)[tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[PFTableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier];
}
// Configure the cell
//cell.textLabel.text = [object objectForKey:self.textKey];
//cell.imageView.file = [object objectForKey:self.imageKey];
PFObject *post = [postArray objectAtIndex:indexPath.row];
[cell.textLabel setText:[post objectForKey:#"title"]];
return cell;
}
Also here is the code for how I am posting the PFObject:
-(IBAction)done:(id)sender {
PFUser *user = [PFUser currentUser];
PFObject *quoteNew = [PFObject objectWithClassName:#"New"];
[quoteNew setObject:user forKey:#"user"];
[quoteNew setObject:[[self quoteText] text] forKey:#"quoteText"];
[quoteNew setObject:[[self attributionTitle] text] forKey:#"title"];
MBProgressHUD *hud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
hud.mode = MBProgressHUDModeIndeterminate;
hud.labelText = #"Uploading";
[hud show:YES];
[quoteNew saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error) {
//[self done:self];
[hud hide:YES];
[self dismissViewControllerAnimated:YES completion:nil];
} else {
[hud hide:YES];
}
}];
You're trying to run the query in the queryForTable method. That's not what it's for. In queryForTable, you're supposed to build the query with the constraints that you want (like the author thing that you added) then return the query. So for your case:
- (PFQuery *)queryForTable {
PFQuery *postQuery = [PFQuery queryWithClassName:#"New"];
[postQuery whereKey:#"author" equalTo:[PFUser currentUser]];
// Don't run the query, return it so PFQueryTableViewcontroller can use it
return postQuery;
}
If you want to programmatically tell your PFQueryTableViewController to load new posts, you can run [self loadObjects].
Note: This only applies if you are subclassing PFQueryTableViewController
I'd recommend reading the documentation, it lays everything out fairly well here.
You are setting the key as user
[quoteNew setObject:user forKey:#"user"];
Then querying for author
[postQuery whereKey:#"author" equalTo:[PFUser currentUser]];
Try instead
[postQuery whereKey:#"user" equalTo:[PFUser currentUser]];
I think this is probably a simple fix, but I am struggling to find the right solution
I have the following:
ContactList - a UICollectionView that loads the content with viewDidAppear: method. When a item is selected in the collection view I fire this method:
[self performSegueWithIdentifier:#"pushContactDetail" sender:self];
So far so good. Everything works great. However when I navigate back from the ContactDetail page using the Navigation Controller button my ContactList duplicates the content.
Is there something I should be doing to prevent the viewDidAppear: from running again?
I don't think I want to set the collection to nil when I push the ContactDetail page as that would cause the content to be reloaded each time...
Here is the code:
-(void) viewDidAppear
{
[super viewDidAppear];
[self.view setBackgroundColor:myColorLightGrey];
_contactList = [[NSMutableArray alloc] init];
[self displayLoadingAndDisableTableViewInteractions];
[self queryData];
}
- (void) queryData
{
//Find the Data
PFQuery *query = [PFUser query];
PFUser *consumer = [PFUser currentUser];
[query includeKey:User_followers];
[query whereKey:#"email" equalTo:consumer.email];
query.maxCacheAge = 60 * 60 * 24; // One day, in seconds
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error)
{
for (PFUser *tmpConsumer in objects)
{
for (PFUser *publisher in [tmpConsumer objectForKey:User_followers])
{
[_contactList addObject:publisher];
}
}
[_collectionView reloadData];
[self hideLoadingAndEnableTableViewInteractions];
}];
}
-(UICollectionViewCell *)collectionView:(UICollectionView *)collectionView cellForItemAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *CellIdentifier = #"contactCell";
ContactCell *cell = [collectionView dequeueReusableCellWithReuseIdentifier:CellIdentifier forIndexPath:indexPath];
//SNIPPED REMAINDER OF CODE
...
}
In your queryData method, nil out the array first
_contactList = nil;
_contactList = [NSMutableArray array]; // or [[NSMutableArray alloc] init];
Move the alloc/init method for _contactList like this:
- (void) queryData
{
_contactList = nil;
_contactList = [[NSMutablArray alloc] init];
//Find the Data
PFQuery *query = [PFUser query];
PFUser *consumer = [PFUser currentUser];
[query includeKey:User_followers];
[query whereKey:#"email" equalTo:consumer.email];
query.maxCacheAge = 60 * 60 * 24; // One day, in seconds
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error)
{
for (PFUser *tmpConsumer in objects)
{
for (PFUser *publisher in [tmpConsumer objectForKey:User_followers])
{
[_contactList addObject:publisher];
}
}
[_collectionView reloadData];
[self hideLoadingAndEnableTableViewInteractions];
}];
}
Navigate back with Animation true. It will solve your problem.
I'm sitting on this almost 4 days and I can't find the problem (already searched google a lot and tried everything but nothing helped). I have this table view I created on my storyboard. Everything is connect BUT when I run my code:
First all the tableView method runs but since array still nil nothing happened. Then after my array got all the data and code says [tableView1 reloadData]; nothing happened and I'm not getting to the tableView methods... (I tried to locate the reloadData in many places in my code and nothing worked).
-(void)viewDidLoad {
[super viewDidLoad];
self.tableView1.dataSource=self;
self.tableView1.delegate=self;
self.tripNamesList = [[NSMutableArray alloc] init];
[self GetTripsList];
PFQuery *query = [PFQuery queryWithClassName:#"Trips"];
NSString *userID = user.objectId;
[query whereKey:#"User_Created_ID" equalTo:userID];
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSLog(#"Successfully retrieved %d trips.", objects.count);
[self.tripNamesList addObjectsFromArray:objects];
NSLog(#"%#", tripNamesList);
for (PFObject *object in objects) {
NSLog(#"%#", object.objectId);
[self.tripNamesList addObject:object ];
}
[tableView1 reloadData];
}
else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [self.tripNamesList count];
}
- (NSInteger)numberOfSectionsInTableView:(UITableView *)tableView {
return 1;
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSLog(#"Calling 1222rrgdfghgfhdgfdfgfdgdfgd on %#", tableView);
static NSString *CellIdentifier = #"TripCell";
UITableViewCell *cell = [self.tableView1 dequeueReusableCellWithIdentifier:CellIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:CellIdentifier] ;
}
PFObject *obj2 = [self.tripNamesList objectAtIndex:indexPath.row];
PFQuery *query = [PFQuery queryWithClassName:#"Trips"];
PFObject *searchedItems = [query getObjectWithId:obj2.objectId];
NSString *tempTripName = [searchedItems objectForKey:#"Trip_Name"];
cell.textLabel.text = tempTripName;
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
[self.tableView1 reloadData];
return cell;
}
#end
Try running the reloadData from the main thread:
[query findObjectsInBackgroundWithBlock:^(NSArray *objects, NSError *error) {
if (!error) {
NSLog(#"Successfully retrieved %d trips.", objects.count);
[self.tripNamesList addObjectsFromArray:objects];
NSLog(#"%#", tripNamesList);
for (PFObject *object in objects) {
NSLog(#"%#", object.objectId);
[self.tripNamesList addObject:object ];
}
dispatch_sync(dispatch_get_main_queue(), ^{
[self.tableView1 reloadData];
});
}
else {
// Log details of the failure
NSLog(#"Error: %# %#", error, [error userInfo]);
}
}];
You dont need to call [tableView reloadData] everywhere. Once this method is called, the tableview's delegates will be called in order like numberOfRowsInSection and based on the number of rows, that much time cellForRowAtIndexPath will be called. So in your case, you have put reloadData in cellForRowAtIndexPath and it will create an infinte loop, I guess.
Also, since you are filling self.tripNamesList in background thread, you can call reloadData in main thread as like below
for (PFObject *object in objects) {
NSLog(#"%#", object.objectId);
[self.tripNamesList addObject:object ];
}
[self performSelectorOnMainThread:#selector(refreshTableData) withObject:nil waitUntilDone:NO];
Yes, for this you need to have a method like this in your .m
- (void) refreshTableData{
[tableView reloadData];
}
Hope this helps.
Try to move your code from viewdidload to viewdidlayout, viewwillappear or viewdidapear, your table might not be initialised yet at that point.