I am creating a NSObject to load the TimeLine twitter
Code:
+ (NSArray *)executeTweetFetch
{
__block NSArray *fetchedTweets = [NSArray array];
NSURL *getTweetUrl = [NSURL URLWithString:@"http://api.twitter.com/1.1/statuses/home_timeline.json"];
SLRequest *tweetRequest = [SLRequest requestForServiceType:SLServiceTypeTwitter
requestMethod:SLRequestMethodGET
URL:getTweetUrl
parameters:nil];
tweetRequest.account = ACAccount HERE!!!;
[tweetRequest performRequestWithHandler:^(NSData *responseData, NSHTTPURLResponse *urlResponse, NSError *error) {
if ([urlResponse statusCode] == 200) {
// Parse the responseData, which we asked to be in JSON format for this request, into an NSDictionary using NSJSONSerialization.
NSError *jsonParsingError = nil;
fetchedTweets = [NSJSONSerialization JSONObjectWithData:responseData options:0 error:&jsonParsingError];
//At this point, fetchedTweet seems working fine, it gets the array send back.
}
}];
return fetchedTweets;
}
…is Ok…but I have to add a ACAccount (another view). and since it is a + (NSArray *) it does not recognize my (ACAccount) or any object outside
Synchroneity
Before getting into the issue that you’re asking about, it’s well worth discussing what @MartinR mentioned at greater length: The method
-[ACAccount performRequestWithHandler:]is asynchronous.Calling the method from the thread that code is being executed on results in some activity starting up on a second background queue. Meanwhile, execution continues on the first thread (the thread that is executing
performRequestWithHandler:andexecuteTweetRequest). Some indeterminate amount of time afterexecuteTweetRequestis called, the block passed as the only argument to-[ACAccount performRequestWithHandler:]is called.Consequently, you will not be able to synchronously return the array of tweets from
executeTweetRequest.The most stylish approach would be to change your method to be block based:
When calling
fetchTweetsWithCompletion:from your secondUITableViewControllersubclass, you will pass in a block probably much like the following:The result of this code is that once the fetch of the tweets on a background queue has completed, your table view controller’s property
tweetswill be set to the fetched result and its table view will reload its data.Scope (Addressing The Question)
The issue that you’re describing
is that you’re unable to access the instance variables of any of the instances of your class (I’m going to call it
TwitterWrapperfor convenience) from within your class method+[TwitterWrapper executeTweetFetch]. The problem here is one of scope. (As a result, thanks to ARC, the problem is also one of memory management.)Our goal is to stash an instance of
ACAccountsomewhere from your first table view controller and access that instance from your second table view controller.There are several ways of doing this:
Use A Global Variable
The worst approach is to a use a dreaded global variable:
In
TwitterWrapper.h, you would declareexterntheACAccountfor twitter:In
TwitterWrapper.m, you would definetwitterAccountso that it has global scope (for example, prior to the@implementationblock:And in the definition of your class method
executeTweetFetchyou would haveNOTE: This approach is very unstylish, not to mention outright dangerous. Global variables should be avoided whenever possible. Avoiding one is certainly possible in this case.
The next two approaches both involve changing
fetchTweetsWithCompletion:into an instance method and adding anACAccountproperty toTwitterWrapper. The class’@interfacewill look likeThe
@implementationwill look likeUse A Shared Instance
The second worst approach in this case is to use a shared instance of
TwitterWrapper. This involves adding a class method (called something clever like+sharedWrapper) to the class:and using Grand Central Dispatch‘s
dispatch_onceto initialize astaticlocal variable:At this point, you have an instance of
TwitterWrapperinto which you can store the instance ofACAccountfrom your first table view controller and make use of (viaself.accountwithin the implementation of-fetchTweetsWithCompletion:) from your second table view controller:Somewhere within your first
UITableViewControllersubclass you would haveThen, within your second
UITableViewControllersubclass (probably within-viewDidLoad), you would haveThis solution, however, is very similar to using a global variable. (The difference is that the creation of the global variable will be thread safe.)
Pass An Instance Of TwitterWrapper
A much better solution is to pass an instance of
TwitterWrapperfrom your first table view controller to your second.NOTE: For brevity, I will assume that the second table view controller becomes active immediately after the user selects an account row in the first table view controller.
To do this, you will need to add a property
to your second
UITableViewControllersubclass.Somewhere within your first table view controller you would have
Then, within your second
UITableViewControllersubclass (probably within-viewDidLoad), you would havePass An Instance Of ACAccount
Rather than storing an instance of
TwitterWrapperon your secondUITableViewControllersubclass instance, the best solution is to store an instance ofACAccountand alterTwitterWrapper‘s interface for fetching tweets once again.In this case, we’ll want the fetch method to again be a class method. As @MartinR suggested, add an account parameter to your fetch method:
Next, add a property of type
ACAccountto your secondUITableViewControllersubclass:Then, somewhere within your first table view controller you would have
Then, within your second
UITableViewControllersubclass (probably within-viewDidLoad), you would have