Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Strange behaviour when trying to use Twitter ACAccount

I've been playing a bit with the new Social.Framework and in particular with SLRequest, both available on iOS 6 and upwards. Thing is, I got really surprised by a crash I've been getting when trying to post such request.

I've been getting the crash with both Facebook and Twitter accounts, so that's why I knew it wasn't related to any particular issue with one of them. It had to be related to the ACAccount object, which I'm getting in this way:

if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"6.0")) {
    //iOS 6
    if (_twitterEnabled) {
        if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeTwitter]) {
            //Set up account
            [accountStore requestAccessToAccountsWithType:accountTypeTwitter options:nil completion:^(BOOL granted, NSError *error) {
                if (granted && !error) {
                    //Get account and get ready to post.
                    NSArray *arrayOfAccounts = [accountStore accountsWithAccountType:accountTypeTwitter];
                    if ([arrayOfAccounts count] > 0) {
                        _twitterAccount = [arrayOfAccounts lastObject];
                    }
                }
            }];
        }
    }
    if (!_facebookEnabled) {
        ACAccountType *accountTypeFacebook = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierFacebook];
        if ([SLComposeViewController isAvailableForServiceType:SLServiceTypeFacebook]) {
            NSArray *permissions = @[@"user_about_me",@"user_likes",@"email"];
            NSMutableDictionary *options = [NSMutableDictionary dictionaryWithObjectsAndKeys:kFacebookAppIDString, ACFacebookAppIdKey, permissions, ACFacebookPermissionsKey, ACFacebookAudienceFriends, ACFacebookAudienceKey, nil];
            [accountStore requestAccessToAccountsWithType:accountTypeFacebook options:options completion:^(BOOL granted, NSError *error) {
                if (granted && !error) {
                    [options setObject:@[@"publish_stream",@"publish_actions"] forKey:ACFacebookPermissionsKey];
                    [accountStore requestAccessToAccountsWithType:accountTypeFacebook options:options completion:^(BOOL granted, NSError *error) {
                        if (granted && !error) {
                            NSArray *arrayOfAccounts = [accountStore accountsWithAccountType:accountTypeFacebook];
                            if ([arrayOfAccounts count] > 0) {
                                _fbAccount = [arrayOfAccounts lastObject];
                            }

                        }
                    }];
                }
            }];
        }
    }
}

Both _twitterAccount and _fbAccount are ACAccount objects in which I store the relevant account when retrieved from the Account Store.

The problem came when later I tried to use such objects (I'll just post the twitter method for brevity's sake):

        NSDictionary *twitterMsg = @{@"status" : message};
        if (SYSTEM_VERSION_GREATER_THAN_OR_EQUAL_TO(@"6.0")) {
            SLRequest *postRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter requestMethod:SLRequestMethodPOST URL:kTwitterUpdateStatusURL parameters:twitterMsg];
            [postRequest setAccount:_twitterAccount];
            [postRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
                NSLog(@"Twitter HTTP response: %d", [urlResponse statusCode]);
            }];
        }

When calling setAccount: on postRequest I was getting an exception with the message: "Invalid account type for this request", which was obviously false. I also tried to debug the code and strangely the accountType on _twitterAccount was set to nil right before being sent to the ACAccount object. More strangely, if I put

NSLog(@"%@",_twitterAccount);

right under

_twitterAccount = [arrayOfAccounts lastObject]

on the first section of code, it works with no problem.

I've reviewed my code and I don't think I'm doing anything wrong so that I think it can be a bug on the framework? It looks like the ACAccountType is being released when it shouldn't, but I wanted to check if anyone of you could see anything wrong with my code that was provoking it and/or find an explanation for the issue, which I'm unable to solve by myself.

Thank you

UPDATE

It seems some other people have the same issue, I'm accepting one of the answers because it actually solves the issue, but I'll be looking forward to anyone that can find an explanation for the issue.

like image 419
Alejandro Benito-Santos Avatar asked Nov 12 '12 18:11

Alejandro Benito-Santos


2 Answers

I was also getting "Invalid account type for this request" when using SLRequest on accounts retrieved via both of the following

ACAccountStore *account_store = [[ACAccountStore alloc] init];
ACAccountType *account_type_twitter = [account_store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
// A.
NSArray *accounts = [account_store accountsWithAccountType:account_type_twitter]; 
// B.
NSString *account_id = @"id from doing account.identifier on one of the above";
ACAccount *account = [account_store accountWithIdentifier:account_id];

NSLog on the account had everything populated as expected but the type was set as null. Guessing this is a bug with Apple's SDK. Doing the following fixed the accounts for me so I could use them with SLRequest:

ACAccountType *account_type_twitter = [account_store accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
account.accountType = account_type_twitter;
like image 183
Stephen Handley Avatar answered Sep 23 '22 11:09

Stephen Handley


You have to retain ACAccountStore:

@property (nonatomic, strong) ACAccountStore *accountStore;

The ACAccount documentation never states specifically that you must retain ACAccountStore, but it does state that

"To create and retrieve accounts from the Accounts database, you must create an ACAccountStore object. Each ACAccount object belongs to a single ACAccountStore object."

When you call:

NSArray *accounts = [accountStore accountsWithAccountType:accountType]

Those accountStore objects in the array don't necessarily have all of their properties fetched from the database. Some properties (like accountType) are only retrieved from the Accounts database if needed. This caused the strange behavior that you saw when you logged the account and everything magically worked. When you logged the account, the ACAccountStore object was still in memory and the logging caused the retrieval of the AccountType property. At that point, the AccountType was retained with the ACAccount and everything was able to work later even after ACAccountStore was released.

like image 44
GingerBreadMane Avatar answered Sep 23 '22 11:09

GingerBreadMane