Using pointers with Parse.com - ios

I'm currently developing my first App. The App is a Basic messenger App based on Parse.com.
I want to create a PFQueryTableViewController where it will show the recent chats with other users.
Photo of other user, Name of other user and timestamp (similar to Facebook messanger "recent" tab).
the chats data is saved in a Parse Class named Room with the following columns:
objectid(string)
roomname(string)
User_1(pointer to _User)
User_2(pointer to _User)...
I can fill the table view easily with the string values (e.g the roomname) but I would like to get the users #"full name" as the Label of each cell.
this is my code (I get an empty TableView):
- (PFQuery *)queryForTable {
PFQuery *query = [PFQuery queryWithClassName:#"Room"];
[query includeKey:#"User_2"];
if (self.objects.count == 0) {
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
}
[query orderByDescending:#"createdAt"];
return query;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
object:(PFObject *)object
{
static NSString *cellIdentifier = #"Cell";
PFTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[PFTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:cellIdentifier];
}
cell.textLabel.text = object[#"fullname"];
return cell;
}

When the pointer object is initially available it's just a stub, it doesn't actually contain any data. You need to call fetchAllIfNeededInBackground:block: on it (them, to keep things efficient) to populate the data.
Look at subclassing the table view controller and overriding - (void)objectsDidLoad:(NSError *)error to trigger the fetch on the new objects.
Note that you might just want to change your Room class to cache the user names (though if you do that you will need some cloud code to update the caches if the user name changes).

Ok, I solved it, hope this helps others.
This is the updated working code:
- (PFQuery *)queryForTable {
PFQuery *query = [PFQuery queryWithClassName:#"Room"];
[query includeKey:#"User_2"];
// If no objects are loaded in memory, we look to the cache first to fill the table
// and then subsequently do a query against the network.
if (self.objects.count == 0) {
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
}
[query orderByDescending:#"createdAt"];
return query;
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
object:(PFObject *)object
{
static NSString *cellIdentifier = #"Cell";
PFTableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:cellIdentifier];
if (!cell) {
cell = [[PFTableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle
reuseIdentifier:cellIdentifier];
}
PFObject *user = object[#"User_2"];
[user fetchIfNeededInBackgroundWithBlock:^(PFObject *user, NSError *error) {
//NSLog(#"%#", user);
cell.textLabel.text = user[#"fullname"];
}];
return cell;
}
#end

Related

Referencing Array from other View Controller Table View

I am attempting to work a segmented control to display data from a search into categories. However although data is received it is not being displayed on my table. Here is my code for the two View Controllers. The child View controller is the one in which the UITableView is stored.
PARENT VC
- (void)searchPeople:(NSString*)text {
if(![text isEqualToString:#""]){
PFQuery *userWithName = [PFQuery queryWithClassName:#"_User"];
[userWithName whereKey:#"fullName" containsString:text];
PFQuery *userWithHandle = [PFQuery queryWithClassName:#"_User"];
[userWithHandle whereKey:#"username" containsString:text];
PFQuery *userQuery = [PFQuery orQueryWithSubqueries:#[userWithHandle,userWithName]];
[userQuery findObjectsInBackgroundWithBlock:^(NSArray *results, NSError *error) {
NSLog(#"USERS: %#",results);
[self.userResults removeAllObjects];
[self.userResults addObjectsFromArray:results];
[[ArrayManager sharedInstance].searchResults addObjectsFromArray:results];
NSLog(#"Count Number: %#", [ArrayManager sharedInstance].searchResults);
[[NSNotificationCenter defaultCenter] postNotificationName:#"reload_data" object:self];
}];
}
}
CHILD VC
-(void)handle_data {
[self.tableView reloadData];
}
- (NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
NSLog(#"Object Entries: %lu", (unsigned long)[[ArrayManager sharedInstance].searchResults count]);
NSMutableArray * array = [[ArrayManager sharedInstance] getGlobalArray];
return [array count];
}
- (void)tableView: (UITableView *) tableView didSelectRowAtIndexPath: (NSIndexPath *) indexPath {
[tableView deselectRowAtIndexPath:indexPath animated:YES];
}
- (UITableViewCell *)tableView:(UITableView *)tableView
cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
NSDictionary *data = [[ArrayManager sharedInstance]init].searchResults[indexPath.row];
static NSString *MyIdentifier = #"MyIdentifier";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:MyIdentifier];
if (cell == nil)
{
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault
reuseIdentifier:MyIdentifier];
}
cell.textLabel.text = data[#"objectId"];
return cell;
}
Data is returned fine from the server however, there is no data being displayed on the table. The ArrayManager class is a Singleton class.
Create a Data Access Object (DAO) and store all information in an array in that.
http://www.tutorialspoint.com/design_pattern/data_access_object_pattern.htm
Have both VC's change and access the DAO instead of each other. It is much simpler that way.

PFQueryTablViewController queryForTable method with network reachability

I am trying to populate a PFQueryTableViewController with Question objects from my Parse backend. How do I implement blocks within this method?
- (PFQuery *)queryForTable // I'm having issues with using the method "findObjectInBackgroundWithBlock" in this method.
{
PFQuery *query = [PFQuery queryWithClassName:self.parseClassName];
[query fromLocalDatastore];
[query orderByDescending:#"createdAt"];
[query findObjectsInBackgroundWithBlock:^(NSArray *parseQuestions, NSError *error) { // Fetch from local datastore
if (parseQuestions != nil) {
NSMutableArray *mutableParseQuestions = [parseQuestions mutableCopy];
self.questions = mutableParseQuestions; // if Set local array to fetched Parse questions
} else {
if ([InternetReachabilityManager isReachable]) {
[query findObjectsInBackgroundWithBlock:^(NSArray *parseQuestions, NSError *error) { // Fetch from Cloud
NSMutableArray *mutableParseQuestions = [parseQuestions mutableCopy];
self.questions = mutableParseQuestions; // if Set local array to fetched Parse questions
[Question pinAllInBackground:parseQuestions]; // Save query results to local datastore
}];
}
}
}];
return query;
}
When blocks are in this method I get this error.
Is this because queryForTable is already a background process? Should I just use
[query findObjects]
Also, I'm trying to implement reachability into my fetch.
Try fetching from local datastore
Load data into table if they are there else switch to the network
Call block if network is available
Save the results of the network fetch into the local datastore
I know that this method is supposed to automatically assign objects it fetches to rows but I don't know how to work with it if we're passing around a PFObject subclass object. This is my explanation for the arrays.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
self.question = [self.questions objectAtIndex:indexPath.row]; // Save one question from row
static NSString *identifier = #"QuestionsCell";
QuestionsCell *cell = [tableView dequeueReusableCellWithIdentifier:identifier];
if (cell == nil) {
cell = [[QuestionsCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:identifier];
}
[cell setQuestion:self.question]; //
return cell;
}
This is how I use the fetched array to populate the tableView.
So my questions:
Why can't I call a block inside of queryForTable?
Is there any way I can make this simpler by using queryForTable's automatic assigning of objects to rows?
If the internet is unreachable and the local datastore is empty what should I do?
There is no reason to call your query in that function. You're supposed to create a query and then return it. PFQueryTableViewController does the actual handling of the request. Read the documentation here: https://parse.com/docs/ios/api/Classes/PFQueryTableViewController.html#//api/name/queryForTable
Once you're in the cellForRowAtIndexPath method you can call objectAtIndexPath: which will you give you the object you need and then use the data from it to set up your cell.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath {
PFObject *object = [self objectAtIndexPath:indexPath];
UITableViewCell *cell = ....
//configure cell using object here
return cell;
}
You do this (semi-pseudo code)
-(NSInteger)tableView:(UITableView*)tableView numberOfRowsInSection:(NSInteger)section {
if (self.objects.count == 0) {
Put some sort of overlay that shows there are no objects to be retrieved or internet is unreachable.
}
return self.objects.count;
}

cellForRowAtIndexPath how to skip a row for data generated through PFQueryTableViewController?

I am trying to skip a row creation in my cellForRowAtIndexPath without empty cells in between while retrieving data from PFQueryTableViewController ? How can i create a cell when my condition satisfies and skip creating a blank cell ?
Here's my code:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
//NSLog(#"data is %#",[object objectForKey:#"ItemName"]);
PFQuery *query = [PFQuery queryWithClassName:#"userData"];
[query whereKey:#"username" equalTo:[PFUser currentUser].username];
[query getFirstObjectInBackgroundWithBlock:^(PFObject * userData, NSError *error) {
if (!error) {
//Doing some logic to get result
if (result == a) {
static NSString *CellIdentifier = #"ident";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
}
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
cell.textLabel.text = [object objectForKey:#"ItemName"];
cell.detailTextLabel.text = [object objectForKey:#"Price"];
cell.detailTextLabel.textColor = [UIColor blueColor];
[_itemPrefArray removeAllObjects];
[_userPrefArray removeAllObjects];
return cell;
}
}else {
NSLog(#"Error: error setting cell data %#", error);
}
}];
}
It is throwing compilation error as i am not aware of how to return a cell based on condition. I tried using id cell = nil; and tried returning which is not working.
So based on what I understand, you should query for the user data only once, most likely in viewWillLoad(), if that scenario fits. Once the query is successful, use the results of the query to filter your other query to determine which objects meet your predicate requirements and save those objects that fit your predicate to an NSMutableArray. This array corresponds to all the records that match what you're trying to do. This array corresponds to the number of cells in the tableview and then in cellForRowAtIndexPath: , to get the contents of the cell, just index the results array based on indexPath.row.
I tried to provide a trivial example to show you what I'm thinking of since I cannot provide any more specifics without knowing the other queries and the predicate you're using to find matches..
-(void)queryForUserData(){
PFQuery *query = [PFQuery queryWithClassName:#"userData"];
[query whereKey:#"username" equalTo:[PFUser currentUser].username];
[query getFirstObjectInBackgroundWithBlock:^(PFObject * userData, NSError *error) {
if(error == nil){
filterResultsUsingUserData(userData);
tableView.reloadData();
}
}
}
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
static NSString *CellIdentifier = #"ident";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell == nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleValue1 reuseIdentifier:CellIdentifier];
}
cell.accessoryType = UITableViewCellAccessoryDisclosureIndicator;
//Use the results from filterResultsUsingUserData to fill in contents
}
-(void)filterResultsUsingUserData(PFObject*)userData{
//Filter results based on a predicate you're comparing to and save to immutable property. Number of results corresponds to number of cells
for(int i = 0;i < objects.count;i++){
if(a == b){
results.addObject(objects[i]);
}
}
}

Display user data on UITableView obtained from a column typed array of PFUser on Parse.com

I created UITableView inside UIView (UserListVC) to display user data stored in a column typed array on Parse.com. The column called "followers" contains the array of PFUser objectId who did like the user in a row (in this case is current user).
In userListVC.m:
#implementation UserListViewController
{
NSMutableArray *tableData;
}
- (void)viewDidLoad {
[super viewDidLoad];
/*
tableData = [NSMutableArray arrayWithObjects:#"user1", #"user2", nil];
NSLog(#"tableData --> %#", tableData);
*/
tableData = [[NSMutableArray alloc] init];
PFQuery *userQuery = [PFQuery queryWithClassName:kPAWParseUserClassKey];
[userQuery whereKey:kPAWParseUsernameKey equalTo:[PFUser currentUser].username];
userQuery.cachePolicy = kPFCachePolicyNetworkElseCache;
[userQuery findObjectsInBackgroundWithBlock:^(NSArray *users, NSError *error)
{
if( !error )
{
NSArray *array = [users valueForKey:#"followings"];
for (int i = 0; i <= array.count; i++)
PFQuery *followingsQuery = [PFUser query];
[followingsQuery whereKey:#"objectId" equalTo:[[[users valueForKey:#"followings"] objectAtIndex:0] objectAtIndex:i]];
followingsQuery.cachePolicy = kPFCachePolicyNetworkElseCache;
[followingsQuery findObjectsInBackgroundWithBlock:^(NSArray *followings, NSError *error) {
if (!error) {
NSLog(#"following names --> %#", [followings valueForKey:kPAWParseUsernameKey]);
[tableData addObject:[followings valueForKey:kPAWParseUsernameKey]]; //??
[self.tableView reloadData];
NSLog(#"table data --> %#", tableData);
}
}];
}
}];
}
I am now able to extract usernames from the user class by using data array that I get from "followers" column and I also have [self.tableView reloadData]; in async query block but there is still a problem with tableview not showing usernames obtained from tableData. If I use sample data ("user1","user2") just for testing, there is no problem.
Here below I show my log from the code:
2014-12-31 11:05:00.626 Test[12354:60b] following names --> (
user1name
)
2014-12-31 11:05:00.628 Test[12354:60b] table data --> (
(
user1name
)
)
2014-12-31 11:05:00.631 Test[12354:60b] following names --> (
user2name
)
2014-12-31 11:05:00.633 Test[12354:60b] table data --> (
(
user1name
),
(
user2name
)
)
I think it is better to also provide code for the method (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath and here it is:
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
static NSString *tableIdentifier = #"TableItem";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:tableIdentifier];
if (cell == nil) {
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleDefault reuseIdentifier:tableIdentifier];
}
NSLog(#"table data xxxxx --> %#", tableData);
cell.textLabel.text = [tableData objectAtIndex:indexPath.row];
return cell;
}
The above method NEVER runs but if I test with my sample data, it does run. To be more specific, my questions would be:
This is UIView with UITableView inside it. I am not sure how to do [self.tableView reloadData]; properly. I mean how to declare tableView. In this case I do this:
#interface UserListViewController ()
#property (weak, nonatomic) UITableView *tableView;//????
#end
Why the method (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath never get run?
Just to answer these 2 specific questions:
Connect tableView to IBOutlet in .h file and synthesise it in .m file, then:
[self.tableView reloadData];
do the above inside async block.
The method (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath will get run if do as the above suggests.
However there remains other issues but they are out of the scope of this question I believe.

Display array from parse.com in tableview (iOS)

I am tying to display in my table view an array that is on a database on parse.com
I can display the first item in the array, but not the rest.
- (UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath object:(PFObject *)object {
static NSString *CellIdentifier = #"Cell";
UITableViewCell *cell = [tableView dequeueReusableCellWithIdentifier:CellIdentifier];
if(cell==nil){
cell = [[UITableViewCell alloc] initWithStyle:UITableViewCellStyleSubtitle reuseIdentifier:CellIdentifier];
}
_ListArray = [object objectForKey:#"content"];
NSLog(#"OBJECT = %#",_ListArray);
NSString *cellValue = [NSString stringWithFormat:#"%#", [_ListArray objectAtIndex:indexPath.row]];
cell.textLabel.text = cellValue;
if(refresh <counter)
{
NSString *br = #"<br>";
List = [NSString stringWithFormat:#"%#%#%#", List, br, cell.textLabel.text];
NSLog(#"%#",List);
refresh ++;
}
cell.accessoryType =UITableViewCellAccessoryDisclosureIndicator;
return cell;
}
If I try to use:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [_ListArray count];
}
I get nothing in the table.
edit update:
did remove the self.ListArray and going with _ListArray.
in the h file:
NSMutableArray *ListArray;
...
#property (retain, atomic) NSMutableArray *ListArray;
and then in m file:
#synthesize ListArray = _ListArray;
regarding the:
self.ListArray = [object objectForKey:#"content"];
the object is the PFObject.
i have queryForTable where i get the data from parse.
- (PFQuery *)queryForTable {
PFQuery *query = [PFQuery queryWithClassName:#"List"];
[query whereKey:#"user" equalTo:[PFUser currentUser]]; // user
[query whereKey:#"objectId" equalTo:[theListID objectId]]; // the specific list
counter =[query countObjects];
NSLog(#"%d",counter);
// If Pull To Refresh is enabled, query against the network by default.
if (self.pullToRefreshEnabled) {
query.cachePolicy = kPFCachePolicyNetworkOnly;
}
// If no objects are loaded in memory, we look to the cache first to fill the table
// and then subsequently do a query against the network.
if ([self.objects count] == 0) {
query.cachePolicy = kPFCachePolicyCacheThenNetwork;
}
[query orderByDescending:#"createdAt"];
return query;
}
And to the objectForKey:#"content"];
content is the name of the column in my class on parse
so i need to say what object from the class i want to get, and that is the array column named "content"
The logg, (when the NSLog is active on the array.
Inköpslista[7856:12e03] Warning: A long-running Parse operation is being executed on the main thread.
Break on warnParseOperationOnMainThread() to debug.
2013-10-21 14:23:31.008 Inköpslista[7856:12e03] 1
2013-10-21 14:23:31.017 Inköpslista[7856:12e03] OBJECT = (
"Br\U00f6d",
Anka,
Boll,
Sko
)
2013-10-21 14:23:31.017 Inköpslista[7856:12e03] <html><body><br>Bröd
2013-10-21 14:23:31.545 Inköpslista[7856:12e03] OBJECT = (
"Br\U00f6d",
Anka,
Boll,
Sko
)
Got it to work, did put the array were i get the data from parse, and then only call
cell.textLabel.text = [_ListArray objectAtIndex:indexPath.row];
and then i can use:
-(NSInteger)tableView:(UITableView *)tableView numberOfRowsInSection:(NSInteger)section {
return [_ListArray count];
}

Resources