I want to get friends(People) list from Google+ in iOS app.
I am using Google+ api tutorial given in link
https://developers.google.com/+/mobile/ios/getting-started
I am created new project on Google+ Developer Console the link is
https://console.developers.google.com/project
Getting following error in -(void)getPeopleInfo.
[lvl=3] __31-[ViewController getPeopleInfo]_block_invoke() Error: Error Domain=com.google.GTLJSONRPCErrorDomain Code=401 "The operation couldn’t be completed. (Invalid Credentials)" UserInfo=0x14d89340 {error=Invalid Credentials, GTLStructuredError=GTLErrorObject 0x14d855e0: {message:"Invalid Credentials" code:401 data:[1]}, NSLocalizedFailureReason=(Invalid Credentials)}
2014-03-13 12:40:21.026 GPlusDemo[636/0x3d35718c] [lvl=3] __31-[ViewController getPeopleInfo]_block_invoke() Error: Error Domain=com.google.GTLJSONRPCErrorDomain Code=401 "The operation couldn’t be completed. (Invalid Credentials)" UserInfo=0x14d85f90 {error=Invalid Credentials, GTLStructuredError=GTLErrorObject 0x14d85ba0: {message:"Invalid Credentials" code:401 data:[1]}, NSLocalizedFailureReason=(Invalid Credentials)}
I written the following code in ViewController.m
- (void)viewDidLoad
{
[super viewDidLoad];
// Do any additional setup after loading the view, typically from a nib.
GPPSignIn *signIn = [GPPSignIn sharedInstance];
signIn.shouldFetchGooglePlusUser = YES;
//signIn.shouldFetchGoogleUserEmail = YES; // Uncomment to get the user's email
// You previously set kClientId in the "Initialize the Google+ client" step
signIn.clientID = kClientId;
// Uncomment one of these two statements for the scope you chose in the previous step
signIn.scopes = #[ kGTLAuthScopePlusLogin]; // "https://www.googleapis.com/auth/plus.login" scope
signIn.scopes = #[ #"profile" ]; // "profile" scope
// Optional: declare signIn.actions, see "app activities"
signIn.delegate = self;
[signIn trySilentAuthentication];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated.
}
-(void)refreshInterfaceBasedOnSignIn
{
if ([[GPPSignIn sharedInstance] authentication]) {
// The user is signed in.
NSLog(#"Login");
self.signInButton.hidden = YES;
// Perform other actions here, such as showing a sign-out button
[self getPeopleInfo];
} else {
self.signInButton.hidden = NO;
// Perform other actions here
}
}
- (void)finishedWithAuth: (GTMOAuth2Authentication *)auth
error: (NSError *) error
{
NSLog(#"Received error %# and auth object %#",error, auth);
if (error) {
// Do some error handling here.
} else {
[self refreshInterfaceBasedOnSignIn];
}
}
- (void)signOut {
[[GPPSignIn sharedInstance] signOut];
}
- (void)disconnect {
[[GPPSignIn sharedInstance] disconnect];
}
- (void)didDisconnectWithError:(NSError *)error {
if (error) {
NSLog(#"Received error %#", error);
} else {
// The user is signed out and disconnected.
// Clean up user data as specified by the Google+ terms.
}
}
-(void)getPeopleInfo
{
GTLServicePlus* plusService = [[GTLServicePlus alloc] init];
plusService.retryEnabled = YES;
[plusService setAuthorizer:[GPPSignIn sharedInstance].authentication];
GTLQueryPlus *query =
[GTLQueryPlus queryForPeopleListWithUserId:#"me"
collection:kGTLPlusCollectionVisible];
[plusService executeQuery:query
completionHandler:^(GTLServiceTicket *ticket,
GTLPlusPeopleFeed *peopleFeed,
NSError *error) {
if (error) {
GTMLoggerError(#"Error: %#", error);
} else {
// Get an array of people from GTLPlusPeopleFeed
NSArray* peopleList = [peopleFeed.items mutableCopy];
NSLog(#"peopleList:%#", peopleList);
}
}];
}
Call the following method, after login success, for me, i am getting friends list using below method
-(void)finishedWithAuth: (GTMOAuth2Authentication *)auth
error: (NSError *) error {
GTLServicePlus* plusService = [[GTLServicePlus alloc] init];
plusService.retryEnabled = YES;
[plusService setAuthorizer:[GPPSignIn sharedInstance].authentication];
GTLQueryPlus *query =
[GTLQueryPlus queryForPeopleListWithUserId:#"me"
collection:kGTLPlusCollectionVisible];
[plusService executeQuery:query
completionHandler:^(GTLServiceTicket *ticket,
GTLPlusPeopleFeed *peopleFeed,
NSError *error) {
if (error) {
GTMLoggerError(#"Error: %#", error);
} else {
// Get an array of people from GTLPlusPeopleFeed
NSArray* peopleList = peopleFeed.items;
NSLog(#"peopleList %# ",peopleList);
}
}];
}
As per my experience, Google+ SDK is not having any method fetching the friend list currently.
It's suggested to use Google Contacts API for fetching contacts. It may happen that contacts fetched from this API are not active on Google+. So it's mixed list.
So, Wait for the updates from Google.
We have a way to get google plus visible friends information. Please take a look on description and if it will be not clear enough for you, then I will provide more description.
GTMOAuth2Authentication *auth;
/*That you will get when you login by your google plus account. So I am considering that you already have it.*/
NSMutableArray *arrFriends = [NSMutableArray new];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^
{
NSURL *url = [NSURL URLWithString:[NSString stringWithFormat:#"https://www.googleapis.com/plus/v1/people/%#/people/visible?orderBy=alphabetical&access_token=%#",#"your_user_id",auth.accessToken]];
/*When you login via Google plus and fetch your profile information, you will get your user id.*/
ASIHTTPRequest *request = [[ASIHTTPRequest alloc] initWithURL:url];
[request startSynchronous];
if(LOGS_ON) NSLog(#"GooglePlusConnect-->getchGooglePlusFriends-->responseString = %#",request.responseString);
});
Plaease let me know if it is not clear enough for you, then i will provide some more description.
Hi I was also facing same error but it got resolved. The problem is in setting scope.
set the scope as #define kGTLAuthScopePlusLogin #"https://www.googleapis.com/auth/plus.login"
- (void)viewDidLoad
{
[super viewDidLoad];
GPPSignIn *signInG = [GPPSignIn sharedInstance];
signInG.shouldFetchGooglePlusUser = YES;
signInG.shouldFetchGoogleUserEmail = YES;
signInG.clientID = kClientId;
signInG.scopes = # [kGTLAuthScopePlusLogin];
signInG.delegate = self;
[signInG trySilentAuthentication];
}
It will fetch friends details like name, image url but not Email address. For fetching Email address try to use Contact API. iOS has NSXMLParser, the contact api code is given in JS, java, net u could use that and fetch the details.
Hi i was also facing the same problem. This problem Occurring Because of scope.
In Your code You have override the scope.
signIn.scopes = #[ kGTLAuthScopePlusLogin];
With
signIn.scopes = #[ #"profile" ]; // "profile" scope
So, You have to change Your scope By Simple
signIn.scopes = #[ kGTLAuthScopePlusLogin];
Or
signIn.scopes = #[ kGTLAuthScopePlusLogin,#"profile"];
GTLServicePlus* plusService = [[GTLServicePlus alloc] init];
plusService.retryEnabled = YES;
[plusService setAuthorizer:[GPPSignIn sharedInstance].authentication];
GTLQueryPlus *query =
[GTLQueryPlus queryForPeopleListWithUserId:#"me"
collection:kGTLPlusCollectionVisible];
[plusService executeQuery:query
completionHandler:^(GTLServiceTicket *ticket,
GTLPlusPeopleFeed *peopleFeed,
NSError *error) {
if (error) {
GTMLoggerError(#"Error: %#", error);
} else {
// Get an array of people from GTLPlusPeopleFeed
NSArray* peopleList = peopleFeed.items;
NSLog(#"peopleList %# ",peopleList.description);
for (NSArray *dict in peopleFeed.items) {
NSString *strID=(NSString*)((GTLPlusPerson*)dict).identifier;
NSLog(#"strID %#",strID);
}
}
}];
Related
I am trying to access the gmail messages in offline access mode. gmail access token I get from the server end.
I followed this link to here
I created my own authenticator class and subclass from GTMOAuth2Authentication and implemented the protocol as mentioned in the above link.
The canAutherize always fail and even though I make gmail query, It crashes in authorize function. Any help would be appreciated.
- (void)viewDidLoad {
[super viewDidLoad];
if ([self.subjectInfo length] > 0) {
[self.subjectLabel setText:self.subjectInfo];
}else {
[self.subjectLabel setText:#""];
}
self.service = [[GTLServiceGmail alloc] init];
[self fetchAccessToken];
// Initialize the Gmail API service & load existing credentials from the keychain if available.
}
// When the view appears, ensure that the Gmail API service is authorized, and perform API calls.
- (void)viewDidAppear:(BOOL)animated {
if (!self.service.authorizer.canAuthorize) {
// Not yet authorized, request authorization by pushing the login UI onto the UI stack.
// [self presentViewController:[self createAuthController] animated:YES completion:nil];
[self fetchAccessToken];
} else {
[self fetchMail:[self gmailMsgId]];
}
}
-(void) fetchAccessToken
{
[[APIManager sharedManager] fetchGmailToken:^(NSDictionary *resultDict) {
if (resultDict != nil) {
self.accessToken = [resultDict objectForKey:#"access_token"];
_authInst = [[GmailAuthenticator alloc] initWithAccessToken:self.accessToken];
self.service.authorizer = _authInst;
}
} failure:^(NSError *error) {
NSLog(#"fail to fetch access token");
}];
}
- (void)fetchMail:(NSString*)messageId {
GTLQueryGmail *query = [GTLQueryGmail queryForUsersMessagesGet];
query.messageId = messageId;
[self.service executeQuery:query
delegate:self
didFinishSelector:#selector(displayResultWithTicket:finishedWithObject:error:)];
}
- (void)displayResultWithTicket:(GTLServiceTicket *)ticket
finishedWithObject:(GTLGmailMessage *)messageResponse
error:(NSError *)error {
if (error == nil) {
NSLog(#"description is %#", [messageResponse description]);
} else {
NSLog(#"error : %#", [error description]);
}
}
I am trying to find the email of the logged in account from the google login API in iOS using Swift.
The code i used is the same as given in the google Developers instruction page.
This is my code
description = [NSString stringWithFormat: #"%# %# %# %# %# %#",
person.displayName,person.gender,person.ageRange.min,person.ageRange.max,person.emails,person.birthday];
The output when i print this "description" id is this:
Karanvir Singh male 18 20 (
"GTLPlusPersonEmailsItem 0x7fbe4a67aee0: {value:\"karanvir95#gmail.com\" type:\"account\"}"
) (null)
I want to know how i can remove the excess of output when i just want to know the email ID
i want the output as such:
Karanvir Singh male 18 20 karanvir95#gmail.com 17/02/1995
Code as requested:
import "ViewController.h"
import "GoogleOpenSource/GoogleOpenSource.h"
import "GooglePlus/GooglePlus.h"
import "AppDelegate.h"
#interface ViewController ()
#end
#implementation ViewController #synthesize signInButton; #synthesize
signOutHandle; #synthesize signInHandle; #synthesize infoLabel;
#synthesize description;
- (void)viewDidLoad {
[super viewDidLoad];
signOutHandle.hidden=true; }
(void)didReceiveMemoryWarning {
[super didReceiveMemoryWarning];
// Dispose of any resources that can be recreated. }
-(void)refreshInterfaceBasedOnSignIn {
if ([[GPPSignIn sharedInstance] authentication]) {
signOutHandle.hidden=false;
signInHandle.hidden=true;
infoLabel.text=description;
} else {
} }
(void)finishedWithAuth: (GTMOAuth2Authentication *)auth error: (NSError *) error {
NSLog(#"Received error %# and auth object %#",error, auth);
if (error) {
// Do some error handling here.
} else {
GTLServicePlus* plusService = [[GTLServicePlus alloc] init];
plusService.retryEnabled = YES;
[plusService setAuthorizer:[GPPSignIn sharedInstance].authentication];
GTLQueryPlus *query = [GTLQueryPlus queryForPeopleGetWithUserId:#"me"];
query.collection=kGTLPlusCollectionVisible;
[plusService executeQuery:query
completionHandler:^(GTLServiceTicket *ticket,
GTLPlusPerson *person,
NSError *error){
if (error) {
GTMLoggerError(#"Error: %#", error);
} else {
// [person retain];
NSString *age = [NSString string];
description = [NSString stringWithFormat: #"%# %# %# %# %# %# %#", person.displayName,person.gender,person.ageRange.min,person.ageRange.max,person.emails,person.birthday,email];
NSLog(description);
}
GTLQueryPlus *query2 =
[GTLQueryPlus queryForPeopleListWithUserId:#"me"
collection:kGTLPlusCollectionVisible];
[plusService executeQuery:query2 completionHandler:^(GTLServiceTicket *ticket, GTLPlusPeopleFeed *peopleFeed, NSError *error) {
if (error) {
GTMLoggerError(#"Error: %#", error);
} else {
// Get an array of people from GTLPlusPeopleFeed
NSArray* peopleList = peopleFeed.items;
}
}];
[self refreshInterfaceBasedOnSignIn];
}];
} }
- (IBAction)signOutButton:(id)sender {
[[GPPSignIn sharedInstance] signOut];
[[GPPSignIn sharedInstance] disconnect];
signOutHandle.hidden=true;
signInHandle.hidden=false;
infoLabel.text=#""; }
(void)signOut {
[[GPPSignIn sharedInstance] signOut]; }
(void)disconnect {
[[GPPSignIn sharedInstance] disconnect]; }
(IBAction)signedIn:(id)sender {
GPPSignIn *signIn = [GPPSignIn sharedInstance];
signIn.shouldFetchGooglePlusUser = YES;
signIn.shouldFetchGoogleUserEmail = YES;
signIn.clientID = kClientId;
AppDelegate *appDelegate = (AppDelegate *) [[UIApplication sharedApplication] delegate];
signIn.shouldFetchGoogleUserEmail = YES;
signIn.delegate = self;
signIn.scopes = #[ kGTLAuthScopePlusUserinfoProfile, kGTLAuthScopePlusLogin,kGTLAuthScopePlusMe,kGTLAuthScopePlusUserinfoEmail
];
signIn.delegate = self;
[signIn authenticate];
email=(#"%#",signIn.userEmail);
NSLog(#" email:%#",email); }
(void)didDisconnectWithError:(NSError *)error {
if (error) {
NSLog(#"Received error %#", error);
} else {
NSLog(#"The user is signed out and disconnected.");
// The user is signed out and disconnected.
// Clean up user data as specified by the Google+ terms.
} }
#end
description = [NSString stringWithFormat: #"%# %# %# %# %# %# %#", person.displayName, person.gender,person.ageRange.min,person.ageRange.max,person.emails.value,person.birthday];
And I want to note that your person has nil bithday
So I am using Parse to link a user with their twitter account. In the app delegate I have the following:
[PFTwitterUtils initializeWithConsumerKey:CONSUMER_KEY consumerSecret:CONSUMER_SECRET];
Then the button which the user clicks to link the user to facebook calls the following:
-(IBAction)twitterConnectPressed{
NSLog(#"twitter");
[PFTwitterUtils linkUser:[PFUser currentUser] block:^(BOOL succeeded, NSError* error){
NSLog(#"haha");
if(succeeded){
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"Done!" message:#"Connected with Twitter!" delegate:self cancelButtonTitle:#"okay" otherButtonTitles: nil];
[alert show];
self.fbButton.backgroundColor = [TGAPublic grey];
}else{
UIAlertView* alert = [[UIAlertView alloc] initWithTitle:#"Oops" message:error.userInfo[#"error"] delegate:self cancelButtonTitle:#"okay" otherButtonTitles: nil];
[alert show];
}
}];
}
However even though linkUser:block: is called it doesn't do anything at all. It doesn't create a pop up to log in to twitter like [PFFacebookUtils linkUser:] does and therefore doesn't end up calling the block either
PFTwitterUtils does not appear to handle all cases on iOS. In particular, if you do not have an account setup (Settings->Twitter) it does not fire up a web view and attempt to used web oauth. Conversely if you have multiple Twitter accounts configured (again in Settings) then it doesn't appear to fire up an action sheet to allow you to select which account you'd like to link.
There's a great tutorial on how to do these things which exposes an extension to PFFacebookUtils here: http://natashatherobot.com/ios-twitter-login-parse/
It does not do linking though, just login, but should be a good basis to add linking.
I've got similar problem with link/unlink methods for both PFFacebookUtils and PFTwitterUtils (v. 1.7.4).
The only way I managed to make it work was to replace them by, unfortunately, messing with internal Parse implementation of authData:
#import "TwitterAuthProvider.h"
#import "PFTwitterUtils.h"
#import "PFUser.h"
static NSString * const kTwitterKey = #"XXX";
static NSString * const kTwitterSecret = #"XXX";
#implementation TwitterAuthProvider
- (instancetype)init {
if ((self = [super init])) {
[PFTwitterUtils initializeWithConsumerKey:kTwitterKey consumerSecret:kTwitterSecret];
}
return self;
}
- (void)setAuthData:(id)twAuthData forUser:(PFUser *)user {
static NSString * const kParseAuthDataKey = #"authData";
static NSString * const kParseLinkedServiceNamesKey = #"linkedServiceNames";
static NSString * const kParseAuthProviderName = #"twitter";
NSMutableDictionary *authData = [[user valueForKey:kParseAuthDataKey] mutableCopy] ?: [NSMutableDictionary dictionary];
authData[kParseAuthProviderName] = twAuthData ?: [NSNull null];
[user setObject:authData forKey:kParseAuthDataKey];
[user setValue:authData forKey:kParseAuthDataKey];
NSMutableSet *linkedServices = [[user valueForKey:kParseLinkedServiceNamesKey] mutableCopy] ?: [NSMutableSet set];
if (twAuthData) {
[linkedServices addObject:kParseAuthProviderName];
} else {
[linkedServices removeObject:kParseAuthProviderName];
}
[user setValue:linkedServices forKey:kParseLinkedServiceNamesKey];
}
- (void)linkWithCompletion:(PFBooleanResultBlock)completion {
NSParameterAssert(completion != nil);
PFUser *user = [PFUser currentUser];
__weak typeof(self) weakSelf = self;
PF_Twitter *twitter = [PFTwitterUtils twitter];
[twitter authorizeWithSuccess:^(void) {
[weakSelf setAuthData:[self twitterAuthData] forUser:user];
[user saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!succeeded) {
//revert
[weakSelf setAuthData:nil forUser:user];
}
completion(succeeded, error);
}];
} failure:^(NSError *error) {
completion(NO, error);
} cancel:^(void) {
completion(NO, nil);
}];
}
- (void)unlinkWithCompletion:(PFBooleanResultBlock)completion {
NSParameterAssert(completion != nil);
PFUser *user = [PFUser currentUser];
[self setAuthData:nil forUser:user];
[user saveInBackgroundWithBlock:completion];
}
- (NSDictionary *)twitterAuthData {
PF_Twitter *twitter = [PFTwitterUtils twitter];
return #{
#"auth_token" : twitter.authToken,
#"auth_token_secret": twitter.authTokenSecret,
#"consumer_key": kTwitterKey,
#"consumer_secret": kTwitterSecret,
#"id": twitter.userId,
#"screen_name": twitter.screenName,
};
}
#end
I am trying to build an app that will fetch Google Calendar information such as calendars, events etc but I am running into a big problem.
This is my code:
#define GoogleClientID #"client id"
#define GoogleClientSecret #"secret"
#define GoogleAuthURL #"https://accounts.google.com/o/oauth2/auth"
#define GoogleTokenURL #"https://accounts.google.com/o/oauth2/token"
NSString *const kKeychainItemName = #"Calendar Panel: Google Calendar";
- (instancetype)initWithViewController: (UIViewController*) viewController;
{
self = [super init];
if (self) {
_viewController = viewController;
}
return self;
}
- (BOOL)isSignedIn {
NSString *name = [self signedInUsername];
return (name != nil);
}
- (NSString *)signedInUsername {
// Get the email address of the signed-in user
GTMOAuth2Authentication *auth = self.calendarService.authorizer;
BOOL isSignedIn = auth.canAuthorize;
if (isSignedIn) {
return auth.userEmail;
} else {
return nil;
}
}
- (GTMOAuth2Authentication * )authForGoogle
{
//This URL is defined by the individual 3rd party APIs, be sure to read their documentation
NSURL * tokenURL = [NSURL URLWithString:GoogleTokenURL];
// We'll make up an arbitrary redirectURI. The controller will watch for
// the server to redirect the web view to this URI, but this URI will not be
// loaded, so it need not be for any actual web page. This needs to match the URI set as the
// redirect URI when configuring the app with Instagram.
NSString * redirectURI = #"urn:ietf:wg:oauth:2.0:oob";
GTMOAuth2Authentication * auth;
auth = [GTMOAuth2Authentication authenticationWithServiceProvider:#"lifebeat"
tokenURL:tokenURL
redirectURI:redirectURI
clientID:GoogleClientID
clientSecret:GoogleClientSecret];
auth.scope = #"https://www.googleapis.com/auth/userinfo.profile";
return auth;
}
- (void)signInToGoogle
{
if (![self isSignedIn]) {
GTMOAuth2Authentication * auth = [self authForGoogle];
// Display the authentication view
GTMOAuth2ViewControllerTouch * viewController = [[GTMOAuth2ViewControllerTouch alloc] initWithAuthentication:auth
authorizationURL:[NSURL URLWithString:GoogleAuthURL]
keychainItemName:#"GoogleKeychainName"
delegate:self
finishedSelector:#selector(viewController:finishedWithAuth:error:)];
[[self.viewController navigationController] pushViewController:viewController animated:YES];
} else
NSLog(#"Something wrong with the sign in procedure!");
}
- (void)viewController:(GTMOAuth2ViewControllerTouch * )viewController
finishedWithAuth:(GTMOAuth2Authentication * )auth
error:(NSError * )error
{
NSLog(#"finished");
NSLog(#"auth access token: %#", auth.accessToken);
self.calendarService.authorizer = auth;
[[self.viewController navigationController] popToViewController:self.viewController animated:NO];
if (error != nil) {
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:#"Error Authorizing with Google"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
} else {
UIAlertView * alert = [[UIAlertView alloc] initWithTitle:#"Success Authorizing with Google"
message:[error localizedDescription]
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alert show];
}
}
- (void)fetchCalendarList {
self.calendarList = nil;
self.calendarListFetchError = nil;
GTLServiceCalendar *service = self.calendarService;
GTLQueryCalendar *query = [GTLQueryCalendar queryForCalendarListList];
self.calendarListTicket = [service executeQuery:query
completionHandler:^(GTLServiceTicket *ticket,
id calendarList, NSError *error) {
// Callback
self.calendarList = calendarList;
self.calendarListFetchError = error;
self.calendarListTicket = nil;
NSLog(#"Fetched number of calendars: %#", self.calendarList);
//[self updateUI];
}];
//[self updateUI];
}
- (GTLServiceCalendar *)calendarService {
static GTLServiceCalendar *service = nil;
if (!service) {
service = [[GTLServiceCalendar alloc] init];
// Have the service object set tickets to fetch consecutive pages
// of the feed so we do not need to manually fetch them
service.shouldFetchNextPages = YES;
// Have the service object set tickets to retry temporary error conditions
// automatically
service.retryEnabled = YES;
}
return service;
}
#end
This is console output:
2014-09-10 23:57:53.603 CalendarPanel[24383:60b] GTLDriveChannel (api#channel) registration conflicts with GTLCalendarChannel
2014-09-10 23:57:53.607 CalendarPanel[24383:60b] GTLPlusDomainsAcl (plus#acl) registration conflicts with GTLPlusAcl
2014-09-10 23:57:53.607 CalendarPanel[24383:60b] GTLPlusDomainsActivity (plus#activity) registration conflicts with GTLPlusActivity
2014-09-10 23:57:53.608 CalendarPanel[24383:60b] GTLPlusDomainsActivityFeed (plus#activityFeed) registration conflicts with GTLPlusActivityFeed
2014-09-10 23:57:53.608 CalendarPanel[24383:60b] GTLPlusDomainsComment (plus#comment) registration conflicts with GTLPlusComment
2014-09-10 23:57:53.609 CalendarPanel[24383:60b] GTLPlusDomainsCommentFeed (plus#commentFeed) registration conflicts with GTLPlusCommentFeed
2014-09-10 23:57:53.609 CalendarPanel[24383:60b] GTLPlusPeopleFeed (plus#peopleFeed) registration conflicts with GTLPlusDomainsPeopleFeed
2014-09-10 23:57:53.610 CalendarPanel[24383:60b] GTLPlusPerson (plus#person) registration conflicts with GTLPlusDomainsPerson
2014-09-10 23:57:53.610 CalendarPanel[24383:60b] GTLPlusPlace (plus#place) registration conflicts with GTLPlusDomainsPlace
2014-09-10 23:57:53.613 CalendarPanel[24383:60b] GTLStorageChannel (api#channel) registration conflicts with GTLDriveChannel
2014-09-10 23:57:53.986 CalendarPanel[24383:60b] Fetched number of calendars: (null)
My two questions is:
1. How can I get rid of these conflicts?
2. Does my code look ok for what I want to do? (fetch all calendars from the user) I think using this GData classes is really tricky and there is very limited/or very outdated information out there.
Instead of installing the entire Pod try use subpods
pod 'Google-API-Client/Calendar', '~> 1.0'
or whichever you need
pod 'Google-API-Client/Drive', '~> 1.0'
I have an iPad app designed for use in a kiosk environment.
The user flow should be
Take Photo
Choose photo from iPad Album view
Share to Facebook and / or Twitter
Automatically log user out after image has been posted
I have the auto-logout of Twitter working properly, my issue is with the Facebook portion.
I have implemented the Graph API for internal testing, and would love to be able to post a complete story this way, but I don't think there is a way to log out from the Facebook app once the authorization and post is complete.
For a fallback, I can use the Feed Dialog and auto-logout from there, but as far as I can tell, there is no way to upload a local image for sharing to Facebook from there.
My Facebook Sharing code is as follows:
- (IBAction)facebookShare:(id)sender {
/// Package the image inside a dictionary
NSArray* image = #[#{#"url": self.mergeImages, #"user_generated": #"true"}];
// Create an object
id<FBGraphObject> object =
[FBGraphObject openGraphObjectForPostWithType:#"me/feed:photo"
title:#"a photo"
image:self.mergeImages
url:nil
description:nil];
// Create an action
id<FBOpenGraphAction> action = (id<FBOpenGraphAction>)[FBGraphObject graphObject];
// Set image on the action
[action setObject:image forKey:#"image"];
// Link the object to the action
[action setObject:object forKey:#"photo"];
// Hardcode the location based on Facebook Place ID
id<FBGraphPlace> place = (id<FBGraphPlace>)[FBGraphObject graphObject];
[place setId:#"279163865580772"]; // Singley + Mackie
[action setPlace:place];
// Check if the Facebook app is installed and we can present the share dialog
FBOpenGraphActionShareDialogParams *params = [[FBOpenGraphActionShareDialogParams alloc] init];
params.action = action;
params.actionType = #"me/feed:share";
// If the Facebook app is installed and we can present the share dialog
if([FBDialogs canPresentShareDialogWithOpenGraphActionParams:params]) {
// Show the share dialog
[FBDialogs presentShareDialogWithOpenGraphAction:action
actionType:#"photo_overlay:share"
previewPropertyName:#"photo"
handler:^(FBAppCall *call, NSDictionary *results, NSError *error) {
if(error) {
// An error occurred, we need to handle the error
// See: https://developers.facebook.com/docs/ios/errors
// NSLog([NSString stringWithFormat:#"Error publishing story: %#", error.description]);
} else {
// Success
NSLog(#"result %#", results);
}
}];
// If the Facebook app is NOT installed and we can't present the share dialog
} else {
// Put together the Feed dialog parameters
NSMutableDictionary *params = [NSMutableDictionary dictionaryWithObjectsAndKeys:
#"name",
#"caption",
#"description",
#"link",
#"picture",
nil];
// Show the feed dialog
[FBWebDialogs presentFeedDialogModallyWithSession:nil
parameters:params
handler:^(FBWebDialogResult result, NSURL *resultURL, NSError *error) {
if (error) {
// An error occurred, we need to handle the error
// See: https://developers.facebook.com/docs/ios/errors
// NSLog([NSString stringWithFormat:#"Error publishing story: %#", error.description]);
} else {
if (result == FBWebDialogResultDialogNotCompleted) {
// User cancelled.
NSLog(#"User cancelled.");
} else {
// Handle the publish feed callback
NSDictionary *urlParams = [self parseURLParams:[resultURL query]];
if (![urlParams valueForKey:#"post_id"]) {
// User cancelled.
NSLog(#"User cancelled.");
} else {
// User clicked the Share button
NSString *result = [NSString stringWithFormat: #"Posted story, id: %#", [urlParams valueForKey:#"post_id"]];
NSLog(#"result %#", result);
}
}
}
// Auto log the user out
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
NSLog(#"defaults fbDidLogout ........%#",defaults);
if ([defaults objectForKey:#"FBAccessTokenKey"])
{
[defaults removeObjectForKey:#"FBAccessTokenKey"];
[defaults removeObjectForKey:#"FBExpirationDateKey"];
[defaults synchronize];
}
NSHTTPCookie *cookie;
NSHTTPCookieStorage *storage = [NSHTTPCookieStorage sharedHTTPCookieStorage];
for (cookie in [storage cookies])
{
NSString* domainName = [cookie domain];
NSRange domainRange = [domainName rangeOfString:#"facebook"];
if(domainRange.length > 0)
{
[storage deleteCookie:cookie];
}
}
[FBSession.activeSession closeAndClearTokenInformation];
}];
}
}
// A function for parsing URL parameters.
- (NSDictionary*)parseURLParams:(NSString *)query {
NSArray *pairs = [query componentsSeparatedByString:#"&"];
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
for (NSString *pair in pairs) {
NSArray *kv = [pair componentsSeparatedByString:#"="];
NSString *val =
[kv[1] stringByReplacingPercentEscapesUsingEncoding:NSUTF8StringEncoding];
params[kv[0]] = val;
}
return params;
}
I have searched Stack Overflow far and wide for an answer to this, but have found no solutions.
I was finally able to figure this out! Posting the answer here to hopefully benefit others who are in the same situation.
First, add the following to your AppDelegate.m:
-(BOOL) application:(UIApplication *)application openURL:(NSURL *)url sourceApplication:(NSString *)sourceApplication annotation:(id)annotation {
return [FBAppCall handleOpenURL:url sourceApplication:sourceApplication fallbackHandler:^(FBAppCall *call) {
// Facebook SDK * App Linking *
// For simplicity, this sample will ignore the link if the session is already
// open but a more advanced app could support features like user switching.
if (call.accessTokenData) {
if ([FBSession activeSession].isOpen) {
NSLog(#"INFO: Ignoring app link because current session is open.");
}
else {
[self handleAppLink:call.accessTokenData];
}
}
}];
}
// Helper method to wrap logic for handling app links.
- (void)handleAppLink:(FBAccessTokenData *)appLinkToken {
// Initialize a new blank session instance...
FBSession *appLinkSession = [[FBSession alloc] initWithAppID:nil
permissions:nil
defaultAudience:FBSessionDefaultAudienceNone
urlSchemeSuffix:nil
tokenCacheStrategy:[FBSessionTokenCachingStrategy nullCacheInstance] ];
[FBSession setActiveSession:appLinkSession];
// ... and open it from the App Link's Token.
[appLinkSession openFromAccessTokenData:appLinkToken
completionHandler:^(FBSession *session, FBSessionState status, NSError *error) {
// Forward any errors to the FBLoginView delegate.
if (error) {
//[self.loginViewController loginView:nil handleError:error];
}
}];
}
Wherever you are calling the posting action in your app, add this line to your header file:
#property (strong, nonatomic) FBRequestConnection *requestConnection;
And the following to your implementation file:
#synthesize requestConnection;
- (IBAction)facebookShare:(id)sender {
NSArray *permissions = [[NSArray alloc] initWithObjects:
#"publish_actions", #"publish_checkins", nil];
UIImage *img = self.facebookImage;
[FBSession openActiveSessionWithPublishPermissions:permissions
defaultAudience:FBSessionDefaultAudienceEveryone allowLoginUI:YES
completionHandler:^(FBSession *session,FBSessionState s, NSError *error) {
[FBSession setActiveSession:session];
if (!error) {
// Now have the permission
[self processPostingImage:img WithMessage:#"Enter_your_message_here"];
} else {
// Facebook SDK * error handling *
// if the operation is not user cancelled
if (error.fberrorCategory != FBErrorCategoryUserCancelled) {
[self presentAlertForError:error];
}
}
}];
}
-(void)logout {
[FBSession.activeSession closeAndClearTokenInformation];
[FBSession.activeSession close];
[FBSession setActiveSession:nil];
}
- (void)processPostingImage:(UIImage *) img WithMessage:(NSString *)message {
FBRequestConnection *newConnection = [[FBRequestConnection alloc] init];
FBRequestHandler handler =
^(FBRequestConnection *connection, id result, NSError *error) {
// output the results of the request
[self requestCompleted:connection forFbID:#"me" result:result error:error];
};
FBRequest *request=[[FBRequest alloc] initWithSession:FBSession.activeSession graphPath:#"me/photos" parameters:[NSDictionary dictionaryWithObjectsAndKeys:UIImageJPEGRepresentation(img, 0.7),#"source",message,#"message",#"{'value':'EVERYONE'}",#"privacy", nil] HTTPMethod:#"POST"];
[newConnection addRequest:request completionHandler:handler];
[self.requestConnection cancel];
self.requestConnection = newConnection;
[newConnection start];
}
// FBSample logic
// Report any results. Invoked once for each request we make.
- (void)requestCompleted:(FBRequestConnection *)connection
forFbID:fbID
result:(id)result
error:(NSError *)error
{
// not the completion we were looking for...
if (self.requestConnection &&
connection != self.requestConnection)
{
return;
}
// clean this up, for posterity
self.requestConnection = nil;
if (error)
{
}
else
{
[self logout];
};
}
- (void) presentAlertForError:(NSError *)error {
// Facebook SDK * error handling *
// Error handling is an important part of providing a good user experience.
// When fberrorShouldNotifyUser is YES, a fberrorUserMessage can be
// presented as a user-ready message
if (error.fberrorShouldNotifyUser) {
// The SDK has a message for the user, surface it.
[[[UIAlertView alloc] initWithTitle:#"Something Went Wrong"
message:error.fberrorUserMessage
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil] show];
} else {
}
}
Note:
To make this work with auto-logout, you also have to disable Safari on the device. This can be done by going to Settings > General > Restrictions > Allow Safari > Off. Once that is turned off, the Facebook IBAction will popup a UIWebView inside the app itself. When tapped, the current user can enter their Facebook credentials, then the app will post the image, and log the user out so the next user can use the app without having access to the previous user's Facebook details.