Update UI after requestAccessToAccountsWithType - facebook

I'm developing an app to help me understand OBJECTIVE-X/OSX.
The app simply connects to Facebook and sends a notification using NSUserNotification.
It is working fine, but now I want to add some UI to the mix.
To make the example simpler, I want to update a label (NSTextField) to show the status of the Facebook connection.
Connecting…
Connected
Failed
I have the following code in one File FacebookRequest.m
- (void) connectFacebook{
if(self.account == nil){
self.account = [[ACAccountStore alloc]init];
}
ACAccountType *facebookAccount = [self.account
accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
NSDictionary *options = #{
ACFacebookAppIdKey: #"MY_CODE",
ACFacebookPermissionsKey: #[#"email",
#"user_about_me",
#"user_likes",
#"manage_notifications",
#"user_activities"],
ACFacebookAudienceKey: ACFacebookAudienceFriends
};
[self.account requestAccessToAccountsWithType:facebookAccount
options:options
completion:^(BOOL success, NSError *error){
if(success){
NSArray *accounts = [self.account accountsWithAccountType:facebookAccount];
self.account = [accounts lastObject];
}
else{
NSLog(#"Erro %#", [error description]);
}
}];
}
and the following one in my AppDelegate.m
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
[self.statusFacebook setStringValue:#"Connecting…"];
FacebookRequest *request = [[FacebookRequest alloc]init];
[request connectFacebook];
}
What is the best way to update the UI after the request is complete and I have an account?
I'm having troubles since the request is asynchronous and I can't return any value inside the requestAccessToAccountsWithType block. Another point is that if I put some "ifs" to check if my account is nil after it, it will be executed before the block has finished executing, so the account would still be nil.
Thanks!
PS.: Sorry for the English if it is not clear enough.

You may use NSNotificationCenter for this purpose:
[self.account requestAccessToAccountsWithType:facebookAccount
options:options
completion:^(BOOL success, NSError *error){
if(success){
NSArray *accounts = [self.account accountsWithAccountType:facebookAccount];
self.account = [accounts lastObject];
// You post a notification that the UI should update here
[[NSNotificationCenter defaultCenter] postNotificationName:#"UpdateUI" object:nil];
}
else{
NSLog(#"Erro %#", [error description]);
}
}];
Then, you add your viewController that should update its UI as an observer of this notification:
- (void)viewDidLoad
{
[super viewDidLoad];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateUI) name:#"UpdateUI" object:nil];
}
- (void)updateUI {
// Here you actually update your UI
}
p.s. if you are not using arc you also remove the observer in dealloc:
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:self];

Related

Google Drive SDK - Stream audio file

Is there a way to stream audio file from Google Drive with AVPlayer?
I have tried with both file.downloadUrl and file.webContentLink and it is not working.
Code:
GTLDriveFile *file = [self.data objectAtIndex:indexPath.row];
if (player)
{
[player removeObserver:self forKeyPath:#"status"];
[player pause];
}
player = [AVPlayer playerWithURL:[NSURL URLWithString:file.downloadUrl]];
//or
//player = [AVPlayer playerWithURL:[NSURL URLWithString:file.webContentLink]];
[player addObserver:self forKeyPath:#"status" options:0 context:nil];
if (delegate && [delegate respondsToSelector:#selector(audioPlayerDidStartBuffering)])
[delegate audioPlayerDidStartBuffering];
If it is not possible to stream, is it possible to start download in /tmp folder and play while downloading?
I could solve it just by appending the access_token to the download url
audiofile.strPath=[NSString stringWithFormat#"%#&access_token=%#",downloadUrl,accessToken];
pass the strPath to your AvPlayer object.
you can fetch the access token from the GTMOAuth2Authentication object
Note that you might need to refresh it if its expires.
Hope this helps you.
Regards
Nitesh
That is simply because you didn't provide your client's access code from header of the download request. When you get downloadUrl, that link is not public link and you should provide same authorization as you did for all other Drive API requests.
For example, Object-c code for downloading content from downloadUrl would be like this:
+ (void)downloadFileContentWithService:(GTLServiceDrive *)service
file:(GTLDriveFile *)file
completionBlock:(void (^)(NSData *, NSError *))completionBlock {
if (file.downloadUrl != nil) {
// More information about GTMHTTPFetcher can be found on
// http://code.google.com/p/gtm-http-fetcher
GTMHTTPFetcher *fetcher =
[service.fetcherService fetcherWithURLString:file.downloadUrl];
[fetcher beginFetchWithCompletionHandler:^(NSData *data, NSError *error) {
if (error == nil) {
// Success.
completionBlock(data, nil);
} else {
NSLog(#"An error occurred: %#", error);
completionBlock(nil, error);
}
}];
} else {
completionBlock(nil,
[NSError errorWithDomain:NSURLErrorDomain
code:NSURLErrorBadUrl
userInfo:nil]);
}
}
Or, if you can pass additional parameter to AVPlayer so that it sends additional header to authorize while downloading file, add the following header:
Authorization: Bearer {YOUR_ACCESS_TOKEN}

Method is not getting called twice in IOS

I am working on Instagram integration in IOS.Every thing goes well.I am geting the feeds of the user and displaying them on tableview and also in scroll view.Here the user is allowed to refresh the page.While refreshing the method is not getting called and its getting crashed because of zero objects in array.I had used the following code to call the method.
-(IBAction)loginAction:(id)sender
{
AppDelegate* appDelegate_new = (AppDelegate*)[UIApplication sharedApplication].delegate;
[appDelegate_new.instagram authorize:[NSArray arrayWithObjects:#"comments", #"likes", nil]];
if ([appDelegate.instagram isSessionValid]) {
// NSLog(#"ViewDidLoad Session Valid");
loginView.hidden=YES;
crossButton.hidden=YES;
settingsButton.hidden=NO;
noticeView.hidden=YES;
[self.view addSubview:feedsView];
// [self.logOutView removeFromSuperview];
self.feedsView.frame=CGRectMake(0, 0, 240, 300);
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:#"users/self/feed", #"method", nil];
[appDelegate.instagram requestWithParams:params
delegate:self];
}
}
The called method was like this
- (void)request:(IGRequest *)request didLoad:(id)result
{
[self performSelector:#selector(startspinner) withObject:nil afterDelay:0.1];
self.data = (NSMutableArray *)[result objectForKey:#"data"];
// NSLog(#"Data Count is %#",[self.data description]);
createdArray=[[NSMutableArray alloc]init];
//*****Here I am performing Json Parsing******//
}
I am calling the above request method again while refreshing
- (void)dropViewDidBeginRefreshing:(ODRefreshControl *)refreshControl
{
[createdArray removeAllObjects];
NSMutableDictionary* params = [NSMutableDictionary dictionaryWithObjectsAndKeys:#"users/self/feed", #"method", nil];
[appDelegate.instagram requestWithParams:params
delegate:self];
[self performSelector:#selector(refreshData) withObject:nil afterDelay:5.0];
}
Please tell me where I am going wrong.Correction appreciated.Thanks in advance.
Try it....
[NSTimer scheduledTimerWithTimeInterval:0.5f
target:self
selector:#selector(showTime)
userInfo:NULL
repeats:YES];
- (void)showTime
{
NSLog(#"Method called");
}
Use NSTimer for call method frequently.
Hope i helped.

Open Twitter Setting from ACAccountStore (iOS 5.1 TWITTER)

in iOS 5.0 i was opening Twitter setting from my app by
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"prefs:root=TWITTER"]];
but , this features is removed in iOS 5.1 , hence i can not able to open twitter setting .
Now i am using
+ (void)makeRequestsWithURL: (NSURL *)url {
// Create an account store object.
ACAccountStore *accountStore = [[ACAccountStore alloc] init];
// Create an account type that ensures Twitter accounts are retrieved.
ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[self canTweetStatus];
// Request access from the user to use their Twitter accounts.
[accountStore requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
if(granted) {
// Get the list of Twitter accounts.
NSArray *accountsArray = [accountStore accountsWithAccountType:accountType];
// For the sake of brevity, we'll assume there is only one Twitter account present.
// You would ideally ask the user which account they want to tweet from, if there is more than one Twitter account present.
if ([accountsArray count] > 0) {
// Grab the initial Twitter account to tweet from.
ACAccount *twitterAccount = [accountsArray objectAtIndex:0];
// Create a request, which in this example, posts a tweet to the user's timeline.
// This example uses version 1 of the Twitter API.
// This may need to be changed to whichever version is currently appropriate.
TWRequest *postRequest = [[TWRequest alloc] initWithURL:url parameters:nil requestMethod:TWRequestMethodPOST];
// Set the account used to post the tweet.
[postRequest setAccount:twitterAccount];
// Perform the request created above and create a handler block to handle the response.
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
NSString *output = [NSString stringWithFormat:#"HTTP response status: %i", [urlResponse statusCode]];
iOS5Twitter *twitter5 = [[iOS5Twitter alloc] init];
[twitter5 performSelectorOnMainThread:#selector(displayText:) withObject:output waitUntilDone:NO];
[twitter5 release]; }];
}
}
}];
}
for making request, i am able to check wether i am loged in or not by the
if ([TWTweetComposeViewController canSendTweet])
but now i want : if i am not loged in it would be show an alert like shown in image and want to move to the twitter setting . is it possible ? or i have to manually go ti twitter setting ?
It is little tricky , i get by the removing the subviews in *TWTWeetComposeViewController*, so it shows only alert when user is not loged in and by the clicking on setting button , we can open Setting page in my app.
+ (void)setAlertForSettingPage :(id)delegate
{
// Set up the built-in twitter composition view controller.
TWTweetComposeViewController *tweetViewController = [[TWTweetComposeViewController alloc] init];
// Create the completion handler block.
[tweetViewController setCompletionHandler:^(TWTweetComposeViewControllerResult result) {
[delegate dismissModalViewControllerAnimated:YES];
}];
// Present the tweet composition view controller modally.
[delegate presentModalViewController:tweetViewController animated:YES];
//tweetViewController.view.hidden = YES;
for (UIView *view in tweetViewController.view.subviews){
[view removeFromSuperview];
}
}
here , deleate is your viewcontroller , if you are using this method inside your viewcontroller just use self instead of delegate.
iOS 6 uses SLComposeViewController instead of TWTweetComposeViewController, so now you have to do this if you want it to work on iOS 6 and iOS 5:
UIViewController *tweetComposer;
if([SLComposeViewController class] != nil)
{
tweetComposer = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter];
[(SLComposeViewController *)tweetComposer setCompletionHandler:^(SLComposeViewControllerResult result)
{
// do whatever you want
}];
}
else
{
tweetComposer = [[TWTweetComposeViewController alloc] init];
[(TWTweetComposeViewController *)tweetComposer setCompletionHandler:^(TWTweetComposeViewControllerResult result)
{
// do whatever you want
}];
}
for (UIView *view in [[tweetComposer view] subviews])
[view removeFromSuperview];
[self presentViewController:tweetComposer animated:NO completion:nil];
Building upon Senior's and PJR's answers above, this is what worked for me.
Same as Senior's, except with a dimissViewController in the completion handler. For me this got around the problem that the empty view controller would stick around after I returned to the app from the settings. It would overlay my view controller and make it useless.
Brilliant solution, thanks Senior and PJR.
UIViewController *tweetComposer;
if([SLComposeViewController class] != nil)
{
tweetComposer = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter];
[(SLComposeViewController *)tweetComposer setCompletionHandler:^(SLComposeViewControllerResult result)
{
[self dismissViewControllerAnimated:NO completion:nil];
}];
}
else
{
tweetComposer = [[TWTweetComposeViewController alloc] init];
[(TWTweetComposeViewController *)tweetComposer setCompletionHandler:^(TWTweetComposeViewControllerResult result)
{
[self dismissViewControllerAnimated:NO completion:nil];
}];
}
for (UIView *view in [[tweetComposer view] subviews])
[view removeFromSuperview];
[self presentViewController:tweetComposer animated:NO completion:nil];
To remove the post view,
instead using this code:
for (UIView *view in [[tweetComposer view] subviews])
[view removeFromSuperview];
use this:
tweetComposer.view.alpha = 0;
works both for twitter and facebook.

returning UIImage from block

I have the following code:
- (UIImage *) getPublisherLogo
{
//check the cache if the logo already exists
NSString * imageUrl = [NSString stringWithFormat:#"%#/%#&image_type=icon", self.baseUrl, self.imageUrl_];
ASIHTTPRequest * imageRequest = [ASIHTTPRequest requestWithURL:[NSURL URLWithString:imageUrl]];
[imageRequest setTimeOutSeconds:30.0];
[imageRequest setDownloadCache:[ASIDownloadCache sharedCache]];
[imageRequest setCacheStoragePolicy:ASICachePermanentlyCacheStoragePolicy];
[imageRequest setCachePolicy:ASIAskServerIfModifiedWhenStaleCachePolicy|ASIFallbackToCacheIfLoadFailsCachePolicy];
[imageRequest setCompletionBlock:^(void){
UIImage *img = [UIImage imageWithData:[imageRequest responseData] ];
if (img){
return img;
}
}];
[imageRequest setFailedBlock:^(void){
NSLog(#"Error in pulling image for publisher %#", [[imageRequest error] userInfo]);
}];
[imageRequest startAsynchronous];
}
}
The issue is that the return value/UIImage is returned at a block. How do I avoid this?
You're unable to return anything from the completion block because it's returned void.
You'll probably need to create a new method like setLogo:(UIImage *)image on the object that's expecting the image to be set, and call that method from within the completion block.
You can place your img pointer outside the block and declare it __BLOCK and use it as a closure. But you really need to be asking yourself what do you plan to do with img, bearing in mind the call is made asynchronously. I would imagine you should make another call in the block to another method and pass in the populated image as a parameter.
For getting an object from the ASIHttpRequest response, I use notifications.
For example, in the calling viewController
- (void)viewDidLoad {
// Subscribe notifications
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(onGetPhoto:) name:#"getPhotoNotification" object:nil];
}
- (void)viewDidUnload {
[super viewDidUnload];
// Unsubscribe from notifications
[[NSNotificationCenter defaultCenter] removeObserver:self name:#"getPhotoNotification" object:nil];
}
- (void)onGetPhoto:(NSNotification *)notification {
...
}
in your completion block
[[NSNotificationCenter defaultCenter] postNotificationName:#"getPhotoNotification" object:self userInfo:userInfo];
With your photo in userInfo.

I think I lost my Managed Object Context

So I'm working on a clone of CoreDataBooks.
It's a little different. When the '+' button is pushed, it launches a navController, containing 2 views. The first (AddPatientVC) asks for name of the Patient then its pushed to a 2nd View Controller (AddPatientDetailVC) which asks for more detailed information. It's the 2nd view controller that I've got the delegate set up with, not the first, like in CoreDataBooks.
For some reason, when the delegate method is fired, the notification method doesn't get fired, so I've somehow lost track of my MOC, either the specific MOC for adding a new Patient.
The specific error i get is:'+entityForName: could not locate an NSManagedObjectModel for entity name 'Patient''
Here's my code - addPatient, delegate method and notification method. Any suggestions on simplification would be appreciated. Thanx
-(void)addPatient:(id)sender
{
PatientAddViewController *patientAddViewController = [[PatientAddViewController alloc] initWithNibName:#"PatientAddViewController" bundle:nil];
PatientAddDetailViewController *patientAddDetailViewController = [[PatientAddDetailViewController alloc] initWithNibName:#"PatientAddViewController" bundle:nil];
patientAddDetailViewController.delegate = self;
//Create a new MOC for adding a book
NSManagedObjectContext *addingContext = [[NSManagedObjectContext alloc] init];
self.addPatientManagedObjectContext = addingContext;
[addingContext release];
[addPatientManagedObjectContext setPersistentStoreCoordinator:[[fetchedResultsController managedObjectContext] persistentStoreCoordinator]];
patientAddViewController.patient = (Patient *)[NSEntityDescription insertNewObjectForEntityForName:#"Patient" inManagedObjectContext:addingContext];
//patientAddViewController.addPatientManagedObjectContext = self.addPatientManagedObjectContext;
UINavigationController *addingNavController = [[UINavigationController alloc] initWithRootViewController:patientAddViewController];
[self.navigationController presentModalViewController:addingNavController animated:YES];
[addingNavController release];
[patientAddViewController release];
}
- (void)patientAddDetailViewController:(PatientAddDetailViewController *)controller didFinishWithSave:(BOOL)save
{
NSLog(#"Delegate Method fired");
if (save)
{
NSNotificationCenter *dnc = [NSNotificationCenter defaultCenter];
//The notification isn't firing becuase addPatientManagedObjectContext is null for some reason
[dnc addObserver:self selector:#selector(addControllerContextDidSave:) name:NSManagedObjectContextDidSaveNotification object:addPatientManagedObjectContext];
NSError *error;
//if (![patient.managedObjectContext save:&error])
if (![addPatientManagedObjectContext save:&error])
{
NSLog(#"Before Error");
//Handle the error...
NSLog(#"Unresolved Error %#, %#",error, [error userInfo]);
exit(-1);//Fail
NSLog(#"After Error");
}
[dnc removeObserver:self name:NSManagedObjectContextDidSaveNotification object:addPatientManagedObjectContext];
}
self.addPatientManagedObjectContext = nil;
[self.tableView reloadData];
[self dismissModalViewControllerAnimated:YES];
}
- (void)addControllerContextDidSave:(NSNotification*)saveNotification {
NSLog(#"Save Notification Fired");
NSManagedObjectContext *context = [fetchedResultsController managedObjectContext];
// Merging changes causes the fetched results controller to update its results
[context mergeChangesFromContextDidSaveNotification:saveNotification];
}
It looks like you create the context, and store it in self
NSManagedObjectContext *addingContext = [[NSManagedObjectContext alloc] init];
self.addPatientManagedObjectContext = addingContext;
[addingContext release];
But then you call the "add" method on the other controller:
patientAddViewController.patient = (Patient *)[NSEntityDescription
insertNewObjectForEntityForName:#"Patient" inManagedObjectContext:addingContext];
(remember, you released 'addingContext' up above, 'addingContext' is not guaranteed to contain anything valid at this point)
Looks like you should be passing self.addPatientManagedObjectContext rather than addingContext in your insertNewObjectForEntityForName:#"Patient" line.