Custom Graph API request on iPhone SDK - ios4

Is it possible to make a custom Graph API call using FBConnect?
For example,
[facebook requestWithGraphPath:#"me" andDelegate:self];
With this call, I can see my own details.
[facebook requestWithGraphPath:#"me/friends" andDelegate:self];
With this I can see my friends.
[facebook requestWithGraphPath:#"FRIEND_ID/picture" andDelegate:self];
With this I can see a picture from a friend with id=FRIEND_ID.
Is there a possibility to make a custom Graph API call, to take id, picture, name and for example email, of all my facebook friends, in just one call?
Problem that I have is, when I call:
[facebook requestWithGraphPath:#"me/friends" andDelegate:self];
In:
- (void)request:(FBRequest *)request didLoad:(id)result;
I take the IDs of my friends, and then, in a for loop I want to do this:
[[self appDelegate].facebook requestWithGraphPath:[NSString stringWithFormat:#"%#/picture",[[party.attendantsDictionary allKeys] objectAtIndex:i]] andDelegate:self];
Afterwards, in
- (void)request:(FBRequest *)request didLoad:(id)result;
I have:
if ([result isKindOfClass:[NSData class]]) {
[party.attendantsPictures addObject:[[UIImage alloc] initWithData:result]];
}
Problem is that pictures and names of my friends are all mixed up.
Anyone has a solution?

You could try NSRunLoop.
In my case, I wanna load user's name, status, profile picture at once. However, requestWithGraphPath and its delegate didload: result definitely only can handle each request at a time. So what I did is:
1/ Setup a tag number
2/ After each request, wait for the request to be completed, increase tag number by 1
So in my button action,
tagNo = 0;
[facebook requestWithGraphPath:graphPathZero // For user's name
andDelegate:detailVC];
while ( tagNo == 0) && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
[facebook requestWithGraphPath:graphPathOne // For user's status
andDelegate:detailVC];
while ( tagNo == 1) && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
[facebook requestWithGraphPath:graphPathTwo // For user's profile picture
andDelegate:detailVC];
while ( tagNo == 2) && [theRL runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]);
And in the delegate,
switch (tagNo) {
case 0:
// Do something with result
break;
case 1:
// Do something with result
break;
case 2:
// Do something with result
break;
default:
break;
}
++tagNo;
I may not cover "Do something with result" here since it's in somewhere else in stackoverflow. This might not be the solution, but I found it approachable. Look for more contributions from others ;)

Hmmm, I've never used NSRunLoop. Could you give me a hint what it is? (Apple's Docs are little confusing...).
In your case, I would do something like this:
NSString *fql = #"SELECT name, uid, pic_square, status FROM user WHERE uid = me() OR uid IN (SELECT uid2 FROM friend WHERE uid1 = me())";
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:fql, #"query", nil];
[facebook requestWithMethodName:#"fql.query" andParams:params andHttpMethod:#"POST" andDelegate:self];
This way, you would get everything from the users, that you want.

Related

Playlist loaded property is YES but playlist.items have null items inside

I'm using the following code to load a playlist
-(void)loadPlaylist:(NSString *)playlistURI withCompletionBlock:(spotifycompletionWithData)completionBlock andfailed:(failedBlock)failedBlock {
NSURL *playlistURL = [NSURL URLWithString:playlistURI];
[[SPSession sharedSession] playlistForURL:playlistURL callback:^(SPPlaylist *playlist) {
[SPAsyncLoading waitUntilLoaded:playlist timeout:kSPAsyncLoadingDefaultTimeout+10 then:^(NSArray *loadedItems, NSArray *notLoadedItems) {
if(notLoadedItems.count >= 1){
[SVProgressHUD dismiss];
failedBlock();
return;
}
self.playlist = [loadedItems lastObject];
}];
}];
}
I'm observing the playlist.loaded property and I see its YES
but when I'm looking into the playlist.items lots of them are null.
What can I do?
The playlist.loaded == YES just means that the playlist's own metadata is loaded - the name, owner, number of items etc. The items themselves load separately, so you need to separately use SPAsyncLoading to load them.
Note that loading the entire contents of a playlist at once is a pretty bad idea - playlists get huge, and if you try to load 10,000 items at once things are going to get bad fast on an iOS device.
Instead, you should consider loading the items in chunks as the user scrolls around your UI.

Using selected NSManagedObject across different controllers

I have an entity called Practice and I use a View Controller called SelectorViewController to select one of the practices, selectedPractice. I then return selectedPractice to a view Controller called RegularViewController where I display some of the selectedPractice attributes. All of this works fine. However the app has a number of other View Controllers which can be reached by modal segues from instances of RegularViewController. As a result, if I leave and then come back to RegularViewController, selectedPractice is reset as null. I would also like to save selectedPractice so that it is available at app initialisation if it has previously been set in SelectorViewController. How do I achieve this by making selectedPractice persistent across the app, and available at runtime?
Regards
Thanks to the post above, which was great, I managed to sort it. Here is my code, which may be very clumsy, but it works.
Firstly, as I loaded the fetchedObjects into a PickerView in SelectorView Controller, I set an attribute "isSelectedPractice" to "NO" with the following code:
for (Practice *fetchedPractice in [self.fetchedResultsController fetchedObjects]) {
[fetchedPractice setValue:#"NO" forKey:#"isSelectedPractice"];
[self.managedObjectContext save:nil];
I then identified for the selected Practice:
- (void)pickerView:(UIPickerView *)pickerView didSelectRow:(NSInteger)row inComponent:(NSInteger)component {
Practice *practice = [[self.fetchedResultsController fetchedObjects] objectAtIndex:row];
self.selectedPractice = practice;
NSLog(#"The '%#' practice was selected using the picker", self.selectedPractice.name);
}
as the view Segue'd back to RegularViewController I set the isSelectedPractice attribute for selectedPractice to YES. I kept it this late as I didn't want more than one selection in the PickerView to result in multiple objects with isSelectedPractice YES.
- (void)prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
if ([segue.identifier isEqualToString:#"SavedPractice Segue"])
{
[self.selectedPractice setValue:#"YES" forKey:#"isSelectedPractice"];
[self.managedObjectContext save:nil];
NSLog(#"Setting SelectedPractice as '%#' in RegularViewController with isSelectedPractice as '%#'",self.selectedPractice.name,self.selectedPractice.isSelectedPractice );
RegularViewController *rvc= segue.destinationViewController;
rvc.delegate = self;
rvc.selectedPractice = self.selectedPractice;
}
else {
NSLog(#"Unidentified Segue Attempted!");
}
}
I then set the following Predicate in the setupFetchedResultsController method of RegularViewController:
request.predicate = [NSPredicate predicateWithFormat:#"isSelectedPractice = %#", #"YES"];
Many thanks for the help
Without seeing your actual project, one way I know will work but might be a little too round a bout would be to add an attribute "isSelectedPractice" to your entity. You could make it a BOOL, but I've had mixed results with BOOL's in Core Data, I prefer to just leave it as a NSString and set it to "yes" or "no". Then when you pull it down, modify it or add it to core Data as a entity with isSelectedPractice set to "yes". Then in your other controllers, do a
if (self.managedObjectContext == nil) {
self.managedObjectContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
}
then do a fetch request to get entities with a predicate which is looking for isSelectedPractice equaling "yes". If you need actual code samples on how to do this let me know and I'll edit them in.

Unexpected behavior of NSFetchRequest

I have created few entities in context for saving it in db using
AppCalendarEntity *appCalendar = [AppCalendarEntity getInstanceWithManagedDocument:manageDocument];
After adding a few entities I execute flowing fetch request
NSFetchRequest *requestToSeeIfCalendarWithIdExist = [NSFetchRequest fetchRequestWithEntityName:#"AppCalendarEntity"];
NSArray *result = [managedDocument.managedObjectContext executeFetchRequest:requestToSeeIfCalendarWithIdExist error:&InternalError] ;
It returns me the result including only the entities I have added in context using first command and NOT the entries already saved in database. I have made sure that at this stage the document state is UIDocumentStateNormal.
When I add this line to already open document (UIDocumentStateNormal) it returns me the expected result, i.e. it fetch results from db as well as memory context which has not yet been saved to db.
[managedDocument openWithCompletionHandler:^(BOOL success)
{
NSFetchRequest *requestToSeeIfCalendarWithIdExist = [NSFetchRequest fetchRequestWithEntityName:#"AppCalendarEntity"];
NSArray *result = [managedDocument.managedObjectContext executeFetchRequest:requestToSeeIfCalendarWithIdExist error:&InternalError] ;
}
My question is
1- I expect that the result of query should be the same in both cases. Why it is not so in the above case.
2- To me if document state is UIDocumentStateNormal I should not be calling "openWithCompletionHandler" in context to open the document. In this particular scenario what difference it is making in NSFetchRequest which gives me the desired result after adding this.
Please let me know if I'm getting wrong
Here is the complete code
This is the complete code of the function
+ (void ) saveCalendarArrayInDbIfItAlreadyDoesNotExist : (NSArray*) appCalendarArray managedDocument: (UIManagedDocument*) managedDocument completionBlock : ( void(^) (NSArray* ObjectSavedSuccesfully, NSError *InternalError)) handler
{
// i dont know why i have to do it :( if i dont add openWithCompletionHandler my query doesnt fetch result from db rather just do query in-memory context and not db
[managedDocument openWithCompletionHandler:^(BOOL success)
{
void (^completionHandler)(NSArray* , NSError* );
completionHandler = [handler copy ];
NSError *error = nil;
NSMutableArray *array = [[NSMutableArray alloc] init];
for (id appCalendar in appCalendarArray) {
if([appCalendar isKindOfClass:[AppCalendarEntity class]])
{
AppCalendarEntity *appCalendarEntity = (AppCalendarEntity*) appCalendar;
NSFetchRequest *requestToSeeIfCalendarWithIdExist = [NSFetchRequest fetchRequestWithEntityName:#"MyEntity"];
requestToSeeIfCalendarWithIdExist.predicate = [NSPredicate predicateWithFormat:#"identifier = %#", appCalendarEntity.identifier];
NSError *InternalError = nil;
[requestToSeeIfCalendarWithIdExist setShouldRefreshRefetchedObjects:YES];
NSArray *result = [managedDocument.managedObjectContext executeFetchRequest:requestToSeeIfCalendarWithIdExist error:&InternalError] ;
// "result" is different when we encapsulate it in openWithCompletionHandler and when we don't…….MY PROBLEM
if(result == nil)
{
// return error
}
// 1 object always return that depict the in memory(context) object we created but not saved. I expect it should be zero because no object has yet been saved to database..
else if(result.count > 1)
{
[managedDocument.managedObjectContext deleteObject:appCalendar];
}
else
{
[array addObject:appCalendarEntity];
}
}
else
{
// error handling
}
}
if (error != nil)
{
completionHandler (nil, error);
return;
}
// saving all the objects
[ managedDocument updateChangeCount:UIDocumentChangeDone ];
}
When using UIManagedDocument, you do not call save on the MOC because it implements auto-save. however, it needs to be told that an auto-save should take place at some point in the future.
Get rid of that call to openWithCompletionHandler in that function (I know it was just there for purposes of debugging this problem).
Replace
[managedDocument.managedObjectContext save:&InternalError ]
with
[managedDocument updateChangeCount:UIDocumentChangeDone];
This will notify the document that it can now be saved.
EDIT
First, I think you should get rid of the debugging hacks. You can add NSLog or NSAssert, but the rest of that stuff just makes it hard to tell why you want, and confuses the real issue.
Second, what is your real goal here? I can see the name of the method, and I can see the code, but they do not match.
There is so much "cruft" here, it is hard to understand your problem. I am going to repost your code, along with an edit to remove the "open" stuff, and annotate it with questions as code comments.
Hopefully, this change will help you solve your problem.
// First, the method name seems to indicate that some objects will be added
// to the database. however, the only database work in this method is removal.
// I don't get it.
+ (void ) saveCalendarArrayInDbIfItAlreadyDoesNotExist : (NSArray*) appCalendarArray managedDocument: (UIManagedDocument*) managedDocument
{
NSError *error = nil;
NSMutableArray *array = [[NSMutableArray alloc] init];
for (id appCalendar in appCalendarArray) {
if([appCalendar isKindOfClass:[AppCalendarEntity class]]) {
// OK, we are filtering the array of objects. We are only interested in
// objects of type AppCalendarEntity, and are going to use its identity
// property to look for objects of type MyEntity.
// What is the relationship between AppCalendarEntity and MyEntity?
AppCalendarEntity *appCalendarEntity = (AppCalendarEntity*) appCalendar;
NSFetchRequest *requestToSeeIfCalendarWithIdExist = [NSFetchRequest fetchRequestWithEntityName:#"MyEntity"];
requestToSeeIfCalendarWithIdExist.predicate = [NSPredicate predicateWithFormat:#"identifier = %#", appCalendarEntity.identifier];
NSError *InternalError = nil;
[requestToSeeIfCalendarWithIdExist setShouldRefreshRefetchedObjects:YES];
NSArray *result = [managedDocument.managedObjectContext executeFetchRequest:requestToSeeIfCalendarWithIdExist error:&InternalError];
// OK, now we just got a result from searching for a MyEntity, where
// its identifier is the same as the appCalendarEntity.
if(result == nil)
{
// return error
}
// 1 object always return that depict the in memory(context) object we created but not saved. I expect it should be zero because no object has yet been saved to database..
else if(result.count > 1)
{
// I am extremely confused by this code. First, why are you
// checking for more than 1 object? The method name indicates
// you are going to insert something. Furthermore, you are only
// deleting one object. How many do you expect? Also, why are
// you deleting an appCalendar? You were searching for a MyEntity.
// If an appCalendar is a MyEntity, then that's terrible naming.
// Furthermore, it would explain why you are finding it...
// because you create entities by inserting them in a MOC to
// begin with!
[managedDocument.managedObjectContext deleteObject:appCalendar];
}
else
{
// Even more confusion. You are adding this object to an internal
// array, not the database. Furthermore, you are doing it if there
// are either 0 or 1 MyEntity objects in the database with matching
// identifier.
[array addObject:appCalendarEntity];
}
}
}
// saving all the objects
// OK - but the only thing being saved are the ones you deleted...
[ managedDocument updateChangeCount:UIDocumentChangeDone ];
}
Finally, if my hunch is correct, and the calendar objects are actually MyEntity objects, they are already in the MOC - because that's how they get created. When you do a fetch, you can force the search to ignore pending changes (as noted in one of my previous comments) and only accept saved changes.
If you want to ignore pending changes,
fetchRequest.includesPendingChanges = NO;
#Jody Problem has been resolved and thank you for giving time to this question.
First let me address your confusions
1- Yes function is intended to save in the database and it is a helping function. The parameter "appCalendarArray" being passed to this function consist of entities that has already been created in context. I intentionally eliminated the logic since it involves communicating with external apis, parsing json etc etc. The code required for inserting entities in context has already been included in first part of the question.
AppCalendarEntity *appCalendar = [AppCalendarEntity getInstanceWithManagedDocument:manageDocument];
2- I eliminate the entities from context which has been constructed but not yet saved from context, based upon a column in database that should be unique. If we have identifier for object already in database, we do not want to resave it. So, I simply delete it from context. This function works as expected, entities are not re-saved in database. The last line do save the objects that are left in context if any. Most of the time there are a lot.
3- Sorry for mistyping AppCalendarEntity and MyEntity are the same.
Solution
I have added this flag fetchRequest.includesPendingChanges = NO; , delete db, restarted Xcode and it started working. Thank you for your persistence

Share Kit, can't get keyboard up in twitter auth web view

Trying to use share kit to send a link to twitter,and when the webview comes up to log into twitter, you can tap into the username and password fields, get a cursor, but no keyboard pops up.
This is really starting to frustrate me.
Here's how I'm trying to call Share kit. (from an alert)
- (void)alertView:(UIAlertView *)alertView clickedButtonAtIndex:(NSInteger)buttonIndex{
if (alertView.tag == 21){
if (buttonIndex == 1) {
//call SHK
NSLog(#"Calling SHK");
// Create the item to share (in this example, a url)
NSURL *url = [NSURL URLWithString:#"http://link.com"];
SHKItem *item = [SHKItem URL:url title:[NSString stringWithFormat:#"I just played a %d by %d board on #GAMENAME and solved it in %d moves!", down, across, turns]];
// Get the ShareKit action sheet
[alertView dismissWithClickedButtonIndex:0 animated:NO];
SHKActionSheet *actionSheet = [SHKActionSheet actionSheetForItem:item];
// Display the action sheet
[actionSheet showInView:self.view];
}
}
It was a problem with UIResponder
UIResponder Troubles

CLLocation manager updates from background thread

I'm launching a localization request using Grand Central Dispatch :
- (void) findGroceriesNearMe {
dispatch_queue_t downloadQueue = dispatch_queue_create("Groceries downloader", NULL);
dispatch_async(downloadQueue, ^{
CLLocationCoordinate2D userLocation = [LocationManagerController findMeWithCaller:self];
dispatch_async(dispatch_get_main_queue(), ^{
[self userSuccessFullyFound:userLocation];
});
});
dispatch_release(downloadQueue);
}
It calls a static method in my Singleton class LocationManager Controller :
+ (CLLocationCoordinate2D) findMeWithCaller: (UIViewController *) viewController {
LocationManagerController *locationManagerController = [LocationManagerController locationManagerController];
[locationManagerController startUpdates];
while(![locationManagerController getterDone]){
//mystique pour nous-- a approfondir
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
In the startUpdates method, the CLLocationManager, property of LocationManagerController, is initialized and asked to startUpdatingLocation.
Finally, the method when location updates happen :
- (void)locationManager:(CLLocationManager *)manager didUpdateToLocation:(CLLocation *)newLocation fromLocation:(CLLocation *)oldLocation
{
locationDenied = NO;
NSLog(#"%f,%f",newLocation.coordinate.latitude,newLocation.coordinate.longitude);
NSDate* eventDate = newLocation.timestamp;
NSTimeInterval howRecent = [eventDate timeIntervalSinceNow];
// On vérifie que la newLocation est récente
if (abs(howRecent) > 10.0) {
return;
}
// Test if it's not an invalid measurement
if (newLocation.horizontalAccuracy < 0) return;
// Test the measurement to see if it meets the desired accuracy
if (newLocation.horizontalAccuracy <= manager.desiredAccuracy)
{
latitude = newLocation.coordinate.latitude;
longitude = newLocation.coordinate.longitude;
locationDefined = YES;
[self setterDone:YES];
}
}
My problem is that the locationManager only send 3 location updates and then stops sending updates even though I didn't ask it to stop. So basically, I never get out of the while(![locationManagerController getterDone]) loop.
By the way, before trying to implement this using GCD, it was working fine so I guess the issue has to do with my implementation of multi-threading.
Any idea ?
Edit
I don't get any error in the console. The program just keeps running but I'm stuck in that while loop and nothing else happens after the 3 first location updates.
Thanks !
From CLLocationManager class reference:
Configuration of your location manager object must always occur on a
thread with an active run loop, such as your application’s main
thread.
A guess. If you are sitting at your desk and testing with your simulator the accuracy may not get better what you want
if (newLocation.horizontalAccuracy <= manager.desiredAccuracy)
So you may get stuck in your loop. Try with higher accuracy while at your desk. Also consider if the accuracy is never better that what you want since it maybe that the gps reception is not good.
Let me know if that helps or if I was way off the mark :-)
-- Fasttouch

Resources