Strange behaviour when trying to use Twitter ACAccount
Asked Answered
A

4

19

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.

Andromede answered 12/11, 2012 at 18:16 Comment(0)
E
31

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;
Execratory answered 22/11, 2012 at 7:16 Comment(4)
Hi, thanks for your answer. I already got to that conclusion, explicitly setting the accountType seems to retain the ACAccountType object. (As it does printing it to an NSLog). I will investigate further on the topic and see what's happening.Andromede
Wow, that solved in 2 min. something I was working some hours. Thanks.Valero
This seems like a bit of a hack; you should try the solutions below first (i.e. keep the account store around instead.)Zwieback
As the other solutions say, you need to retain the Account Store object (ACAccountStore).Closer
E
17

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.

Endgame answered 11/1, 2013 at 20:15 Comment(1)
This is the real fix rather than trying out the fix suggested in the accepted answer.Humphries
T
5

Are you retaining the ACAccountStore you used to fetch the arraryOfAccounts?

Tried answered 17/11, 2012 at 6:47 Comment(0)
S
1

I use this code without problem. (Twitter only)

ACAccountStore *accountStore = [[ACAccountStore alloc] init];
ACAccountType *accountType = [accountStore accountTypeWithAccountTypeIdentifier:ACAccountTypeIdentifierTwitter];
[accountStore requestAccessToAccountsWithType:accountType options:nil completion:^(BOOL granted, NSError *error) {
    if (!granted) {
        NSLog(@"Access denied.");
    }
    else {
        NSArray *accounts = [accountStore accountsWithAccountType:accountType];
        if (accounts.count > 0) {
            twitterAccount = [accounts objectAtIndex:0];
        }
    }
}];

I think something about accountStore init issue?

Twitter's official reference is here. Twitter: API requests with TWRequest

Selaginella answered 14/11, 2012 at 20:42 Comment(1)
Hi, thanks for your response. Definitely not, it's more a problem with accountType, if that's the case. I'm already running your code but it you read my question carefully, the problem is this code is running inside a singleton, which might me the problemAndromede

© 2022 - 2024 — McMap. All rights reserved.