I am trying to pull a user's twitter handle and populate it into a textfield in a tableview. The request completes successfully and calls the execution block, but the UI takes several seconds to update. I've tried using NSNotifications to trigger the UI change but that still had a delay in updating the UI. Below is the code I use to pull information from ACAccountStore and the only special class I am using for the table is for a custom UITableViewCell. Is anyone else seeing this? or am I missing something that's causing a runtime issue?
- (IBAction)connectTwitter:(id)sender {
UISwitch *sw = (UISwitch *)sender;
if (sw.isOn == NO) {
return;
}
ACAccountStore *account = [[ACAccountStore alloc] init];
ACAccountType *accountType = [account accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
__weak MBSuitUpViewController *weakSelf = self;
[account requestAccessToAccountsWithType:accountType options:nil completion:^(BOOL granted, NSError *error) {
if (granted == YES) {
NSArray *arrayOfAccounts = [account accountsWithAccountType:accountType];
if (arrayOfAccounts.count > 0) {
ACAccount *twitterAccount = [arrayOfAccounts lastObject];
NSURL *requestURL = [NSURL URLWithString:#"https://api.twitter.com/1/account/verify_credentials.json"];
SLRequest *user = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodGET URL:requestURL parameters:nil];
user.account = twitterAccount;
[user performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
NSDictionary *json = [NSJSONSerialization JSONObjectWithData:responseData options:kNilOptions error:nil];
[weakSelf twitterDictionaryHandler:json];
weakSelf.userInfo = #{#"name": json[#"name"], #"handle":json[#"screen_name"]};
}];
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:nil message:#"No Twitter Account Found" delegate:nil cancelButtonTitle:nil otherButtonTitles:#"Okay", nil];
[alert show];
}
} else {
NSLog(#"Permission denied %#", error.localizedDescription);
}
}];
}
- (void)twitterDictionaryHandler:(NSDictionary *)dictionary {
MBSuitUpCell *username = (MBSuitUpCell *)[self.view viewWithTag:2];
username.textField.text = dictionary[#"screen_name"];
NSLog(#"Should update textfield");
}
Does it actually call the 'twitterDictionaryHandler' function right away?
If it does that, but does not update the UI for a while then it seems to be a common issue with iOS. What you need to do is place the change of the UI into the GCD (Grand Central Dispatch) Main Queue so that it has priority and is completed ASAP. I've put an example below for anyone else who may have this issue.
dispatch_async(dispatch_get_main_queue(), ^{
username.textField.text = dictionary[#"screen_name"];
});
Related
- (void)viewDidLoad {
[super viewDidLoad];
if (NO == [SLComposeViewController isAvailableForServiceType: SLServiceTypeFacebook]) {
[self showAlert:#"There are no Facebook accounts configured. Please add or create a Facebook account in Settings."];
return;
}
[self getMyDetails];
// Do any additional setup after loading the view from its nib.
}
- (void) getMyDetails {
if (! accountStore) {
accountStore = [[ACAccountStore alloc] init];
}
if (! facebookAccountType) {
facebookAccountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
}
NSDictionary *options = #{ ACFacebookAppIdKey: [NSString stringWithFormat:#"1725978560966525"] };
[accountStore requestAccessToAccountsWithType: facebookAccountType
options: options
completion: ^(BOOL granted, NSError *error) {
if (granted)
{
NSArray *accounts = [accountStore accountsWithAccountType:facebookAccountType];
facebookAccount = [accounts lastObject];
NSURL *url = [NSURL URLWithString:#"https://graph.facebook.com/me"];
SLRequest *request = [SLRequest requestForServiceType:SLServiceTypeFacebook requestMethod:SLRequestMethodGET
URL:url
parameters:nil];
request.account = facebookAccount;
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
NSDictionary *responseDictionary = [NSJSONSerialization JSONObjectWithData:responseData
options:NSJSONReadingMutableContainers
error:nil];
NSLog(#"id: %#", responseDictionary[#"id"]);
NSLog(#"first_name: %#", responseDictionary[#"first_name"]);
NSLog(#"last_name: %#", responseDictionary[#"last_name"]);
NSLog(#"gender: %#", responseDictionary[#"gender"]);
NSLog(#"city: %#", responseDictionary[#"location"][#"name"]);
}];
} else {
[self showAlert:#"Facebook access for this app has been denied. Please edit Facebook permissions in Settings."];
}
}];
}
- (void) showAlert:(NSString*) msg {
dispatch_async(dispatch_get_main_queue(), ^(void) {
UIAlertView *alertView = [[UIAlertView alloc]
initWithTitle:#"WARNING"
message:msg
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
});
}
i tried this for getting personal information of Facebook (Logged-in User), But it require permission, as i don't know where To add permissions, i have checked in setting, tried all thing in Simulator But Still Not getting.
I recently installed the app "mapstr" and when you choose Facebook login, you don't go through the Facebbok app, you just accept the permissions through a kind of alertView :
It's written in French and it says : "mapstr" wants to access your public profile and your friends list.
When I tap OK, I am just logged with Facebook : no app switching !
How do they do that ?
(I am developping in Swift 2.0)
I'm Mapstr founder ;)
It's a loginBehavior option in Facebook LoginManager (in iOS SDK) which is the "FBSDKLoginBehaviorSystemAccount" (you also have the app option and the web option)
If a user has configure his face boo account on its iPhone, the SDK will use it, if not it will try the app, and then the web.
The only drawback is that if the system account exists but is misconfigured it failed...
Sebastien
Its using the facebook Credentials saved in your iPhone settings to login through facebook. You will need to use accounts framework for that. Below is the sample code for the same.
-(void)facebook
{
ACAccountStore *accountStore;
accountStore = [[ACAccountStore alloc]init];
ACAccountType *FBaccountType= [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
NSDictionary *dictFB = [NSDictionary dictionaryWithObjectsAndKeys:kFACEBOOK_APPID,ACFacebookAppIdKey,#[#"email"],ACFacebookPermissionsKey, nil];
[accountStore requestAccessToAccountsWithType:FBaccountType options:dictFB completion:
^(BOOL granted, NSError *e) {
if (granted)
{
[self afterPermissionGranted:accountStore accountType:FBaccountType];
}
else
{
//Fail gracefully...
NSLog(#"error getting permission %#",e);
dispatch_async(dispatch_get_main_queue(), ^{
[self openSessionWithAllowLoginUI:YES];
});
}
}];
}
-(void)afterPermissionGranted:(ACAccountStore *)accountStore accountType:(ACAccountType *)FBaccountType{
NSArray *accounts = [accountStore accountsWithAccountType:FBaccountType];
//it will always be the last object with single sign on
if ([accounts count] == 0) {
UIAlertView *alert = [[UIAlertView alloc]initWithTitle:#"" message:#"No Other Facebook Account Found" delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
}
else{
ACAccount *facebookAccount;
facebookAccount = [accounts lastObject];
ACAccountCredential *facebookCredential = [facebookAccount credential];
NSString *accessToken = [facebookCredential oauthToken];
NSLog(#"FAT: %#", accessToken);
NSURL *requestURL = [NSURL URLWithString:#"https://graph.facebook.com/me"];
SLRequest *request = [SLRequest requestForServiceType:SLServiceTypeFacebook requestMethod:SLRequestMethodGET URL:requestURL parameters:nil];
request.account = facebookAccount;
[request performRequestWithHandler:^(NSData *data, NSHTTPURLResponse *response, NSError *error) {
if(!error)
{
NSDictionary *list =[NSJSONSerialization JSONObjectWithData:data options:kNilOptions error:&error];
NSDictionary *errorPart = [list objectForKey:#"error"];
NSString *errorsubCode =[NSString stringWithFormat:#"%#",[errorPart valueForKey:#"error_subcode"]];
if (![errorsubCode isEqualToString:#"(null)"]) {
dispatch_async(dispatch_get_main_queue(), ^{
[self openSessionWithAllowLoginUI:YES];
});
}
else{
NSString *globalmailID = [NSString stringWithFormat:#"%#",[list objectForKey:#"email"]];
NSLog(#"global mail : %#",globalmailID);
NSString *fbname = [NSString stringWithFormat:#"%#",[list objectForKey:#"name"]];
NSLog(#"fname %#",fbname);
ACAccountCredential *fbCredential = [facebookAccount credential];
NSString *accessToken = [fbCredential oauthToken];
[self saveDataFromFacebook:error user:list accessToken:(NSString *)accessToken];
}
}
else
{
//handle error gracefully
NSLog(#"error from get%#",error);
//attempt to revalidate credentials
}
dispatch_async(dispatch_get_main_queue(), ^{
[self endProgressBar];
});
}];
}
}
Please remove the extra code. Feel free to ask any queries.
I am new to the concept of POSTing to twitter. I've looked here in order to understand how GET something from twitter. But I can't find any example about how to POST something to twitter.
how do I tell twitter that userXYZ is posting a tweet? How about the text body of the tweet itself.
Again, I'm very new to the concept.
You can easily find those in API Documentation. I think you are looking for this.
Also there are some question already asked in SO. You can also check this.
Using Twitter SDK import #import<Twitter/Twitter.h> in your .h or .m file
if ([TWTweetComposeViewController canSendTweet])
{
NSURL* aURL = [NSURL URLWithString:#"your image Url"];
NSData* data = [[NSData alloc] initWithContentsOfURL:aURL];
UIImage *img1 = [UIImage imageWithData:data];
// Initialize Tweet Compose View Controller
TWTweetComposeViewController *vc = [[TWTweetComposeViewController alloc] init];
// Settin The Initial Text
[vc setInitialText:#"Setting The Initial Text"];
[vc addImage:img1];
// Adding a URL
strWEBTwitter = #"Pass Your URL here"
[vc addURL:[NSURL URLWithString:strWEBTwitter]];
// Setting a Completing Handler
[vc setCompletionHandler:^(TWTweetComposeViewControllerResult result)
{
[self dismissViewControllerAnimated:YES completion:nil];
NSLog(#"Result : %d", result);
if (result == TWTweetComposeViewControllerResultDone)
{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Message" message:#"Thanks for sharing." delegate:self cancelButtonTitle:#"OK" otherButtonTitles:nil]; [alertView show];
}
else if (result == TWTweetComposeViewControllerResultCancelled)
{
NSLog(#"Cancle")
}
}];
// Display Tweet Compose View Controller Modally
[self presentViewController:vc animated:YES completion:nil];
} else
{
// Show Alert View When The Application Cannot Send Tweets
NSString *message = #"There are no Twitter accounts configured. You can add or create a Twitter account in Settings.";
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"No Twitter Accounts" message:message delegate:self cancelButtonTitle:#"OK" otherButtonTitles:#"Cancel",nil];
[alertView show];
}
Thanks Rashad. This is what I am using now. It works.
- (void)testPost {
ACAccountStore *accountStore = [[ACAccountStore alloc] init];
ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[accountStore requestAccessToAccountsWithType:accountType options:nil completion:^(BOOL granted, NSError *error)
{
if (granted)
{
NSArray *accounts = [accountStore accountsWithAccountType:accountType];
if (accounts.count)
{
NSMutableDictionary *dict = [[NSMutableDictionary alloc] init];
[dict setValue:#"check Check CHECK" forKey:#"status"];
NSString *retweetString = [NSString stringWithFormat:#"https://api.twitter.com/1.1/statuses/update.json"];
NSURL *retweetURL = [NSURL URLWithString:retweetString];
SLRequest *request = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodPOST URL:retweetURL parameters:dict];
request.account = [accounts objectAtIndex:0];
[request performRequestWithHandler:^(NSData *responseData1, NSHTTPURLResponse *urlResponse, NSError *error)
{
if (responseData1)
{
NSError *error1 = nil;
id response = [NSJSONSerialization JSONObjectWithData:responseData1 options:NSJSONReadingMutableLeaves error:&error1];
NSLog(#"%#", response);
}
}];
}
}
}];
}
I have application with upload photo like in Instagram app. First my app uploads photo and returns link. Then sends link in status message. When I'm testing on my iPhone everything is ok, but when I'm testing on another iPhone then twitter return code 215 bad authentication data. Any ideas what could be wrong? Thx for reply.
- (void)tweet:(NSString *)identifier withCompletionHandler:(void (^)())completionHandler {
ACAccountStore *accountStore = [[ACAccountStore alloc] init];
// Create an account type that ensures Twitter accounts are retrieved.
ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
// Request access from the user to use their Twitter accounts.
[accountStore requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
if(granted) {
if (identifier) {
// Get the list of Twitter accounts.
NSArray *accountsArray = [accountStore accountsWithAccountType:accountType];
if ([accountsArray count] > 0) {
// Grab the initial Twitter account to tweet from.
NSURL *url = [NSURL URLWithString:#"https://api.twitter.com/1.1/statuses/update.json"];
NSDictionary *dict = [NSDictionary dictionaryWithObject:[NSString stringWithFormat:#"Check out this great flick on BLA BLA %#", [BLA_URL stringByAppendingFormat:#"/%#/%#", #"flick", identifier]]
forKey:#"status"];
SLRequest *request = [SLRequest requestForServiceType:SLServiceTypeTwitter
requestMethod:SLRequestMethodPOST
URL:url
parameters:dict];
[request setAccount:[accountsArray lastObject]];
[request performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if ([urlResponse statusCode] == 200) {
NSLog(#"TWEEET!");
}
else {
NSError *jsonError;
id data = [NSJSONSerialization JSONObjectWithData:responseData
options:NSJSONReadingMutableLeaves
error:&jsonError];
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Twitter response"
message:[NSString stringWithFormat:#"%d S: %d %#", [error code], [urlResponse statusCode], data]
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
NSLog(#"NOOO TWEEET!");
[alertView show];
completionHandler();
});
}
}];
...
And Pre-checking function:
- (IBAction)twitterButtonTapped:(id)sender {
if ([self.twitterButton isSelected]) {
[self.twitterButton setSelected:NO];
}
else {
[self.twitterButton setSelected:YES];
ACAccountStore *accountStore = [[ACAccountStore alloc] init];
ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[accountStore requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
if (!granted) {
[self.twitterButton setSelected:NO];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Twitter access"
message:#"Please make sure you have allowed twitter for BLA BLA in your Settings."
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil];
[alertView performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:NO];
}
}];
}
}
Ok, I've got the answer! Problem was that other iPhone has incomplete account data - meanings you have to has filled username and password in your Settings app.
I am attempting to post to Twitter without user interaction (as this would force the user to hit 'Send' multiple times.).
The following is my code:
- (void) postToTwitterUsingTWRequest: (NSDictionary*) appDictionary {
NSString *trackName = [appDictionary objectForKey:#"trackName"];
NSString *trackId = [[appDictionary objectForKey:#"trackId"] description];
NSString *artworkUrl512 = [appDictionary objectForKey:#"artworkUrl512"];
NSMutableString *requestUrlString = [NSMutableString new];
[requestUrlString appendFormat:#"http://itunes.apple.com/%#",[[NSLocale currentLocale] objectForKey:NSLocaleCountryCode]];
[requestUrlString appendFormat:#"/app/%#", trackName];
[requestUrlString appendFormat:#"/id%#?mt=8", trackId];
ACAccountStore *account = [[ACAccountStore alloc] init];
ACAccountType *accountType = [account accountTypeWithAccountTypeIdentifier:
ACAccountTypeIdentifierTwitter];
[account requestAccessToAccountsWithType:accountType options:nil completion:^(BOOL granted, NSError *error) {
if (granted == YES) {
NSArray *arrayOfAccounts = [account
accountsWithAccountType:accountType];
if ([arrayOfAccounts count] > 0)
{
ACAccount *twitterAccount = [arrayOfAccounts lastObject];
TWRequest *postRequest = [[TWRequest alloc] initWithURL:[NSURL URLWithString:#"https://upload.twitter.com/1/statuses/update_with_media.json"] parameters:nil requestMethod:TWRequestMethodPOST];
//NSData *tempData = [NSData dataWithContentsOfURL:[NSURL URLWithString: #"http://eborkdev.com/wp-content/uploads/2012/05/logo.png"]];
NSData *tempData = [NSData dataWithContentsOfURL:[NSURL URLWithString: artworkUrl512]];
[postRequest addMultiPartData:tempData withName:#"media" type:#"image/png"];
tempData = [[NSString stringWithFormat:#"%# was recommended using Tell A Friend (http://link_to_tell_a_friend.com). \n %#", trackName, requestUrlString] dataUsingEncoding:NSUTF8StringEncoding];
[postRequest addMultiPartData:tempData withName:#"status" type:#"text/plain"];
[postRequest setAccount:twitterAccount];
isPostingToTwitter = true;
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
isPostingToTwitter = false;
NSLog(#"Twitter HTTP response: %i", [urlResponse statusCode]);
}];
}
else {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error"
message:#"No Twitter accounts found. Please ensure that there are accounts present, and try again."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
}
}
}];
}
I am looping through this in order to make the multiple calls like so:
for (NSDictionary* appDictionary in selectedApps) {
[self postToTwitterUsingTWRequest:appDictionary];
}
Sometimes it allows me to send one giving me the 200 statusCode. But when sending multiple, I get 403 and 200, or just 403.
How can I fix this?
You should read the following links before proceeding, the thing you are trying to do is called spamming..
https://dev.twitter.com/docs/error-codes-responses
https://support.twitter.com/articles/15364-about-twitter-limits-update-api-dm-and-following
TRY THIS
AT .H
#import <Twitter/Twitter.h>
#import <Accounts/Accounts.h>
- (void)sendTweet
{
Class tweeterClass = NSClassFromString(#"TWTweetComposeViewController");
if(tweeterClass != nil) { // check for Twitter integration
if ([TWTweetComposeViewController canSendTweet])
{
// Create account store, followed by a twitter account identifier
// At this point, twitter is the only account type available
ACAccountStore *account = [[ACAccountStore alloc] init];
ACAccountType *accountType = [account accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
// Request access from the user to access their Twitter account
[account requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error)
{
// Did user allow us access?
if (granted == YES)
{
// Populate array with all available Twitter accounts
NSArray *arrayOfAccounts = [account accountsWithAccountType:accountType];
// Sanity check
if ([arrayOfAccounts count] > 0)
{
// Keep it simple, use the first account available
ACAccount *acct = [arrayOfAccounts objectAtIndex:0];
// Build a twitter request
TWRequest *postRequest = [[TWRequest alloc] initWithURL:
[NSURL URLWithString:#"http://api.twitter.com/1/statuses/update.json"]
parameters:[NSDictionary dictionaryWithObject:str_tweet
forKey:#"status"] requestMethod:TWRequestMethodPOST];
// Post the request
[postRequest setAccount:acct];
// Block handler to manage the response
[postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error)
{
// NSString *response = [[NSString alloc] initWithData:responseData encoding:NSUTF8StringEncoding];
// NSLog(#"Twitter response: %#, HTTP response: %#", response, [urlResponse statusCode]);
}];
}
}
}];
}
else
{
NSLog(#"Unable to tweet!");
}
}
}