I'm having trouble coming up with a solution to the following from the Simple Login iOS quick start:
Since it is possible for a device to have more than one Twitter account attached, you will need to provide a block which can be used to determine which account to log in. Replace "[yourApp selectUserName:usernames]" below with your own code to choose from the list of usernames.
This is the code that is provided:
[authClient loginToTwitterAppWithId:#"YOUR_CONSUMER_KEY"
multipleAccountsHandler:^int(NSArray *usernames) {
// If you do not wish to authenticate with any of these usernames, return NSNotFound.
return [yourApp selectUserName:usernames];
} withCompletionBlock:^(NSError *error, FAUser *user) {
if (error != nil) {
// There was an error authenticating
} else {
// We have an authenticated Twitter user
}
}];
Would a UIActionSheet that allows the user to pick which account to use be best? How would that be done?
#import <Twitter/Twitter.h>
#import <Accounts/Accounts.h>
For login:
ACAccountStore *accountStore = [[ACAccountStore alloc] init];
// Create an account type that ensures Twitter accounts are retrieved.
ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[accountStore requestAccessToAccountsWithType:accountType options:nil completion:^(BOOL granted, NSError *error) {
// Request access from the user to use their Twitter accounts.
// [accountStore requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
//NSLog(#"%#",error);
if(granted) {
// Get the list of Twitter accounts.
NSArray *accountsArray = [accountStore accountsWithAccountType:accountType];
//NSLog(#"%#",accountsArray);
twitterAccountsArray=[accountsArray mutableCopy];
if ([twitterAccountsArray count] > 0){
sheet = [[UIActionSheet alloc] initWithTitle:#"Choose an Account" delegate:self cancelButtonTitle:nil destructiveButtonTitle:nil otherButtonTitles:nil];
for (ACAccount *acct in twitterAccountsArray) {
[sheet addButtonWithTitle:acct.username];
}
}
}
else{
dispatch_async(dispatch_get_main_queue(), ^{
//NSLog(#"%#",error);
if (![error.localizedDescription isEqual:[NSNull null]] &&[error.localizedDescription isEqualToString:#"No access plugin was found that supports the account type com.apple.twitter"]) {
}
else
[Utility showAlertWithString:#"We could not find any Twitter account on the device"];
});
}
}];
It will open an action sheet which will show you the list of accounts your device has.
For posting :
There must be at least one valid twitter account added on your iPhone device.
if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter])
{
NSString *initialText=#"Text to be posted";
SLComposeViewController *tweetSheet = [SLComposeViewController composeViewControllerForServiceType:SLServiceTypeTwitter];
tweetSheet.completionHandler = ^(SLComposeViewControllerResult result) {
switch(result) {
// This means the user cancelled without sending the Tweet
case SLComposeViewControllerResultCancelled:{
}
break;
case SLComposeViewControllerResultDone:{
}
break;
}
};
[tweetSheet setInitialText:initialText];
if (initialText) {
[self presentViewController:tweetSheet animated:YES completion:nil];
}
}
I've found a nice example of how to do this with Awesome Chat's code. It uses a UIActionSheet like I was attempting to.
//-------------------------------------------------------------------------------------------------------------------------------------------------
- (IBAction)actionTwitter:(id)sender
//-------------------------------------------------------------------------------------------------------------------------------------------------
{
[ProgressHUD show:#"In progress..." Interaction:NO];
//---------------------------------------------------------------------------------------------------------------------------------------------
selected = 0;
//---------------------------------------------------------------------------------------------------------------------------------------------
ACAccountStore *account = [[ACAccountStore alloc] init];
ACAccountType *accountType = [account accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
//---------------------------------------------------------------------------------------------------------------------------------------------
[account requestAccessToAccountsWithType:accountType options:nil completion:^(BOOL granted, NSError *error)
{
if (granted)
{
accounts = [account accountsWithAccountType:accountType];
//-------------------------------------------------------------------------------------------------------------------------------------
if ([accounts count] == 0)
[self performSelectorOnMainThread:#selector(showError:) withObject:#"No Twitter account was found" waitUntilDone:NO];
//-------------------------------------------------------------------------------------------------------------------------------------
if ([accounts count] == 1) [self performSelectorOnMainThread:#selector(loginTwitter) withObject:nil waitUntilDone:NO];
if ([accounts count] >= 2) [self performSelectorOnMainThread:#selector(selectTwitter) withObject:nil waitUntilDone:NO];
}
else [self performSelectorOnMainThread:#selector(showError:) withObject:#"Access to Twitter account was not granted" waitUntilDone:NO];
}];
}
//-------------------------------------------------------------------------------------------------------------------------------------------------
- (void)selectTwitter
//-------------------------------------------------------------------------------------------------------------------------------------------------
{
UIActionSheet *action = [[UIActionSheet alloc] initWithTitle:#"Choose Twitter account" delegate:self cancelButtonTitle:nil
destructiveButtonTitle:nil otherButtonTitles:nil];
//---------------------------------------------------------------------------------------------------------------------------------------------
for (NSInteger i=0; i<[accounts count]; i++)
{
ACAccount *account = [accounts objectAtIndex:i];
[action addButtonWithTitle:account.username];
}
//---------------------------------------------------------------------------------------------------------------------------------------------
[action addButtonWithTitle:#"Cancel"];
action.cancelButtonIndex = accounts.count;
[action showInView:self.view];
}
//-------------------------------------------------------------------------------------------------------------------------------------------------
- (void)actionSheet:(UIActionSheet *)actionSheet clickedButtonAtIndex:(NSInteger)buttonIndex
//-------------------------------------------------------------------------------------------------------------------------------------------------
{
if (buttonIndex != actionSheet.cancelButtonIndex)
{
selected = buttonIndex;
[self loginTwitter];
}
else [ProgressHUD dismiss];
}
//-------------------------------------------------------------------------------------------------------------------------------------------------
- (void)loginTwitter
//-------------------------------------------------------------------------------------------------------------------------------------------------
{
Firebase *ref = [[Firebase alloc] initWithUrl:FIREBASE];
FirebaseSimpleLogin *authClient = [[FirebaseSimpleLogin alloc] initWithRef:ref];
//---------------------------------------------------------------------------------------------------------------------------------------------
[authClient loginToTwitterAppWithId:TWITTER_KEY multipleAccountsHandler:^int(NSArray *usernames)
{
return (int) selected;
}
withCompletionBlock:^(NSError *error, FAUser *user)
{
if (error == nil)
{
if (user != nil) [delegate didFinishLogin:ParseUserData(user.thirdPartyUserData)];
[self dismissViewControllerAnimated:YES completion:^{ [ProgressHUD dismiss]; }];
}
else
{
NSString *message = [error.userInfo valueForKey:#"NSLocalizedDescription"];
[self performSelectorOnMainThread:#selector(showError:) withObject:message waitUntilDone:NO];
}
}];
}
Related
I'm calling up a session in my LoginViewController as seen below. The problem is that, after it authenticates and closes the popupview... the session returns nil. So it initially reaches out to the api and starts a session (the first log records nil as the session is open and the second log records "Signed in as..." with the username) then when it closes the login view it records nil again. Not exactly use what I could be doing wrong...
Could it be because I'm using TWTRSession* session to check the session? Could that be logging me off? Or is it related to me closing the popupview?
I do essentially the same thing with my facebook login but the facebook session stays open until I logout...
- (void)viewDidLoad
{
[super viewDidLoad];
self.email.delegate = self;
self.pass.delegate = self;
//self.view.frame = CGRectMake(self.bounds.size.width, self.bounds.size.height);
[Fabric with:#[[Twitter sharedInstance]]];
TWTRSession* session;
if (session) {
NSLog(#"Early bird %#", [session userName]);
[self closePopup];
}
else {
NSLog(#"No dice");
}
/*
TWTRLogInButton *logInButton = [TWTRLogInButton buttonWithLogInCompletion:^(TWTRSession *session, NSError *error) {
// play with Twitter session
}];
logInButton.center = self.view.center;
[self.view addSubview:logInButton];
*/
TWTRLogInButton* logInButton = [TWTRLogInButton
buttonWithLogInCompletion:
^(TWTRSession* session, NSError* error) {
if (session) {
NSLog(#"signed in as %#", [session userName]);
[self closePopup];
} else {
NSLog(#"error: %#", [error localizedDescription]);
}
}];
I decided to include my actionsheet as well so you could see how I'm handling the login/logout situation:
- (void)sheet: (id) sender {
NSString *email = [[NSUserDefaults standardUserDefaults] objectForKey:#"email"];
UIActionSheet *sheet;
TWTRSession *session;
//if ([FBSDKAccessToken currentAccessToken]) {
if (email == nil && [FBSDKAccessToken currentAccessToken] == nil && session == nil) {
sheet = [[UIActionSheet alloc] initWithTitle:#"Profile"
delegate:nil // Can be another value but will be overridden when showing with handler.
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:#"Login"
otherButtonTitles:nil];
}
else
{
sheet = [[UIActionSheet alloc] initWithTitle:#"Profile"
delegate:nil // Can be another value but will be overridden when showing with handler.
cancelButtonTitle:#"Cancel"
destructiveButtonTitle:#"Logout"
otherButtonTitles:#"User page", nil];
}
[sheet showInView:self.view
handler:^(UIActionSheet *actionSheet, NSInteger buttonIndex) {
if (buttonIndex == [actionSheet cancelButtonIndex]) {
NSLog(#"Cancel button index tapped");
} else if (buttonIndex == [actionSheet destructiveButtonIndex]) {
if (email == nil && [FBSDKAccessToken currentAccessToken] == nil && session == nil) {
LoginViewController *loginView = [[LoginViewController alloc] initWithNibName:#"LoginView" bundle:[NSBundle mainBundle]];
//[self.view modalViewController:loginView animated:YES];
//[self presentModalViewController:loginView animated:YES];
//[self.navigationController pushViewController:loginView animated:YES];
loginView.delegate = (id) self;
[self presentPopupViewController:loginView animationType:MJPopupViewAnimationSlideTopBottom];
NSLog(#"Login View");
}
else
{
[[NSUserDefaults standardUserDefaults] setObject:nil forKey:#"email"];
FBSDKLoginManager *logout = [[FBSDKLoginManager alloc] init];
[logout logOut];
}
} else {
NSLog(#"Button %i tapped", buttonIndex);
if (buttonIndex == 1) {
UserViewController * userView = [[UserViewController alloc] initWithNibName:#"UserView" bundle:[NSBundle mainBundle]];
[userView setUEmail:email];
[self presentViewController:userView animated:YES completion:nil];
}
}
}];
}
So I ended up creating my own custom IBAction to handle twitter logins. This works much better. Basically you just have to be careful with how you check if a session is active:
-(IBAction)twlogin:(id)sender
{
[Fabric with:#[[Twitter sharedInstance]]];
[[Twitter sharedInstance] logInWithCompletion:^
(TWTRSession *session, NSError *error) {
if (session) {
NSLog(#"signed in as %#", [session userName]);
TWTRShareEmailViewController* shareEmailViewController =
[[TWTRShareEmailViewController alloc]
initWithCompletion:^(NSString* email2, NSError* error) {
NSLog(#"Email %#, Error: %#", email2, error);
}];
[self presentViewController:shareEmailViewController
animated:YES
completion:nil];
[self closePopup];
NSLog(#"session check %#", [session userName]);
} else {
NSLog(#"error: %#", [error localizedDescription]);
}
}];
[[Twitter sharedInstance] logOut];
}
As you can see TWTR *session has to be included with Twitter shared instance like so:
[[Twitter sharedInstance] logInWithCompletion:^
(TWTRSession *session, NSError *error) {
I didn't do that before, and that's why it wasn't working. Hopefully this helps someone. (now if someone would help me getting Twitter emails to work... that would be greatly appreciated...)
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 using SLComposeViewController to share the images on twitter. If i am not logged-in via iPhone settings than i want to show alert view. Please any body will suggest what to do.
Use this code:
if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter])
//show compose view
else
UIAlertView *message = [[UIAlertView alloc] initWithTitle:#"Not available"
message:#"Twitter is not available."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[message show];
Using ACAccountStore and ACAccount you can Check weather you have login or not.
Refer the sample code here: How to post on twitter by hiding SLComposeViewController *tweetSheettweet instance
Declare in .h file
#property (strong, nonatomic) ACAccount *fbAccount;
#property (strong, nonatomic) NSString *slService;
#property (strong, nonatomic) ACAccountStore *accountStore;
Add in .m file
- (IBAction)TwittButtonPressed:(id)sender
{
if(!accountStore)
accountStore = [[ACAccountStore alloc] init];
ACAccountType *twitterAccountType = [self.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
// Request access to the Twitter account with the access info
[self.accountStore requestAccessToAccountsWithType:twitterAccountType options:nil completion:^(BOOL granted, NSError *error) {
if (granted) {
// If access granted, then get the Twitter account info
NSArray *accounts = [self.accountStore accountsWithAccountType:twitterAccountType];
if (accounts.count>0) {
self.fbAccount=[accounts lastObject];
//NSLog(#"Twitter account Details =%#",self.fbAccount);
self.fbAccount=[accounts objectAtIndex:0];
NSString *username1 = self.fbAccount.username;
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
[defaults setValue:username1 forKey:#"twitUserName"];
[defaults synchronize];
//NSLog(#"USer ID %#and Username%#",[defaults valueForKey:#"twitUserID"],[defaults valueForKey:#"twitUserName"]);
self.slService = SLServiceTypeTwitter;
// NSLog(#"Show indicatoer############");
[self performSelectorOnMainThread:#selector(sharingPostData:) withObject:_slService waitUntilDone:YES];
}
else{
// NSLog(#"Access not granted");
[self performSelectorOnMainThread:#selector(throwAlertWithTitle:) withObject:#"Account not found. Please setup your account in settings" waitUntilDone:NO];
}
// [self sharingPostData:self.slService];
} else {
// NSLog(#"Access not granted");
[self performSelectorOnMainThread:#selector(throwAlertWithTitle:) withObject:#"Account not found. Please setup your account in settings" waitUntilDone:NO];
}
}];
}
-(void)throwAlertWithTitle:(NSString *)message{
//remove DSActivity if any
UIAlertView *errorAlert = [[UIAlertView alloc] initWithTitle:#"Notification" message:message delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[errorAlert show];
errorAlert=nil;
}
-(void)sharingPostData:(NSString *)serviceType{
if ([SLComposeViewController isAvailableForServiceType:serviceType])
{
SLComposeViewController *fvc = [SLComposeViewController composeViewControllerForServiceType:serviceType];
[fvc setInitialText:#"Share text here"];
[fvc addImage:backGroundImage.image];
[fvc addURL:[NSURL URLWithString:kAppituneslink]];
[self presentViewController:fvc animated:YES completion:nil];
}
}
In Twitter ,if User has Logged In in the Twitter Account in Settings Screen It will allow to post.Or Else it will display a Alert as "No Twitter Accounts" with 2 Options "Settings" and "Cancel". If Cancel is Tapped it will close alert and reject post to twitter. And if Settings is Tapped it is not redirecting to Settings Screen.
Also i used
[[UIApplication sharedApplication] openURL:[NSURL URLWithString:#"prefs:root=TWITTER"]];
But No Luck. As far as i checked all are saying as from iOS 5.1 it wont work.But i see some apps still redirecting to settings screen in iOS7. Is it possible to redirect in iOS7.
Thanks in Advance.
You can use below code in your login button's action:
if ([TWTweetComposeViewController canSendTweet])
{
//yes user is logged in
accountStore = [[ACAccountStore alloc] init];
ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
// Request access from the user to use their Twitter accounts.
[accountStore requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error)
{
// Did user allow us access?
if (granted == YES)
{
// Populate array with all available Twitter accounts
NSArray *arrayOfAccounts = [accountStore accountsWithAccountType:accountType];
ACAccount *acct = [arrayOfAccounts objectAtIndex:0];
// Set the cell text to the username of the twitter account
NSString *userID = [[acct valueForKey:#"properties"] valueForKey:#"user_id"];
TwitterIdStr = [[NSString alloc] initWithString:userID];
FbIdStr = [[NSString alloc] init];
NSLog(#"%#",userID);
NSString *networkCheck = [[NSUserDefaults standardUserDefaults] valueForKey:#"isNetWorkAvailable"];
if ([networkCheck isEqualToString:#"NotConnected"])
{
// not connected
dispatch_async(dispatch_get_main_queue(), ^{
// Display/dismiss your alert
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"No network connection" message:#"You must be connected to the internet to proceed." delegate:nil cancelButtonTitle:#"OK"otherButtonTitles:nil];
[alert show];
});
} else
{
fNameStr = [[NSString alloc] init];
lNameStr = [[NSString alloc] init];
emailStr = [[NSString alloc] init];
[self startProgressViewAgain];
}
}
}];
}
else
{
//show tweeet login prompt to user to login
TWTweetComposeViewController *viewController = [[TWTweetComposeViewController alloc] init];
//hide the tweet screen
viewController.view.hidden = YES;
//fire tweetComposeView to show "No Twitter Accounts" alert view on iOS5.1
viewController.completionHandler = ^(TWTweetComposeViewControllerResult result) {
if (result == TWTweetComposeViewControllerResultCancelled) {
[self dismissModalViewControllerAnimated:NO];
}
};
[self presentModalViewController:viewController animated:NO];
//hide the keyboard
[viewController.view endEditing:YES];
}
I want recent 20 statuses of screen_name after successful authentication.
I have done authentication successfully.
Here is my code here for retrieving tweets:
- (void)listResults
{
NSLog(#"list");
dispatch_async(GCDBackgroundThread, ^{
#autoreleasepool {
[UIApplication sharedApplication].networkActivityIndicatorVisible = YES;
dict = [[FHSTwitterEngine sharedEngine]searchTweetsWithQuery:#"#uknationofblue" count:30 resultType:FHSTwitterEngineResultTypeRecent unil:nil sinceID:nil maxID:nil];
NSLog(#"%#",dict);
NSArray *results = [dict objectForKey:#"statuses"];
NSLog(#"array text = %#",results);
for (NSDictionary *item in results)
{
NSLog(#"text == %#",[item valueForKey:#"text"]);
NSLog(#"name == %#",[[item objectForKey:#"user"]objectForKey:#"name"]);
NSLog(#"screen name == %#",[[item objectForKey:#"user"]objectForKey:#"screen_name"]);
NSLog(#"pic == %#",[[item objectForKey:#"user"]objectForKey:#"profile_image_url_https"]);
}
dispatch_sync(GCDMainThread, ^{
#autoreleasepool {
UIAlertView *av = [[UIAlertView alloc]initWithTitle:#"Complete!" message:#"Your list of followers has been fetched" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[av show];
[UIApplication sharedApplication].networkActivityIndicatorVisible = NO;
}
});
}
});
}