views:

3994

answers:

2

I am writing a controller for an audio server on the iPhone. Each 'view' generally needs to get data from the TCP/IP socket as a client. I have sockets working from one class using the AsyncSocket class. (which, after trying to get a socket client working for more time than I'd like to admit, is a very impressive and helpful class).. This requires delegate functions to be written for recieving data...

- (void)onSocket:(AsyncSocket *)sock didReadData:(NSData *)data withTag:(long)tag

I have a standard table view interface where I see a list of Artists on one view, then when an artist is selected I move to the albums on the next view and so on.

My question is: When I go from view to view, what is the best way to still send and receive data? Do I need to create a whole new socket for each view class? (seems a bit over the top) Somehow link the local receiver delegate again?

I can't seem to think of the 'proper' way to get this working outside of 1 class and there isn't much online about socket client communications on the iphone.

+2  A: 

It sounds like you want to have the delegate methods received by multiple objects, right? On Mac OS X, the solution is to use the notification system. I haven't looked at the AsyncSocket class, but it looks like it only supports a delegate out of the box.

Notifications are great because they let an object broadcast information to any other objects that are interested in receiving it. Objects register themselves with the notification center to be notified when particular notifications are posted. You could very easily add this capability by implementing your own class to wrap AsyncSocket.

Here's what you'd do. You'd write your own class that has an AsyncSocket as an instance variable. You would set this class as the delegate for the AsyncSocket object. Then, when the delegate methods get called, you would post notifications to the NSNotificationCenter. You'll probably need to stuff the parameters from the delegate methods into the notification's userInfo dictionary.

On the flip side, your view controllers would sign up with the NSNotificationCenter as observers for the notifications your custom class sends. Then, every time the delegate methods fire, each view controller will receive a notification of that event.

Enough talk; here's some code:

extern NSString *const AsyncSocketDidReadData;

@interface MySocketWrapper : NSObject { // give this class a better name ;-)
    AsyncSocket *socket;
}

@property (nonatomic, readonly) socket;

@end

In the .m file:

NSString *const AsyncSocketDidReadData = @"AsyncSocketDidReadData";

@implementation MySocketWrapper

@synthesize socket;

- (id)init {
    if (![super init]) return nil;

    socket = [[AsyncSocket alloc] init]; // initialize this however you want
    [socket setDelegate:self];
    return self;
}

- (void)onSocket:(AsyncSocket *)aSocket didReadData:(NSData *)data withTag:(long)tag {
    NSDictionary *userInfo = 
         [NSDictionary dictionaryWithObjectsAndKeys:
          data, @"data",
          [NSNumber numberWithLong:tag], @"tag",
          nil];
    [[NSNotificationCenter defaultCenter] postNotificationName:AsyncSocketDidReadData object:self.socket userInfo:userInfo];
}

@end

Finally, in your various view controllers, you can write code like this:

- (id)initWithNibName:(NSString *)nibName bundle:(NSBundle *)bundle {
    if (![super initWithNibName:nibName bundle:bundle]) return nil;

    // Do any initalization you need here

    // Note that if you specify 'nil' for object, you'll be sent notifications for every MySocketWrapper object.
    [[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(asyncSocketDidReadData:) notification:AsyncSocketDidReadData object:nil]; 

    return self;
}

- (void)asyncSocketDidReadData:(NSNotification *)notification {
    AsyncSocket *socket = [[notification object] socket];
    NSData *theData = [[notification userInfo] objectForKey:@"data"];
    long tag = [[[notification userInfo] objectForKey:@"tag"] longValue];

    // Do what you want with the data here
}

Obviously, this code isn't entirely complete, and I may have gotten some method names wrong (I'm doing this from memory) but this is a solution that should work for you.

Alex
Here's the reference to NSNotificationCenter: https://developer.apple.com/iphone/library/documentation/Cocoa/Reference/Foundation/Classes/NSNotificationCenter_Class/index.html
Alex
A: 

The previous answer is great but "notification:" in the view controller snippet should be "name:".

KeithF