iOS
BlueTooth

Modified

Downloads

HelloWorld application

Note that testing requires:

  1. simulators on two machines,
  2. BlueTooth is simulated over a local network,
  3. both machines must be on the same local network (WiFi, Ethernet connection over a switch or hub, direct Ethernet connection between two machines.

Resources

GameKit Developers Guide

Bonjour

Overview

BlueTooth is part of the GameKit framework, which provides a simple API. At present, there does not seem to be an API for lower-level BlueTooth protocols.

Service discovery is very simple through the use of Bonjour, which works on BlueTooth, EtherNet and WiFi networks.

Services are advertized by broadcasts over a local network. For our example, HelloWorld is the name of the service with service ID of HelloWorldID.

Below are two cases for advertizing a service:

  1. A server advertizes a service and waits for a client to discover and connect to the service.
  2. Several peers advertize the same service and wait for a peer to discover and connect to the service.

Peer-to-peer communication offers several advantages:

  1. Possibly only one copy of the application executes on multiple peers.
  2. More robust than client/server since any device can act as a client or a server.

 

HelloWorld

The basics of peer-to-peer communication are illustrated below. Remember another device is executing the same app.


Start by Connect which creates picker.

Picker searching for other devices.

List of nearby devices. User selects.

Other (client) device chose first,
we (server) can accept.

Send/receive message on either device.

 

Code

The code is numbered to reflect the order executed.

  1. Creates a GKPeerPickerController picker which in turn creates a session.

    The picker is a UI for picking devices to connect. The following restricts discovery to only nearby, peer devices.

    mPicker.connectionTypesMask = GKPeerPickerConnectionTypeNearby;
     

  2. Creates a session.

    The session object manages the connection with the device the user chooses in the picker; data can be sent and received through a session.

    Three key elements in establishing a session are:
     

    1. Session or service ID used to identify the services available on a remote device, ours is HelloWorldID.
       
    2. Name seen by remote user, ours is HelloWorld; default is device name.
       
    3. Session mode:
      1. server, which advertizes services and wait for connections
      2. client, which searches for services but does not advertize
      3. peer, which does both, defined by GKSessionModePeer.

    To create a peer session:

          GKSession* session = [[GKSession alloc] initWithSessionID: @"HelloWorldID"
                                                                                displayName: @"HelloWorld"
                                                                                 sessionMode: GKSessionModePeer];
     

  3. When remote peer chooses this device, need to:
     
    1. retain session object for this peer
       
    2. make self the session delegate to receive events
       
    3. dismiss the picker UI
       
  4. When peer connection made or broken, a state change event occurs.

    It is displayed but otherwise ignored here.
     

  5. Send or receive "Hello World" message.

    Message is sent to all peers that have connected. Note that a reliable transmission is made by:

          [mSession sendDataToAllPeers: [@"Hello World"
                            dataUsingEncoding: NSASCIIStringEncoding]
                                   withDataMode: GKSendDataReliable error:nil];
     

    Receive event signals arrival of a message so blocking for reading data is not necessary.


#import <UIKit/UIKit.h>
#import <GameKit/GameKit.h>
 
@interface HelloWorldViewController :
            UIViewController<GKPeerPickerControllerDelegate,GKSessionDelegate> {
      GKSession *mSession;
      IBOutlet UITextView *mTextView;
}
 
-(IBAction) connectClicked:(id)sender;
-(IBAction) sendClicked:(id)sender;

@property (retain) GKSession *mSession;
 
@end
#import "HelloWorldViewController.h"
 
@implementation HelloWorldViewController
 
@synthesize mSession;
 
//    1. Create a picker for user to choose from discoverable local devices

-(IBAction) connectClicked:(id)sender{
      GKPeerPickerController *mPicker=[[GKPeerPickerController alloc] init];
      mPicker.delegate=self;
      mPicker.connectionTypesMask = GKPeerPickerConnectionTypeNearby;
      [mPicker show];
}

// 2. Creates a GKPeerPickerConnectionTypeNearby session when picker created

- (GKSession *)peerPickerController: (GKPeerPickerController *) picker
               sessionForConnectionType: (GKPeerPickerConnectionType) type {

      GKSession* session = [[GKSession alloc] initWithSessionID: @"HelloWorldID"
                                                                     displayName: @"HelloWorld"
                                                                     sessionMode: GKSessionModePeer];
    [session autorelease];
    return session;
}
 
// 3. This peer has connected to remote peer GKSession  selected by the user.

- (void)peerPickerController:(GKPeerPickerController *) picker
                   didConnectPeer:(NSString *)peerID
                            toSession:(GKSession *)session {

    self.mSession = session;           // Use a retaining property on the session.
     
    session.delegate = self;              // Our object is the session's delegate.
    [session setDataReceiveHandler: self withContext:nil];
     
    [picker dismiss];                         // Remove picker
    [picker autorelease];
                                                       // Start application.
}

// 4. A remote peer caused a state change.
- (void) session: (GKSession *) session
                peer: (NSString *) peerID
                didChangeState: (GKPeerConnectionState)state{     
      switch (state)    {
        case GKPeerStateConnected:
             {
                   NSLog(@"%@ connected to %@", session.peerID, peerID );
                   break;
             }
        case GKPeerStateDisconnected:
             {
                   NSLog(@"%@ Disconnected from %@",session.peerID, peerID );
                   break;
             }
    }
}

// 5. Send data using retained session
-(IBAction) sendClicked:(id)sender {
      [mSession sendDataToAllPeers: [@"Hello World"
                          dataUsingEncoding: NSASCIIStringEncoding]
                                 withDataMode: GKSendDataReliable
                                               error: nil];
}

 // 5. Receive data delegate method
- (void) receiveData: (NSData *) data
                  fromPeer: (NSString *) peer
                  inSession: (GKSession *) session
                     context: (void *) context                    {    
      mTextView.text = [[NSString alloc] initWithData:data
                                                                  encoding:NSASCIIStringEncoding];
}
 
- (void)didReceiveMemoryWarning {
    [super didReceiveMemoryWarning];
}
 
- (void)dealloc {
    [super dealloc];
}

@end

For two peers, only one (client peer) will connect to the other (server peer) depending upon the order in which the iPhone/iTouch picked.

The client peer executes on a connection:

- (void)peerPickerController:(GKPeerPickerController *) picker
                   didConnectPeer:(NSString *)peerID
                   toSession:(GKSession *)session

The server peer executes on a connection:

- (void) session: (GKSession *) session
                peer: (NSString *) peerID
                didChangeState: (GKPeerConnectionState)state

This is useful when two peers must perform their function in some order.

For example, in TicTacToe one peer must play X and the other O. Assigning the client peer O and the server peer X in their respective connection delegate methods.