To help on the development of peer to peer apps on the iPhone, I developed an abstraction on GKSession called NetworkProxy. To use it, we just create a new instance of a NetworkMessage:

        NetworkMessage* message = [[[NetworkMessage alloc] init] autorelease];

Set it’s message type and any other (optional) information we may want to transmit:

        [message setMessageType:NetworkMessageType_FinalScore];
        [message setScore:35];
        [message setMessage:@"Text to send goes here!"];
        

And send it:

        [networkProxy sendMessage:message];

NetworkProxy is the class that does the heavy work. It is initialized with a connected GKSession:

        @implementation NetworkProxy        
        
        -(id) initWithSession:(GKSession*) msession
        {
                …
                return self;
        }

And expects a INetworkClient to be injected, to be able to handle incoming messages:

        -(void) setClient:(id<INetworkClient>) mnetworkClient
        {
                networkClient = [mnetworkClient retain];
        }
        

Inheriting INetworkClient means implementing two methods, one to handle incoming messages and one for network errors:

        @protocol INetworkClient <NSObject>
                @required
                -(void) messageReceived:(NetworkMessage*) data;
                -(void) networkError;
        @end


Incoming messages should be pretty straight forward:

        -(void) messageReceived:(NetworkMessage*) data
        {
                switch ([data messageType]) {
                        case NetworkMessageType_GameStarting:
                                …
                                break;
                        case NetworkMessageType_Click:
                                …
                                break;
                        case NetworkMessageType_FinalScore:
                                …
                                break;
                        default:
                                break;
                }
        }

If you’re planning on using the code start by checking the NetworkMessage class. That’s where the magic (with some help from NSCoder) is.
Start by adding your own message types:

        typedef enum {
                NetworkMessageType_GameStarting = 0,
                NetworkMessageType_Click = 1,
                NetworkMessageType_FinalScore = 2,
                NetworkMessageType_SendBomb = 3
        } NetworkMessageType;

And be sure to un/serialize them properly:

        - (id)initWithCoder:(NSCoder *)coder
        {
                self = [super init];
                
                [coder decodeValueOfObjCType:@encode(NSInteger) at:&messageType];
                [coder decodeValueOfObjCType:@encode(NSInteger) at:&score];
        
                return self;
        }

        - (void)encodeWithCoder:(NSCoder *)coder
        {
                [coder encodeValueOfObjCType:@encode(NSInteger) at:&messageType];
                [coder encodeValueOfObjCType:@encode(NSInteger) at:&score];
        }

Of course, you can have your NetworkMessage with complex objects as attributes, just make sure they also implement NSCoder.

Code here.