Not sure if any one has tried this. I want to access twitter with an account that the user of the phone doesn't have access to.
The way I have looked at it is by adding a twitter account using the OAth (token/secret)
I can add an account using the following code.
NSString *token = #"blahblahblah";
NSString *secret = #"blahblahblah";
ACAccountStore *store = [[ACAccountStore alloc] init];
ACAccountCredential *credential = [[ACAccountCredential alloc] initWithOAuthToken:token tokenSecret:secret];
ACAccountType *twitterAcctType = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
ACAccount *newAccount = [[ACAccount alloc] initWithAccountType:twitterAcctType];
newAccount.credential = credential;
[store saveAccount:<#(ACAccount *)#> withCompletionHandler:<#^(BOOL success, NSError *error)completionHandler#>
[store saveAccount:newAccount withCompletionHandler:^(BOOL success, NSError *error) {
if (success) {
NSLog(#"Account was saved!");
} else {
//something went wrong, check value of error
}
}];
But this code stores the user id associataed with the secret in the iphones account store. The problem being that if the phone user then uses twitter the account privilages are still there for him to use...
There doesn't seem to be a store deleteAccount method implemented
For my second attempt i tried using just the user that was created without adding it to the account store but I think the SaveAccount method is what verifies the token and secret and gets the user name...
Any tips???
Just a friendly reminder that this will most likely have your app banned from the app store, as this can easily be classified as spying on the user/device, if you can pull it off which I doubt.
Also, beware of having thousands/millions of users twitting from the same account.
Finally, there does not seem to be a way to remove the account, as you said, plus in order to use it you'd need to use requestAccessToAccountsWithType:withCompletionHandler: which would trigger a notice to the user and would make them think you were requesting access to his tweeter account, rather than yours...
Related
I'm trying to use iOS Social.framework to share to Facebook. However, most users will not have Facebook setup in Settings>Facebook so therefore Facebook doesn't show up when you bring up a UIActivityViewController, it just isn't listed. (See UIActivity with no settings for Facebook or just google "facebook doesn't show in UIActivityViewController," there's plenty of people trying to solve this problem.)
Our app already uses the Facebook SDK so I can get at the Facebook account info, so I thought I would just get the info and then save the info in Settings>Facebook. Here's my code. (Which I got from When do we use saveAccount:(ACAccount *)account of ACAccountStore class? and iOS requestAccessToAccountsWithType is Not Showing Permission Prompt / NSAlert)
- (void)storeAccountWithAccessToken:(NSString *)token secret:(NSString *)secret {
if (self.accountStore == nil) {
self.accountStore = [[ACAccountStore alloc] init];
}
//We start creating an account by creating the credentials object
ACAccountCredential *credential = [[ACAccountCredential alloc] initWithOAuthToken:token tokenSecret:secret];
ACAccountType *acctType =[self.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
ACAccount *newAccount = [[ACAccount alloc] initWithAccountType:acctType];
//Here we assign credentials and now can save account
newAccount.credential = credential;
NSDictionary *options = [[NSDictionary alloc] initWithObjectsAndKeys:
[VVEnvironment stringForKey:kVVEnvironmentKeyFacebookAppID], (NSString *)ACFacebookAppIdKey,
[NSArray arrayWithObject:#"basic_info"], (NSString *)ACFacebookPermissionsKey,
ACFacebookAudienceEveryone, (NSString *)ACFacebookAudienceKey,
nil];
[_accountStore requestAccessToAccountsWithType:acctType options:options completion:^(BOOL granted, NSError *error) {
if (granted == YES) {
[self.accountStore saveAccount:newAccount withCompletionHandler:^(BOOL success, NSError *error) {
if (success) {
NSLog(#"the account was saved!");
}
else {
if ([error code] == ACErrorPermissionDenied) {
NSLog(#"Got a ACErrorPermissionDenied, the account was not saved!");
}
NSLog(#"the account was not saved! %d, %#", [error code], error);
}
}];
}
else {
NSLog(#"ERR: %# ",error);
}
}];
}
I added the call to requestAccessToAccountsWithType there because without I was getting a ACErrorPermissionDenied back from saveAccount. When I run this I do see my app slide briefly aside for the Facebook dialog (if I delete my app on Facebook's website it does bring up the full dialog asking me for permissions which I grant.)
The problem is that granted is always NO, so I never get a chance to save the account. What am I missing here?
I am running this on a real device that has the Facebook App installed and setup.
I'd appreciate any help on this, thanks!
There is no solution available for this. iOS native facebook applications are not allowed to write or save account to ACAccountStore. It only allow to access account from the ACAccountStore. iOS only allow to store 1 facebook account at a time. Try to do these steps you will understand this easily,
Add a facebook account to settings App and try to get permission to access it. It will grand permission.
Remove account and try to get the permission it will deny access.
Add a facebook account and get access and try to save new account to ACAccountStore, It will say that Native applications are not allowed to save account to ACAccountStore.
The proposed solution is, Add the account to settings App and login with it and Share using Social framework or presentOSIntegratedDialog.
After logging into Twitter, I am able to print out some useful data such as the username and user ID. However, the OAuth token is always null. How do I get it? I need to send OAuth token to my server so it can verify that the user is indeed who he says he is.
ACAccountStore* accountStore = [[ACAccountStore alloc] init];
ACAccountType* twitterType = [self.context.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[accountStore
requestAccessToAccountsWithType:twitterType
withCompletionHandler:^(BOOL isAllowed, NSError* error) {
dispatch_sync(dispatch_get_main_queue(), ^(void) {
if (isAllowed) {
ACAccount* account = [[self.context.accountStore accountsWithAccountType:[self.context.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter]] lastObject];
NSLog(#"username = %#", account.username);
NSLog(#"user_id = %#", [[account valueForKey:#"properties"] valueForKey:#"user_id"]);
// ouath token is always null
NSLog(#"oauth token = %#", account.credential.oauthToken);
}
});
}
];
I "think" I need Reverse Auth, but that tutorial mysteriously left out the code for "step 1".
You will indeed need to use Reverse Auth.
I recently used Sean Cook's TWReverseAuth and it worked very well. Just be careful to turn off ARC for the individual files in the Vendor directory.
Please clarify me when do we use this method ?
- (void)saveAccount:(ACAccount *)account withCompletionHandler:(ACAccountStoreSaveCompletionHandler)completionHandler
As I know we get access to the account through OAuth and we do not get user's credentials. So how do we create an account? I've found that ACAccount has the only one creation method:
- (id)initWithAccountType:(ACAccountType *)type
What actually happens when we create account this way? And can we save it now ?
Ok, finally I've found the information about it.
Consider this scenario:
Our app have already been authorized by the user and we've got both access token and secret. Now we want to support new iOS 6 features and create twitter (for example) account in Settings. To do this we need to migrate these tokens to the central account store.
Here's how:
- (void)storeAccountWithAccessToken:(NSString *)token secret:(NSString *)secret {
//We start creating an account by creating the credentials object
ACAccountCredential *credential = [[ACAccountCredential alloc] initWithOAuthToken:token tokenSecret:secret];
ACAccountType *twitterAcctType =[self.accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
ACAccount *newAccount = [[ACAccount alloc] initWithAccountType:twitterAcctType];
//Here we assign credentials and now can save account
newAccount.credential = credential;
[self.accountStore saveAccount:newAccount withCompletionHandler:^(BOOL success, NSError *error) {
if (success) {
NSLog(#"the account was saved!");
}
else {
//Handle error here
}
}];
}
For more information about it read here how to migrate tokens
Whenever you try and present a TWTweetComposeViewController and a user has no Twitter account added to their device they are prompted to go to the Settings app and add one. Once they are done they have to manually navigate back to the application.
Is there any way for my application to know that they have successfully added an account?
Actually, there is a way to be notified of new accounts while your application is running. ACAccountStore provides a notification ACAccountStoreDidChangeNotification which you can observe for changes using Key-Value Observing.
Ah, in that case, you can keep track of how many user accounts they had when they first launched the app, save that to NSUserDefaults. When you're putting up the TWTweetComposeViewController check to see if the number is the same as it was before.
__block BOOL accountSizeChanged = NO;
ACAccountStore *accountStore = [[ACAccountStore alloc] init];
ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[accountStore requestAccessToAccountsWithType:accountType withCompletionHandler:^(BOOL granted, NSError *error) {
if(granted)
{
int oldSize = [[NSUserDefaults standardUserDefaults] integerForKey:#"myKey"];
int newSize = [accountStore accountsWithAccountType:accountType].count;
if(oldSize != newSize)
accountSizeChanged = YES;
[[NSUserDefaults standardUserDefaults] setInteger:newSize forKey:#"myKey"];
}
}];
I am trying to make a basic iphone app that shows nearby tweets. I was using the TWRequest object to accomplish this with the twitter search api. Unfortunately, I would actually like to mark the tweets on a map using their GPS coordinates and the search api doesn't seem to return the actual location that a tweet was made with any better accuracy than the city name.
As such, I think I need to switch to the streaming api. I am wondering if it is possible to continue using the TWRequest object in this case or if I need to actually switch over to using NSURLConnection? Thanks in advance!
Avtar
Yes, you can use a TWRequest object. Create your TWRequest object using the appropriate URL and parameters from the Twitter API doco, and set the TWRequest.account property to the ACAccount object for the Twitter account.
You can then use the signedURLRequest method of TWRequest to get an NSURLRequest which can be used to create an asynchronous NSURLConnection using connectionWithRequest:delegate:.
Once this is done, the delegate's connection:didReceiveData: method will be called whenever data is received from Twitter. Note that each NSData object received may contain more than one JSON object. You will need to split these up (separated by "\r\n") before converting each one from JSON using NSJSONSerialization.
It took me a bit of time to get this up and running, So I thought I aught to post my code for others. In my case I was trying to get tweets close to a certain location, so you will see that I used a locations parameter and a location struct I had in scope. You can add whatever params you want to the params dictionary.
Also note that this is bare bones, and you will want to do things such as notify the user that an account was not found and allow the user to select the twitter account they would like to use if multiple accounts exist.
Happy Streaming!
//First, we need to obtain the account instance for the user's Twitter account
ACAccountStore *store = [[ACAccountStore alloc] init];
ACAccountType *twitterAccountType = [store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
// Request permission from the user to access the available Twitter accounts
[store requestAccessToAccountsWithType:twitterAccountType
withCompletionHandler:^(BOOL granted, NSError *error) {
if (!granted) {
// The user rejected your request
NSLog(#"User rejected access to the account.");
}
else {
// Grab the available accounts
NSArray *twitterAccounts = [store accountsWithAccountType:twitterAccountType];
if ([twitterAccounts count] > 0) {
// Use the first account for simplicity
ACAccount *account = [twitterAccounts objectAtIndex:0];
NSMutableDictionary *params = [[NSMutableDictionary alloc] init];
[params setObject:#"1" forKey:#"include_entities"];
[params setObject:location forKey:#"locations"];
[params setObject:#"true" forKey:#"stall_warnings"];
//set any other criteria to track
//params setObject:#"words, to, track" forKey#"track"];
// The endpoint that we wish to call
NSURL *url = [NSURL URLWithString:#"https://stream.twitter.com/1.1/statuses/filter.json"];
// Build the request with our parameter
TWRequest *request = [[TWRequest alloc] initWithURL:url
parameters:params
requestMethod:TWRequestMethodPOST];
// Attach the account object to this request
[request setAccount:account];
NSURLRequest *signedReq = request.signedURLRequest;
// make the connection, ensuring that it is made on the main runloop
self.twitterConnection = [[NSURLConnection alloc] initWithRequest:signedReq delegate:self startImmediately: NO];
[self.twitterConnection scheduleInRunLoop:[NSRunLoop mainRunLoop]
forMode:NSDefaultRunLoopMode];
[self.twitterConnection start];
} // if ([twitterAccounts count] > 0)
} // if (granted)
}];