I've retrieved data to my iOS app from my Drupal database using the below code:
.h
#property (strong, nonatomic) NSMutableArray *userData;
.m
self.userData = [NSMutableArray new];
NSMutableDictionary *viewParams = [NSMutableDictionary new];
[viewParams setValue:#"u000" forKey:#"view_name"];
[DIOSView viewGet:viewParams success:^(AFHTTPRequestOperation *operation, id responseObject) {
self.userData = [responseObject mutableCopy];
NSLog(#"%#",self.userData);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Failure: %#", [error localizedDescription]);
}];
self.userData = [[NSMutableArray alloc] init];
self.userBio.text = self.userData[0][#"userbio"];
self.userData is populated, so I know my data has successfully been returned. Does anyone know how I would display the JSON returned field in a UIlabel? I'm just not sure how I would write this line of code (newb, sorry). My field name is userbio.
Here's how the data structure is returned (incase it's of any use):
UPDATE: NSLog(#"userData = %#", self.userData); returns this in the console:
2015-10-25 11:22:06.601 [7605:2738034]
userData = (
> {
> userbio = "No user bio available at this time.";
> "users_name" = "<a href=\"/user/20\" title=\"View user profile.\" class=\"username\" xml:lang=\"\" about=\"/user/20\"
> typeof=\"sioc:UserAccount\" property=\"foaf:name\"
> datatype=\"\">Brittany</a>";
> },
If it's an NSDictionary the code would look like this:
self.myLabelName.text = self.userData[#"userbio"];
If it's an NSArray of NSDictionaries it would look like:
self.myLabelName.text = [self.userData firstObject][#"userbio"];
That's assuming that the array contains only one dictionary, otherwise you would have to access each element: self.userData[0], self.userData[1], etc.
Related
I'm trying out the "Sample Blog App" on Parse Server for iOS and cannot figure out what is the smartes way to fetch all child objects of another class (together with the parent objects).
The "Sample Blog App" (which creates automatically when you create a new account) contains the classes Comment and Post. The Comment class contains a relation to the Post class as shown below (from the dashboard), but there is no relation in the opposite direction.
Now, I want to fetch all posts and all the comments related to each post. The code below does that, but I'm assuming there must be a smarter way...? If you know how, please share. Thanks in advance!
- (void)fetchPosts {
NSString *commentsKey = #"comments";
NSString *postKey = #"post";
PFQuery *query = [PFQuery queryWithClassName:#"Comment"];
[query includeKey:postKey];
[query findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {
if (error == nil) {
NSMutableArray *array = [[NSMutableArray alloc]init];
for (PFObject *comment in objects) {
PFObject *post = [comment objectForKey:postKey];
NSDictionary *existingPostDict = [[array filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"%K = %#", #"post.objectId", post.objectId]] firstObject];
if (existingPostDict) {
// update comments
NSArray *comments = [[existingPostDict objectForKey:commentsKey] arrayByAddingObject:comment];
// create new dictionary and replace the old one
NSDictionary *newPostDict = [[NSDictionary alloc]initWithObjectsAndKeys:[existingPostDict objectForKey:postKey], postKey, comments, commentsKey, nil];
[array replaceObjectAtIndex:[array indexOfObject:existingPostDict] withObject:newPostDict];
}
else {
// first post, create a new dict
NSDictionary *newPostDict = [[NSDictionary alloc]initWithObjectsAndKeys:post, postKey, #[comment], commentsKey, nil];
[array addObject:newPostDict];
}
}
self.posts = array; // assuming: #property (nonatomic, strong) NSArray *posts;
}
else {
NSLog(#"Error fetching posts: %#", error.localizedDescription);
}
}];
}
Instead of using include on your query you should use whereKey:equals: and pass the post object as the second argument. This will filter and return only the comment objects that contain that have that post as their value for post
One problem I see with your query is that there is a possibility this will not fetch every post in the database. If a post has 0 comments, none of the Comment objects will have a reference to it and thus you will not receive it.
Therefore you should actually do a query on "Post" and in its completion do a query on "Comment". This way you will not miss any posts with 0 comments. When you do this, you will not need to include the "post" key in the Comment query. This has multiple benefits.
First, each include is also another query for that object. So each new Comment object will create another query in the backend. You will get rid of this automatically.
Second, for a "Post" with multiple comments, you will be querying for the same post multiple times and that same post will be returned multiple times which consumes unnecessary bandwidth.
After getting Posts and Comments separately just combine them.
Apart from that I would do the combining like so which I find more readable but that is just personal preference.
- (void)fetchPosts {
NSString *commentsKey = #"comments";
NSString *postKey = #"post";
PFQuery *query = [PFQuery queryWithClassName:#"Comment"];
[query includeKey:postKey];
[query findObjectsInBackgroundWithBlock:^(NSArray * _Nullable objects, NSError * _Nullable error) {
if (error == nil) {
NSMutableArray *array = [[NSMutableArray alloc]init];
NSMutableDictionary *d = [NSMutableDictionary dictionary];
for (PFObject *comment in objects) {
PFObject *post = [comment objectForKey:postKey];
if (d[post.objectId]) {
[d[post.objectId][commentsKey] addObject:comment];
}
else{
d[post.objectId] = [NSMutableDictionary dictionary];
d[post.objectId][postKey]=post;
d[post.objectId][commentsKey] = [NSMutableArray arrayWithObject:comment];
}
}
for (NSString *key in [d allKeys]) {
[array addObject:d[key]];
}
self.posts = array; // assuming: #property (nonatomic, strong) NSArray *posts;
}
else {
NSLog(#"Error fetching posts: %#", error.localizedDescription);
}
}];
}
This is how I did it, using findObjectsInBackground together with continueWithSuccessBlock: methods (for better error handling one can choose continueWithBlock: instead):
- (void)fetchPosts {
/**
create "post" and "comment" queries and use a BFTask-method from
Bolts.framework to chain downloading tasks together (bundled with Parse SDK)
*/
NSMutableArray *posts = [NSMutableArray new];
PFQuery *postQuery = [PFQuery queryWithClassName:#"Post"];
[[[postQuery findObjectsInBackground] continueWithSuccessBlock:^id(BFTask * task) {
[posts addObjectsFromArray:task.result];
PFQuery *commentsQuery = [PFQuery queryWithClassName:#"Comment"];
return [commentsQuery findObjectsInBackground];
}] continueWithSuccessBlock:^id(BFTask * task) {
/**
loop through posts and filter out comments with the same objectId in post,
then create a dictionary with post and related comments. done! :)
*/
NSMutableArray *postsAndComments = [NSMutableArray new];
for (PFObject *post in posts) {
NSArray *comments = [task.result filteredArrayUsingPredicate:[NSPredicate predicateWithFormat:#"%K == %#", #"post.objectId", post.objectId]];
[postsAndComments addObject:#{#"post":post, #"comments":comments}];
}
/**
note: BFTask-blocks not running in main thread!
*/
dispatch_async(dispatch_get_main_queue(), ^{
self.posts = postsAndComments; // assuming: #property (nonatomic, strong) NSArray *posts;
});
return nil;
}];
}
I'm trying to check if NSString 'testing' (47) exists inside of my NSMutableArray 'self.checkfriendData'. I'm using the code below, though after logging my if statement it appears as though it's never executed (even though the statement is true - see console data below, uid = 47, and thus hiding my object should fire?) Any idea as to why this isn't working? Help is much appreciated!
ViewController.m
NSMutableDictionary *viewParams3 = [NSMutableDictionary new];
[viewParams3 setValue:#"accepted_friends" forKey:#"view_name"];
[DIOSView viewGet:viewParams3 success:^(AFHTTPRequestOperation *operation, id responseObject) {
self.checkfriendData = (NSMutableArray *)responseObject;
NSString *testing = #"47";
NSArray *friendorNo = self.checkfriendData;
if ([friendorNo containsObject:testing]) // YES
{
self.addFriend.hidden = YES;
}
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
}];
Here's what's inside self.checkfriendData:
2017-05-18 19:36:07.266529-0700 This is the friend data check (
{
body = "My name is Britt";
friendphoto = "/sites/default/files/stored/x.jpg";
"node_title" = "Britt";
uid = 47;
}
)
It appears that your NSArray contains NSDictionarys and you are asking if the array contains an NSString. The answer will always be no as the array doesn't directly contain any NSStrings.
If you want to search for the uid of 47 you will have to iterate over the array and check the uid key of each NSDictionary for the value 47.
The code for this would look something like:
for (NSDictionary *dict in friendorNo) {
if ([dict[#"uid"] isEqualToString:testing]) {
self.addFriend.hidden = YES;
}
}
Im newly using AFNetworking to get JSON and parse it,I've imported it to my project and got the JSON but i don't know how is it possible to parse the JSON for display especially in Objective-c.
Here is the code in my viewDidLoad to Get JSON :
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:#"http://api.androidhive.info/json/movies.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
The Retrieved JSON for parsing:
{
genre = (
Action,
Drama,
"Sci-Fi"
);
image = "http://api.androidhive.info/json/movies/1.jpg";
rating = "8.300000000000001";
releaseYear = 2014;
title = "Dawn of the Planet of the Apes";
},
{
genre = (
Action,
"Sci-Fi",
Thriller
);
image = "http://api.androidhive.info/json/movies/4.jpg";
rating = "8.4";
releaseYear = 2014;
title = "X-Men: Days of Future Past";
},
{
genre = (
Action,
Adventure,
Fantasy
);
image = "http://api.androidhive.info/json/movies/7.jpg";
rating = "7.3";
releaseYear = 2014;
title = "The Amazing Spider-Man 2";
},
{
genre = (
Animation,
Comedy,
Family
);
image = "http://api.androidhive.info/json/movies/9.jpg";
rating = "8.300000000000001";
releaseYear = 2013;
title = Rush;
},
{
genre = (
Animation,
Adventure,
Family
);
image = "http://api.androidhive.info/json/movies/15.jpg";
rating = "8.199999999999999";
releaseYear = 2010;
title = "How to Train Your Dragon";
}
)
Update :
How is it possible to display it and store the retrieved data so its easier to show it in UITableView.
i tried to do like :
NSLog(#"JSON: %#", [responseObject objectForKey:#"image"]);
to get the whole images only but it crashes. i just need to get for example all the titles and store them in array and then display them UITableView.
you try this way to get data from NSDictionary
AFHTTPRequestOperationManager *manager = [AFHTTPRequestOperationManager manager];
[manager GET:#"http://api.androidhive.info/json/movies.json" parameters:nil success:^(AFHTTPRequestOperation *operation, id responseObject) {
NSLog(#"JSON: %#", responseObject);
self.arrData=[[NSMutableArray alloc]initWithArray:responseObject];
[self.tableView reloadData];// reload table data
} failure:^(AFHTTPRequestOperation *operation, NSError *error) {
NSLog(#"Error: %#", error);
}];
TableView Delegate Method
-(UITableViewCell *)tableView:(UITableView *)tableView cellForRowAtIndexPath:(NSIndexPath *)indexPath
{
// your cell code here
// indexPath.row(use in tableView) means your number of index to get value
NSLog(#"JSON: %#", [[self.arrData objectAtIndex:indexPath.row] objectForKey:#"image"]);
NSLog(#"title: %#", [[self.arrData objectAtIndex:indexPath.row] objectForKey:#"title"]);
NSLog(#"rating: %#", [[self.arrData objectAtIndex:indexPath.row] objectForKey:#"rating"]);
NSLog(#"releaseYear: %#", [[self.arrData objectAtIndex:indexPath.row] objectForKey:#"releaseYear"]);
NSLog(#"genre: %#", [[self.arrData objectAtIndex:indexPath.row] objectForKey:#"genre"]);// return array get value particular using array index
}
AFNetworking returns already processed JSON in responseObject - and in your case it's an NSArray of NSDictionary objects inside it.
To access a single NSDictionary create an NSArray property (e.g. resultArray) and set it to responseObject, then you can access its objects by resultArray[1] (make sure to bounce check).
Then you can access the values of the dictionary by their keys (myDictionary[#"title] and so on).
I have an NSMutableArray with keys UserID and UserName.
I also have an NSDictionary with keys UserID and UserScore.
How do I add the key and values for UserScore from the NSDictionary to the NSMutableArray so that the UserName and UserScore represent the same UserID?
NSMutableArray:
UserID = 123,
UserName = JohnP
NSDictionary:
UserID = 123,
UserScore = 100
Desired output as NSMutableArray:
UserID = 123,
UserName = JohnP,
UserScore = 100
Below is how I retrieve data into the NSMutableArray using Parse and then grab data to a label:
[FBRequestConnection startWithGraphPath:#"/me/friends"
parameters:nil
HTTPMethod:#"GET"
completionHandler:^(
FBRequestConnection *connection,
id result,
NSError *error
) {
FacebookFriends = [result objectForKey:#"data"];
}];
NSString *friendname = [[NSString alloc] initWithFormat:#"%#",[[FacebookFriends objectAtIndex:indexPath.row] objectForKey:#"name"]];
I think you really just wanna use an NSMutableDictionary which is made to store value-key pairs.
You can give your mutable dictionary values from your other dictionary simply by pointing to its key-value (objectForKey).
It looks to me like you want to combine two dictionaries into one:
NSDictionary *dict1 = ...;
NSDictionary *dict2 = ...;
NSMutableDictionary *combined = [NSMutableDictionary new];
NSNumber *userid = dict1[#"UserID"];
NSAssert(userid, #"UserID missing from dict1");
NSAssert([userid isEqualToString:dict2[#"UserID"]], #"Mismatched UserIDs");
NSString *username = dict1[#"UserName"];
NSAssert(username, #"Username missing from dict1");
NSNumber *score = dict2[#"UserScore"];
NSAssert(score, #"Score missing from dict2");
combined[#"UserID"] = userid;
combined[#"UserName"] = username;
combined[#"UserScore"] = score;
However I also suspect you have an array of dictionaries. If so, let me know.
I'm trying to implement Facebook iOS SDK 3.1.1 into my app and I'm having issues.
When I try to run
NSString *query =
#"SELECT uid, name, pic_square FROM user WHERE uid IN "
#"(SELECT uid2 FROM friend WHERE uid1 = me() LIMIT 25)";
// Set up the query parameter
NSDictionary *queryParam =
[NSDictionary dictionaryWithObjectsAndKeys:query, #"q", nil];
// Make the API request that uses FQL
[FBRequestConnection startWithGraphPath:#"/fql"
parameters:queryParam
HTTPMethod:#"GET"
completionHandler:^(FBRequestConnection *connection,
id result,
NSError *error) {
if (error) {
NSLog(#"Error: %#", [error localizedDescription]);
} else {
NSLog(#"Result: %#", result);
}
}];
My app crashes on NSLog(#"Result: %#", result); If someone could help me out that would be much appreciated. I'm trying to return the user's friends list with each friend's user data. Below is the Debug Navigation view.
Thanks,
Wes
The result you can expect here is a dictionary containing all the friend data you need. You can make sense of it like this:
NSDictionary *resultDict = (NSDictionary *)result;
NSArray *friendsArr = [resultDict objectForKey:#"data"];
NSMutableArray *resultArr = [NSMutableArray array];
for (FBGraphObject *friendObj in friendsArr) {
NSString *name = [friendObj objectForKey:#"name"];
NSString *imageUrl = [friendObj objectForKey:#"pic_square"];
long long uid = [[friendObj objectForKey:#"uid"] longLongValue];
}
I rebuild my project from scratch by adding each controller and library back into an empty project. It appears now everything is working correctly. Same code same spot.