Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

iOS App Architecture with NSOperations

Two month ago I started to write a new iPhone Application and for this reason I created a generic RESTFul web service, which allows me to have a lot of these necessary features like user authentication, user profiles, a friendship system, media processing, a messaging system and so on. In my mind there are several use cases to reuse this webservice for future iPhone applications.

With this state of mind, I decided to write a static library for this application (and all future apps) that handles all the heavy lifting like media (image, video, sound) setup and processing, communicating with the web service, parsing and mapping of the results, handling CoreData and so on.

Given my application there are scenarios when a lot of parallel tasks are running (worst case) e.g. the user currently changes his/her profile picture, while the application sends the users location to the server (in the background) and a new push notification is received.

So decided to encapsule each logical operation (like SendUserLocation or GetCurrentFriendList) in a NSOperation and add them to a serviceQueue (NSOperationQueue).

Each Operation is able to spawn subtasks when the operation successfully got a result from the webservice and should process it now.

alt text

A typical ServiceManager method looks like

- (void)activateFriendsSync:(id)observer onSuccess:(SEL)selector {
    ELOSyncFriends *opSyncFriends  = [[ELOSyncFriends alloc] initWithSM:self];
    [self ELServiceLogger:opSyncFriends];
    [serviceQueue addOperation:opSyncFriends];
    if(observer) {
        [self registerObserver:observer selector:selector name:opSyncFriends.notificationName]; 
    }
}

Each operation, request (to the server) and subTask uses a GUID as a notificationName to notify the parent object when it's done processing. If everything in an operation is done, it sends a notification back to the user interface.

That said, the code for adding and removing subtasks looks like this

- (void)removeSubTask:(NSNotification*)notification {
    ELRequest *request = (ELRequest*)[notification object];
    [subTasks removeObjectIdenticalTo:request.notificationName];
    if([subTasks count] == 0) {
         // all SubTaks done, send notification to parent
        [serviceManager.notificationCenter postNotificationName:self.notificationName object:request];
    }
}

- (NSString*)addSubTask {
    NSString* newName = [self GetUUID];
    [subTasks addObject:[newName retain]];
    [serviceManager.notificationCenter addObserver:self selector:@selector(removeSubTask:) name:newName object:nil];
    return newName;
} 

- (NSString *)GetUUID {
    CFUUIDRef theUUID = CFUUIDCreate(NULL);
    CFStringRef string = CFUUIDCreateString(NULL, theUUID);
    CFRelease(theUUID);
    return [(NSString *)string autorelease];
}

Now all I've got to do is to call the serviceManager in my gui to start a specific operation like

[self.core.serviceManager activateFriendsSync:nil onSuccess:nil];

If I want to register an observer, I just pass an observer object and a selector like this

[self.core.serviceManager activateFriendsSync:self onSuccess:@selector(myMethod:)];

Last but not least my question(s): The "architecture" runs very well and stable, but is it worth doing? Does it create too much overhead? Does it even make sense? How you, personally, implement concurrent operations?

Best Henrik

P.S. Feel free to edit my question, ask questions (as a comment), call me names for this thinking.

I really had a hard time explaining it, basically because I'm not a native english speaker. And don't get me wrong. I didn't write this posting to show off in any kind. All I want to do is learn (and maybe to write a more advanced iphone / objective c question)

like image 372
Henrik P. Hessel Avatar asked Oct 04 '10 15:10

Henrik P. Hessel


2 Answers

yes, if it's being farmed out to service requests and you have many calls to make, then such a library is not (imo) overkill, and i have written something similar. this structure made it very easy for me to manage a complex system, with very complex and varied tasks.

the primary design difference i made was not to use NSNotification with a service manager. instead, i favored using protocols/types for callbacks (which the operation holds a reference to). NSNotification is quite heavy. in this case, the operation does not retain the listener(s)/notified objects, but the listeners retain the operation. if the relation is 1-1, then allow cancellation.

another major consideration is to define threading early on. allow clients to define which thread they want to receive their response on. the reason for this is that there is often a constraint or logical entry for the callback if the notified/listener must update the UI (e.g., you're using UIKit or AppKit). therefore, the creator can say to the operation 'you must inform me from the main thread', or 'i can handle the response from any thread'. this will reduce your controller/listener/observer code and chance for errors greatly.

like image 120
justin Avatar answered Nov 19 '22 08:11

justin


For "sub operations": what about putting them on your queue, with the parent operation being a dependency (cf. -[ NSOperation addDependency: ]) of each of its child operations? The NSOperationQueue can sequence your whole pile of operations for you. I think this is simple and natural to work with.

like image 2
nielsbot Avatar answered Nov 19 '22 08:11

nielsbot