I have created a library which can download JSON data which is then placed into an NSDictionary. I wrap this class with a simple Twitter engine which allows me to pull my friends timeline, post an update and post an update with my GPS location. From my limited experience with Objective-C the way to connect everything is with delegation. I set a delegate property which calls back the asynchronous result to either a selector or a method signature. I can even create an optional or required interface on the delegate which will allow Xcode to assist me a little with implementing the delegate. To learn about using delegates in Objective-C I created this simple project.
http://www.smallsharptools.com/downloads/ObjC/Delegates.zip
It defines a Worker class which allows you to initialize the class with a delegate. When the work is done with the doWork method it looks for a method signature on the delegate to send a message back to it. It uses the following code.
if([[self delegate] respondsToSelector:@selector(workFinished:)]) {
NSString *msg = @"That's it? Easy!";
[[self delegate] workFinished:msg];
}
It looks for the workFinished: method to pass back a message. I declared this method signature as an optional interface with the following code in the header, Worker.h.
@protocol WorkerNotifications
@optional
- (void) workFinished: (NSString *) msg;
@end
You can see the rest of the project from the download for all of the details. But these 2 code snippets show how this delegation pattern works. But with the Twitter class I need to know the context of the method which started an asynchronous action which leads to a callback to a delegate method. If I call the sendUpdate method more than once from the calling class, how I am supposed to know the context of the callback?
Normally with a language like JavaScript, Java or C# I would create an inline closure or anonymous class which would have access to the starting context, but that is not possibly currently with Objective-C on the iPhone. I found that this question was already asked and answered on StackOverflow.
http://stackoverflow.com/questions/766475/anonymous-delegate-implementation-in-objective-c
So what I have done is skip the optional interface and instead passed in a selector which the Twitter class will call when the asynchronous action is completed. A call to start this action looks like...
CMTwitterEngine *engine = [[CMTwitterEngine alloc] initWithDelegate:self];
[engine setSendUpdateFinished:@selector(sendUpdateFinished:)];
[engine setSendUpdateFailed:@selector(sendUpdateFailed:)];
[engine setParsingSendUpdateFailed:@selector(parsingSendUpdateFailed:)];
[engine setUsername:TWITTER_USERNAME pass:TWITTER_PASSWORD];
[engine sendUpdate:statusUpdateText.text];
This code first initializes the engine reference with self as the delegate. To attach the callbacks I send in selectors which I originally had on the sendUpdate method signature but the method calls got pretty long. I opted to simply set properties of the selectors. This all works but I am not sure I like how this is working since it only partially solves my problem.
To complete this example, I finish the asynchronous work and eventually call a method internally which looks for the given selector and calls it if it is defined.
- (void)sendUpdateFinished:(NSDictionary *)dictionary {
if (self.sendUpdateFinished != nil) {
[self.delegate performSelector:self.sendUpdateFinished withObject:dictionary];
}
}
I can pass in the status message to send as a Twitter update but I still do not have the context of the originating call. What if I want to call sendUpdate more than once and the first asynchronous call is still running? And what if the second call finishes first? They will both have self as the delegate so I would have to either track the context somehow or pass them to a different selector to distinguish them, which also does not satisfy my needs. What happens if I have 3 or 4 or 5 asynchronous calls? I need to know which ones were sent successfully and when they are complete.
It appears the only way that I can do all this is to create a class which holds onto all of the properties needed for the context, have that class act as the delegate for the call to the asynchronous Twitter method and then report back to the parent class which is likely UIViewController. I would take this approach but I have not read about this approach or seen any sample code yet which does this.
What would you do? How would you handle multiple asynchronous calls going out which could end in a different order than going out and then process them with context upon completion?