12. Making Connections with GameKit and Bonjour

This chapter introduces GameKit, the simplest way to connect two iPhone devices. GameKit is Apple’s new ad hoc networking solution for peer-to-peer connectivity. It’s built on a technology called Bonjour that offers simple, no-configuration communications between devices. With GameKit and Bonjour, you can build games and utilities that move information back and forth between iPhones or between an iPhone and a desktop system. In this chapter, you discover how to use GameKit to build connected applications. You expand GameKit communications to create a cooperative drawing system that transmits both user touches and a selected color. You see how to add GameKit Voice to your applications for walkie-talkie-style voice chats. And you learn some basic Bonjour programming that goes beyond GameKit limitations, allowing you to expand your iPhone communications to the desktop.

Recipe: Creating Basic GameKit Services

GameKit provides peer-to-peer connectivity between iPhone and iPod touch devices. New to the 3.0 SDK, this framework helps you create interconnected applications that exchange live data in real time.

In its default implementation, GameKit works by creating and managing an ad hoc Bluetooth network that lets devices find each other, establish a connection, and transmit data through that connection. Starting with the iPhone 3.1 firmware, GameKit also allows you to find, connect, and transmit to devices on the same WiFi network. Using Bluetooth is a fast and reliable approach to interdevice communications. Unfortunately, Bluetooth communication using GameKit is not supported for first generation iPhones and iPod touches. GameKit offers an online (WiFi- or Internet-based) mode as well as the Bluetooth mode, but at the time of writing this book it’s basically a “bring your own technology to the table” option.

Although, as the name suggests, you can build games with GameKit, you can do far more. You can create applications to support collaborative layout, picture sharing, chats, and more. So long as the same application exists on both devices, you can establish GameKit communications either with (3.1 firmware or later) or without a shared WiFi network.

GameKit Bluetooth Limitations

All you need is proximity. GameKit’s Bluetooth-based applications are limited to about 10 meters, or 30 feet. So think of your audience including people riding together on a train or in a car, in a convention hall’s meeting room, or working in the same office. Within that range, your application can easily establish a peer-to-peer connection.

GameKit offers excellent performance for short, tight blips of information. Apple recommends that GameKit transmissions be limited to 1,000 bytes and under. Although GameKit can handle larger blobs, up to 95 Kilobytes at a time, it’s not meant for use as general device-to-device data transfer. Try to send too much data at once and you will receive transmission errors.

If you must transfer large files, you need to break those files into manageable chunks. Make sure you use standard handshaking and packet checksumming techniques to ensure the reliability of your data.

Device Limitations

GameKit’s Bluetooth networking is not for every iPhone and iPod touch. It works with the 3G iPhone and later models and with the second generation iPod touch and later models. You cannot deploy a GameKit Bluetooth application to first generation iPhones and iPod touch units. Plus, GameKit Bluetooth is only partially supported on the Simulator. That is, you can discover nearby devices, but you cannot actually connect to them.

As of the 3.1 SDK, Apple added support for the peer-peer key in the Info.plist UIRequiredDeviceCapabilities entry. This key indicates that your application requires peer-to-peer connectivity over Bluetooth. For firmware earlier than 3.1, you can specify required device features like telephony and microphone, but there’s no key available to describe a Bluetooth networking prerequisite.

Make it very clear in your marketing materials which devices you do and do not support, especially if your application centers on nearby iPhone connectivity. Users cannot use GameKit Bluetooth features on noncompliant devices. When attempting to do so, they’ll receive a message like the one shown in Figure 12-1, which displays a Bluetooth logo and says, “Not supported on this iPhone” (or iPod).

Figure 12-1 GameKit features are not universally available to all iPhone and iPod touch devices. The first generation iPhone and iPod touch units do not support GameKit Bluetooth connections. Avoid this error by specifying the peer-peer device requirement in your application’s Info.plist file.

image

This is why you should strongly consider offering an “online mode” fallback. The same interface that moves you to the Bluetooth-powered “nearby mode” provides a unified GUI allowing users to access other networking options. Recipe 12-8 demonstrates this second connection layer toward the end of this chapter.

Sessions

GameKit’s peer-to-peer connections are built using Bonjour networking. Bonjour, which is Apple’s trade name for zero configuration networking, allows devices to advertise and discover network services. Built into Mac OS X since version 10.2, Bonjour offers these features without calling attention to itself. For example, Bonjour powers the features that let users find shared music for iTunes or connect to wireless printers without requiring custom configuration. These services automatically appear when they become available and disappear when they’re not. It’s a powerful OS feature.

GameKit provides that same Bonjour power without having to build the often complicated Bonjour callbacks for registering and detecting services. With GameKit, you request a connection using a “peer picker” controller and then manage a “session” once the connection has been established.

GameKit’s session objects provide a single focus point for data transfer management. Each session uses a unique name, which you choose, to advertise itself. When an application looks for another device to connect to, it uses this name to identify compatible services.

If you use a Bonjour browsing service to look for that name, you’ll fail. Apple encodes the service name. For example, a service called “MacBTClient Sample” becomes the “_11d7n7p5tob54j._udp.” Bonjour service. GameKit automatically transforms the name you supply so it knows how to find matching services.

Unfortunately, there’s no Mac OS X or Windows API available to let you build services from a desktop system that would let you hook into GameKit. Apple’s name encryption more or less guarantees that standard Bonjour communications will not work with iPhone-based GameKit applications.

You read about bypassing this limitation later in this chapter by working directly with Bonjour.

Servers, Clients, Peers

GameKit offers three session modes; applications can act as servers, clients, and peers. Servers advertise a service and initialize a session, allowing clients to search for and connect to them. This is the kind of behavior that a smart printer uses, letting clients find and use its capabilities. It’s handy for devices that provide a fixed functionality but it’s not the best choice for most iPhone applications, especially games.

Peers work as both servers and clients. They advertise and search simultaneously. Once a peer selects a service, its client/server role is hidden both from the user and from the developer. This makes the peer approach very easy to develop for iPhones. You don’t have to build separate client and server applications. One peer-based application does all the work.

The Peer Connection Process

The peer picking process is handled by a class called GKPeerPickerController. It provides a built-in series of interactive alert dialogs that automate the task of advertising device availability and selecting a peer. Using this class is not mandatory. You can bypass it and create a custom class to search for and connect to peers. For simple connections, however, the GKPeerPickerController class offers a ready-to-use interface that sidesteps the need for detecting and negotiating with peers.

To use the peer picker, you allocate it, set a delegate (which must implement the GKPeerPickerControllerDelegate protocol), and show it. For targets earlier than the 3.1 SDK, avoid using autorelease with the picker. Instead, wait for a delegate callback and release it there, ensuring that you will not run into unexplained application crashes when the dismissal animation fails. This issue was fixed in the 3.1 firmware. With 3.1 or later, you can choose to release in the callback (3.0 compatible) or use autorelease (3.1-or-later compatible).

As mentioned, GameKit supports two kinds of connections: nearby (via Bluetooth) and online (via the Internet and WiFi). The code in this chapter’s first few recipes exclusively uses a nearby connection; an online recipe appears toward the end of this chapter. The Internet-style approach is less friendly and minimally documented at the time of writing this book. In contrast, the nearby Bluetooth style is friendly, easy-to-use, and ready for inclusion in your applications.

Displaying the Peer Picker

The following code allocates and shows a new peer picker controller, setting its connection style to Nearby. This skips an optional interaction step (discussed later in this chapter) where a user selects between Online and Nearby modes. When presented, it shows the interface in Figure 12-2. You do not have to use a peer picker to establish GameKit sessions. The iPhone SDK now lets you create your own custom interfaces to work with the underlying GameKit connections. A sample that demonstrates how to do so has been added to the sample code that accompanies this chapter.

image

Figure 12-2 This is the first screen presented to the user for peer-to-peer Bluetooth connections.

image

When your mask includes the online type as well (GKPeerPickerConnectionTypeOnline), the picker first asks the user which kind of connection to use before moving on to either the nearby connection interface of Figure 12-2 or to a custom online interface that you must build yourself.

Pressing Cancel

Users may cancel out of the peer picker alert. When they do so, the delegate receives a peerPickerControllerDidCancel: callback. If you display a “connect” button in your application, make sure to restore it at this point so the user can try again.

Creating the Session Object

As the picker delegate, you must supply a session object on request. Sessions, which provide an abstract class that creates and manages a data socket between devices, belong to the GKSession class and must be initialized with a session identifier. This is the unique string used to create the Bonjour service and link together two iPhone devices (peers) both advertising the same service. By setting the display name to nil, the session uses the built-in device name.

image

Although this is an optional method, you’ll usually want to implement it so you can set your session ID and mode. Upon detecting another iPhone or iPod with the same advertised service ID, the peer picker displays the peer as a compatible match, as shown in Figure 12-3.

Figure 12-3 The peer picker lists all devices that can act as peers.

image

Waiting for the peer picker list can take a few seconds or up to a few minutes. During development, you usually need to allow your Bonjour network stack to clear out any previous sessions when you iterate on the code. That’s what typically causes the longer delays. Apple recommends always debugging from a clean restart. If debugging delays get frustrating enough, make sure to reboot.

In normal use, connection delays usually hover around 45 seconds at a maximum. Warn your users to be patient. In Figure 12-3, Binky is the device name for a second iPhone running the same application. When the user taps the name Binky, this iPhone automatically goes into client mode, and Binky goes into server mode.

Client and Server Modes

When a device changes into client mode, it stops advertising its service. The Choose an iPhone or iPod Touch dialog shown previously in Figure 12-3 changes on the server unit. The client’s peer name dims to dark gray and the words “is not available” appear underneath. A few seconds later (and this can actually run up to a minute, so again warn your users about delays), both units update their peer picker display.

Figure 12-4 shows the server and client peer pickers during this process. The client waits as the server receives the connection request (left). On the server, the host user must accept or decline the connection (middle). Should they decline, an updated peer picker notifies the client (right). If they accept, both delegates receive a new callback.

Figure 12-4 Upon choosing a partner, the client goes into wait mode (left) as the server decides whether to accept or decline the connection (middle). Should the server decline, the client receives a notice to that effect (right).

image

The delegate callback lets the new peers dismiss the peer picker and to set their data received handler. Make sure to release the picker at this time.

image

Sending and Receiving Data

The data handler (in this case, self) must implement the receiveData:fromPeer:inSession:context: method. The data sent to this method uses an NSData object; there are no hooks or handles for partial data receipt and processing. As the data arrives as a single chunk, keep your data bursts short (under 1,000 bytes) and to the point for highly interactive applications.

image

Send data via the session object. You can send in reliable mode or unreliable mode. Reliable mode uses error checking and retrying until the data is properly sent. All items are guaranteed to arrive in the order they are sent, using TCP transmission. With unreliable mode, data is sent once using UDP transmission, with no retry, Data may arrive out of order. Use reliable mode (GKSendDataReliable) when you must guarantee correct delivery and unreliable mode for short bursts of data that must arrive nearly instantaneously.

image

As a rule, the one error you’ll encounter here results from queuing too much data in reliable mode. This produces a “buffer full” error.

State Changes

The following session delegate callback lets you know when a peer’s state has changed. The two states you want to look for are connected, that is, when the connection finally happens after the peer picker has been dismissed, and disconnected, when the other user quits the application, manually disconnects, or moves out of range.

image

To force a session to disconnect, use the disconnectFromAllPeers method.

image

Creating a GameKit Helper

Recipe 12-1 bundles the entire peer process into a simplified helper class. This class hides most of the GameKit details connection and data transfer details, while providing a demonstration of how to use these features. More importantly, it breaks down how you might look at the GameKit process, with its two key details: connection and data.

Recipe 12-1 GameKitHelper Class

image

image

image

image

image

Get This Recipe’s Code

To get the code used for this recipe, go to http://github.com/erica/iphone-3.0-cookbook-, or if you’ve downloaded the disk image containing all of the sample code from the book, go to the folder for Chapter 12 and open the project for this recipe.

Connecting

Any GameKit client you write must respond appropriately to the current connection state. You need to be able to establish that connection and respond when it goes live or when it drops. This class provides both connect and disconnect requests. For the most part, monitoring connections involves toggling a state Boolean (isConnected) and updating any buttons that control a connect/disconnect toggle.

To simplify these updates, the class allows you to assign a view controller (via the viewController property) and automatically updates the right-hand navigation item button. The button starts off as Connect, and when tapped disappears until the user cancels or a connection is fully established. After connecting, the button updates to Disconnect and provides a callback to the helper’s disconnect method.

Handling Data

By providing the connection state details for you, you can use this GameKitHelper class to create simple GameKit-enabled applications. The data handling, however, remains in your hands. Consider the following snippet. It shows the entire implementation for a chat application view controller, demonstrating the data transfer methods for this app.

image

image

As you can see, this application monitors a “send” text view, and when it changes (as the user types), sends the contents of that view through GameKit to a peer. At the same time, it waits for data, and when it receives it, updates the received text view to show what the peered user has typed. A Clear button erases the “send” view text.

This application demonstrates the second half of the GameKit problem, handling data. Recipe 12-1’s helper class creates a data delegate protocol, which is subscribed to by this text chat view controller. Data is passed along through the custom receivedData: delegate method, allowing the received text view to update with text typed on the peer device.

Similarly, the text view delegate method textViewDidChange: passes on responsibility for transmitting the actual text to the GameKitHelper class, calling the sendData: method to convey the data to connected peers.

Note

Recipe 12-1 does not address the issue of out-of-order packet receipt. See Apple’s GKTank sample code for an example of network packet handling. Apple’s code looks for the last packet time and the packet ID to ensure that packets are handled in the proper sequence.

The Helper Class

Recipe 12-1 contains the implementation for the GameKitHelper class. The associated sample code for this recipe shows the class in action, creating the text chat application discussed previously. This class was designed for reuse and can easily be decoupled from the text chat and repurposed, as you see in the next recipe.

Recipe: Peeking Behind the Scenes

At the time of writing, GameKit logs its status information as it runs, mostly by NSLog calls introduced by Apple’s engineers. You can track this information at the debug console, or you can use the following trick to redirect it to a file (the messages will not output to the console) and then display it in-application with a text view. Recipe 12-2 uses a standard C freopen() call to redirect stderr data, which is what NSLog() produces, to a file. It then sets up an NSTimer instance to monitor that file, and when the file contents change, it updates the text view with that output. You can use this redirection approach with GameKit or with any other application that produces console output of some kind.

Recipe 12-2 Monitoring GameKit

image

Get This Recipe’s Code

To get the code used for this recipe, go to http://github.com/erica/iphone-3.0-cookbook-, or if you’ve downloaded the disk image containing all of the sample code from the book, go to the folder for Chapter 12 and open the project for this recipe.

Take note of the way this recipe updates the content offset for the text view. It ensures that the text at the bottom of the view is always displayed after an update. It does this by setting the offset to one page height shorter than the full content size.

Recipe: Sending Complex Data Through GameKit

Sending simple strings back and forth through GameKit a la Recipe 12-1 gets you only so far. Soon, you’ll need to move forward to more complex objects and data. Property lists offer a good way to transmit custom objects. That’s because property lists are easily serialized to and from NSData objects.

Property lists provide a helpful abstract data type. A property list object can point to data (NSData), strings (NSString), arrays (NSArray), dictionaries (NSDictionary), dates (NSDate), and numbers (NSNumber). When working with collection objects (i.e., arrays and dictionaries) all members and keys must be property list objects as well, that is, data, strings, numbers, and dates as well as embedded arrays and dictionaries.

While that seems limiting, you can transform most structures and objects to and from strings. For example, you can use the built-in NSStringFromCGPoint() or NSStringFromClass() functions, or you can create your own. The following pair of methods extend the UIColor class, providing functionality needed to send color information across a GameKit connection as strings.

image

Once in property list form, you can serialize your data and send it as a single chunk. On receipt, the deserialized data is ready to use. Recipe 12-3 shows the transmit and receivedData: methods that handle this. This code comes from a sample that stores a series of drawing points (a la Recipe 8-9) along with the color used to draw them in an NSDictionary object. You can use the NSKeyedArchiver and NSKeyedUnarchiver classes as well as the NSPropertyListSerialization class shown here.

Recipe 12-3 Serializing and Deserializing Property Lists

image

Get This Recipe’s Code

To get the code used for this recipe, go to http://github.com/erica/iphone-3.0-cookbook-, or if you’ve downloaded the disk image containing all of the sample code from the book, go to the folder for Chapter 12 and open the project for this recipe.

By storing both the points and colors as strings, this data can easily be converted into a form better suited for transmission via GameKit. The source code for this chapter shows these methods in action, demonstrating the full collaborative drawing tool that leverages property list transfers.

Recipe: GameKit Voice Chat

GameKit’s In-Game Voice service lets applications create a walkie-talkie-style voice channel connecting two devices together. You can use this service with iPhones, taking advantage of their built-in speaker and microphone, or with second generation or later iPod touch units by adding an external microphone. The standard iPhone earbuds with their built-in mic work very well with iPod touches, routing the audio into the earbuds and picking up voice input through the microphone.

GameKit as Middleman

The GameKitHelper class introduced in Recipe 12-1 provides all the normal functionality for GameKit connections. To adapt this code for Voice Chat, you need to think of the GameKit communications as a voice chat middleman. GameKit handles the data throughput, both receipt and delivery.

The voice additions, provided by the GKVoiceChatService class, sit outside normal GameKit. Chat services connect into the iPhone’s audio playback and recording system, so Voice Chat can listen to and play back audio. Voice Chat then sends its data through GameKit and plays back the data it receives from GameKit. Figure 12-5 shows this separation of responsibilities.

Figure 12-5 Voice Chat adds a layer outside normal GameKit communications to enable live peer-to-peer audio.

image

Unfortunately, you cannot use the GameKit Voice Chat service over a connection other than Bluetooth. GKVoice expects a GKSession with GKPeers in order to transmit its data. If you need to use voice transmission for another connection style, you’ll have to write that layer yourself.

Implementing Voice Chat

When working with voice, there’s no difference in the way you get started. You display a peer picker and negotiate the connection, as you would normally do with GameKit. The difference arrives once the peer connects. You need to establish the voice chat and redirect the data to and from that service.

Upon connecting to the new peer, set up the voice chat basics. The peer connection method in Recipe 12-4 activates a play-and-record audio session, sets the default chat service client, and starts a new voice chat with that peer. By setting the client property, you ensure that your class receives the voice chat callbacks needed for negotiating data.

Recipe 12-4 Adding In-Game Voice Chat Services

image

image

Get This Recipe’s Code

To get the code used for this recipe, go to http://github.com/erica/iphone-3.0-cookbook-, or if you’ve downloaded the disk image containing all of the sample code from the book, go to the folder for Chapter 12 and open the project for this recipe.

Your primary class must declare the GKVoiceChatClient protocol to do this. When the chat service gathers data through the microphone, it triggers the voiceChatService:sendData:toParticipantID: callback. Here, you can redirect voice data to your normal GameKit session. For a voice-only connection, just send along the data. When your application handles both voice and other data, build a dictionary and tag the data with a key such as @"voice" or When your class receives data through the normal receiveData:fromPeer:inSession:context: callback, the same approaches apply. For voice only, use receivedData:fromParticipantID: to send the data off to the chat service. Voice Chat allows you to mix game audio with in-game voice. For voice-data hybrid applications, deserialize the data, determine whether the packet included voice or regular data, and redirect that data to the appropriate recipient.

Recipe: Using Bonjour to Create an iPhone Server

Although GameKit is built around Bonjour, sometimes you’ll want to use Bonjour directly. For example, you might build an iPhone service that connects to a Macintosh-based client. (Bonjour has been ported to both Windows and Linux, but those platforms fall outside the scope of this book.)

Apple has provided bounteous quantities of Bonjour sample code, and Recipe 12-5 takes advantage of this material. The recipe uses Apple’s ready-supplied TCPServer and TCPConnection classes to broadcast a Bonjour service and respond to external connections.

Recipe 12-5 Providing a Bonjour Service

image

image

image

image

image

Get This Recipe’s Code

To get the code used for this recipe, go to http://github.com/erica/iphone-3.0-cookbook-, or if you’ve downloaded the disk image containing all of the sample code from the book, go to the folder for Chapter 12 and open the project for this recipe.

This code is built around the basic image-picking sample from Recipe 7-1. Instead of just selecting an image, this recipe serves that image out via a Bonjour data connection.

The handshake process starts by establishing a new TCPServer and setting its delegate. The viewDidLoad method starts the service using the current run loop and announces a "PictureThrow" service. When an external client connects, the server:didOpenConnection: callback accepts the connection and sets its TCPConnection delegate. The difference between the server delegate and the connection delegate is that the server is responsible for listening for new connections, and the connection is responsible for sending and receiving data.

As with GameKit, the user decides whether to accept a new connection. The server:shouldAcceptConnectionFromAddress: connection delegate method returns a Boolean value, allowing or denying the connection. Figure 12-6 shows the dialog displayed by Recipe 12-5 when a new connection is proposed.

Figure 12-6 Emulating GameKit, Recipe 12-5 allows the user to decide whether to accept or reject a remote connection.

image

After accepting the connection, the connection delegate receives a connectionDidOpen: callback. Here, the application finally sends the data to the client and then closes the connection with invalidate. This allows the client to implement a push-to-request-data button. Each press of that button initializes a new connection, and thus a new data request.

The data sent is the currently selected image. The user can update this image choice by clicking Choose Image (using a standard image picker) or Camera (to snap a photo).

As you can see, the code in this recipe is far more concerned with handling the image selection choices than the simple hooks into the Bonjour service. Just a few methods and callbacks provide a complete suite of data server connectivity.

Note

The code for Apple’s classes has been included with the sample that accompanies this chapter.

Recipe: Creating a Mac Client for an iPhone Bonjour Service

Apple’s Bonjour sample code works with both iPhone and Macintosh applications with just a few changes needed. Notably, you need to drop any reference to the CFNetwork framework, replacing that with the AppKit framework for Mac. Recipe 12-6 provides a Macintosh client to demonstrate how to use the client side of the Bonjour sample code. This meshes with the server from Recipe 12-5.

Recipe 12-6 Providing a Bonjour Client

image

image

image

Get This Recipe’s Code

To get the code used for this recipe, go to http://github.com/erica/iphone-3.0-cookbook-, or if you’ve downloaded the disk image containing all of the sample code from the book, go to the folder for Chapter 12 and open the project for this recipe.

There’s nothing intrinsically specific about the roles and the platforms chosen. A Mac could just as easily provide a service and an iPhone its client. Since an iPhone/iPhone client/server pair is best implemented with GameKit, this recipe demonstrates how to cross platforms and use Bonjour for mobile desktop communication.

Because this is an iPhone book and not a Macintosh development book, Recipe 12-6 limits itself to the methods specific to the Bonjour communications. If you want to see how the rest of the application is built (and, especially, how to rectify the UIImage data into a properly oriented NSImage), please refer to the sample code that accompanies this book. Also refer to Recipe 12-8, which implements both server and client for iPhone. Figure 12-7 shows the Macintosh client application, as it was used to capture the screenshot used in Figure 12-6.

Figure 12-7 The Macintosh Bonjour client shown here was used with the iPhone Bonjour server from Recipe 12-5 to capture some of the screenshots used to illustrate this book.

image

A Bonjour client begins by browsing for services. Since the service on the iPhone is provided by Bonjour and not GameKit, its name is known in advance of compilation and testing, namely @"PictureThrow". This is the same name used in Recipe 12-5 by the server.

The NSNetServiceBrowser class provides the ability to find a given service type. Its delegate receives a netServiceBrowser:didFindService:moreComing: callback when a match appears. The delegate can then stop the browser and begin to resolve the service.

Resolving a service transforms a service name into an actual IP address. The same TCPConnection class used in Recipe 12-5 allows the Bonjour client to request data from the server. Its connection:didReceiveData: callback delivers that data.

Connections can close for three reasons. First, the data transferred over successfully and the host service closed it deliberately. Second, the user may have denied the connection. Third, the application might have lost its connection by quitting or moving out of range. The same connectionDidClose: callback must handle all three cases.

In this recipe, this callback sets a Boolean value for success. When the connection closes, the connectionDidClose: callback method checks that value. If the data transfer did not succeed, the user is told that the connection was denied or lost.

Recipe: Working Around Real-World GameKit Limitations

Although GameKit is built on Bonjour, it isn’t meant to provide the same kind of general use data transfer capabilities displayed in the previous two Bonjour-only recipes. GameKit Bluetooth prefers small data packets, preferably under 1,000 bytes each. GKSession objects cannot send data over 95 kilobytes. When you try, the sendDataToAllPeers:error: method fails, returning a Boolean value of NO.

Recipe 12-7 addresses this problem by checking for data length before queuing any send requests. Short data can be shared; long data is denied. To provide a test bed, this recipe works with the iPhone’s built-in pasteboard.

Recipe 12-7 Sharing the iPhone Pasteboard over GameKit

image

image

image

Get This Recipe’s Code

To get the code used for this recipe, go to http://github.com/erica/iphone-3.0-cookbook-, or if you’ve downloaded the disk image containing all of the sample code from the book, go to the folder for Chapter 12 and open the project for this recipe.

In the real world, you’d likely split up long data items into short bursts and send them using reliable transfer. Reliable transmission ensures that data arrives and does so in the same order that it was sent. You can implement checksumming and other standard network approaches to ensure your data arrives properly. (You might alternatively consider programming a custom Bonjour WiFi service or using Internet server connections for more intense data transfer needs.)

This recipe provides a jumping off point for testing file size elements in the GameKit world. You are welcome to expand this code to explore file decomposition and reconstruction on your own.

Using the iPhone Pasteboard

Pasteboards, also known as clipboards, provide a central OS feature for sharing data across applications. Users can copy data to the pasteboard in one application, switch tasks, and then paste that data into another application. Cut/copy/paste features appear in most operating systems and are new to the iPhone, having debuted in the 3.0 firmware.

The UIPasteboard class offers access to a shared iPhone pasteboard and its contents. As with Macs and Windows-based computers, you can use the pasteboard to share data within an application or between applications. In addition to the general shared system pasteboard, the iPhone offers both a name finding pasteboard and application-specific pasteboards to better ensure data privacy. This snippet returns the general system pasteboard, which is appropriate for most general copy/paste use.

UIPasteboard *pb = [UIPasteboard generalPasteboard];

Storing Data

Pasteboards can store one or more entries at a time. Each has an associated type, using the Uniform Type Identifier (UTI) to specify what kind of data is stored. For example, you might find public.text (and more specifically public.utf8-plain-text), public.url, and public.jpeg among common data types used on the iPhone. The dictionary that stores the type and the data is called an item, and you can retrieve an array of all available items via the pasteboard’s items property.

Query a pasteboard for its available types by sending it the pasteboardTypes message. This returns an array of types currently stored on the pasteboard.

NSArray *types = [pb pasteboardTypes];

Pasteboards are specialized for several data types. These are colors, images, strings, and URLs. The UIPasteboard class provides specialized getters and setters to simplify handling these items. Because Recipe 12-7 provides a general pasting tool, only strings are demonstrated with a specialized call, that is, setString.

Retrieving Data

Retrieve data using dataForPasteboardType:. This returns the data from the first item whose type matches the one sent as the parameter. Any other matching items in the pasteboard are ignored. Should you need to retrieve all matching data, recover an itemSetWithPasteboardTypes: and then iterate through the set to retrieve each dictionary. Recover the data type for each item from the single dictionary key and the data from its value.

UIPasteboard offers two approaches for pasting to the pasteboard. Use setValueForPasteboardType: for Property List objects. (See the discussion earlier in this chapter about these objects.) For general data, use setData:forPasteboardType: as is used in this recipe. When pasteboards are changed, they issue a UIPasteboardChangedNotification, which you can listen into via a default NSNotificationCenter observer.

Responsible Pasteboarding

Recipe 12-7 provides several checks before sending, retrieving, and copying pasteboard data. Users must confirm that they intend to share data of a given type. When receiving data, they must authorize the application to copy the data to the general system pasteboard. This approach ensures that proactive user effort must take place before performing these actions.

Recipe: iPhone to iPhone Gaming Via BonjourHelper

If you’re willing to forgo GameKit’s Bluetooth and work with WiFi, you can duplicate many of GameKit’s features on all iPhones including the older first generation units. Recipe 12-8 introduces BonjourHelper. It was designed to mimic GameKitHelper from Recipe 12-1. That recipe established its connection by setting a session identifier, a data delegate, and assigning a view controller.

image

Recipe 12-8 BonjourHelper Provides GameKit-like Connectivity over WiFi

image

image

image

image

image

image

image

image

image

Get This Recipe’s Code

To get the code used for this recipe, go to http://github.com/erica/iphone-3.0-cookbook-, or if you’ve downloaded the disk image containing all of the sample code from the book, go to the folder for Chapter 12 and open the project for this recipe.

Substituting BonjourHelper for GameKitHelper requires very few programming changes. It uses the same initialization steps, and the data delegate receives an identical set of callbacks. You do need to omit the space in the session ID, a step that isn’t needed in GameKit. GameKit encrypts its session IDs to produce a guaranteed no-space proper Bonjour identifier. BonjourHelper’s plain-text approach means spaces are off-limits. Limit your session ID names to simple alphanumeric text with 14 characters or fewer. Refer to RFC 2782 service types (http://www.dns-sd.org/ServiceTypes.html) for details. The BonjourHelper code transforms the session ID into a standard Bonjour identifier (i.e., _typingtogether._tcp.).

image

That’s not to say that the functionality and implementation are identical. With BonjourHelper, both units must be on the same network. You lose the pretty GameKit peer connection controller sequence shown in Figures 12-2, 12-3, and 12-4. Instead, BonjourHelper provides a simple alert, as shown in Figure 12-8. Beyond that, BonjourHelper basically provides the same peer-to-peer connectivity and data flow as GameKit.

Figure 12-8 The custom BonjourHelper class provides a simpler connection interface than GameKit.

image

Registering Bonjour Names and Ports

You should register any Bonjour names you plan to use for commercial release with the DNS Service Discovery organization. Registration ensures that your service names and protocols will not overlap or conflict with any other vendor. A list of currently registered services is maintained at http://www.dns-sd.org/ServiceTypes.html.

These names must conform to the RFC 2782 standard. Submit your protocol name to [email protected]. Include the up-to-14-character name of the Bonjour service, a longer descriptive name, the contact information (name and e-mail address) of the person registering the service, and an information page URL. Specify the transportation protocol (i.e., _tcp or _udp) and a list of any TXT record keys used. (An example that uses and displays TXT data follows at the end of this chapter.)

It may take some time for the volunteers at the dns-sd.org site to process and respond to your query. Delays on the order of weeks are not uncommon. You may need to resubmit, so keep a copy of all your information.

If you plan to use a fixed port (most Bonjour implementations randomly pick a port at runtime to use), you’ll want to submit an application for a registered port number with IANA, the Internet Assigned Numbers Authority, as well. IANA provides a central repository for port registrations and will, at some time, be merged with the dns-sd registry. IANA often takes a year or longer to finish registering new protocol port numbers.

Note

Apple maintains a list of official OS X Bonjour service types in its Technical Q&A QA1312 document, which you can find at http://developer.apple.com/mac/library/qa/qa2001/qa1312.html.

Duplex Connection

For simplicity, BonjourHelper works by establishing a duplex connection. Each device provides both a client and a host. This avoids any issues about trying to get two peers to negotiate with each other and assume the proper server and client roles without both of them ending up as client or server at the same time.

When resolving addresses, the helper ensures that the unit will not connect to itself. It demands a unique IP address that doesn’t match the local one. If the incoming address does match, it just continues looking. The host needs no such checks; outgoing client connections are limited to foreign addresses.

When the helper has established an outgoing connection and accepted an incoming one, it stops looking for any further peers and considers itself fully connected. The helper updates the Connect/Disconnect button if a view controller has been set.

Reading Data

Unlike Recipe 12-6, Recipe 12-8 cannot use a simple read loop, that is, request data, read it, and repeat. Reading data is blocking. A read loop prevents an application from handling its server duties at the same time as its client duties.

Instead, this class uses the nonblocking hasDataAvailable check before asking for new data. A delayed selector adds a natural interval into the poll allowing each host time to update and prepare new data before being barraged by a new request.

Closing Connections

Connections can break in several ways. Users can quit an application, they can press the Disconnect button in the sample, or they can move out of range of the connection. BonjourHelper checks for disconnects exclusively from the server point of view. This simplifies its implementation, assuming that a lost client equates to a lost host and avoids the issue of multiple user notifications, i.e., “Lost connection to server” and “Lost connection to client” for both ends of the duplex connection.

Note

For space considerations, this listing of Recipe 12-8 omits a number of basic IP utilities, including stringFromAddress:, addressFromString:address:, and localIPAddress. These methods are included in the sample code that accompanies this chapter and are discussed further in Chapter 13, “Networking.”

Creating an “Online” GameKit Connection

In the GameKit world, “online” currently means any valid connection style other than Bluetooth. You might use a local WLAN network to connect to another device on the same network or connect through WWAN (i.e., the cellular service) or WiFi to a remote Internet-based host. GameKit takes you only so far as the dialog shown in Figure 12-9. By selecting Online, your user depends on you to create a custom connection to another device or service.

Figure 12-9 The Online GameKit connection means “bring your own networking.” (Please note that the Send button shown on the keyboard here is a standard Return key. In this recipe, data is sent as it is typed.)

image

You create this two-item dialog by supplying the online option to the peer picker mask. In all other ways, there’s no change in how you create and present a standard GameKit peer picker controller.

image

Catch the user selection in the peerPickerController:didSelectConnectionType: callback. You can assume that if the user selected Nearby that all the handshaking dialogs are taken care of for you. Should the user select Online, however, it’s up to you to move things to the next step. You need to dismiss the picker and display the next stage of the connection task. Here, control passes away from the peer picker. BonjourHelper from Recipe 12-8 is initialized, and its connection begun. Instead of the gray peer picker dialog, BonjourHelper’s standard blue alert appears.

image

As Recipe 12-9 demonstrates, almost no changes are needed from the BonjourHelper side of things. The Connect button on the navigation bar must point back to GameKit’s connect method, not to BonjourHelper’s. This ensures that users can finish a Bonjour connection and then move on to a Bluetooth one without restarting the application.

Recipe 12-9 Updating the Macro Code to Use GameKit’s Version of Connect

image

Get This Recipe’s Code

To get the code used for this recipe, go to http://github.com/erica/iphone-3.0-cookbook-, or if you’ve downloaded the disk image containing all of the sample code from the book, go to the folder for Chapter 12 and open the project for this recipe.

Additionally, you might think the Info.plist file needs a UIRequiresPersistentWiFi key set to the Boolean value of true. Avoid doing this. Instead, check for WiFi only when you are ready to attempt to create a WiFi connection, i.e., when the user clicks Online. GameKit Bluetooth connections don’t need persistent WiFi although standard Bonjour ones do. Don’t require your users to connect to a (possibly nonexistent) WiFi service when Bluetooth is sufficient for Nearby gaming.

One More Thing: Scanning for Services

The NSNetServiceBrowser class is not limited to a single predefined service. You can adapt the browser code to search for all available services that an iPhone can communicate with. Recipe 12-10 extends the service browsing and resolution samples used in this chapter to find all active Bonjour service providers, displaying them as a list, as shown in Figure 12-10. Tapping on a list cell moves to a service detail page.

Figure 12-10 The iPhone can scan for and detect all local Bonjour services. If you are familiar with the built-in protocols, you can develop applications that communicate with those services.

image

Recipe 12-10 Bonjour Scanner

image

image

image

This recipe works by using DNS-based service discovery by searching for _services._dns-sd._udp. This returns a list of records with service types. A second round of service discovery, iterating through this list, produces the actual services shown in Figure 12-10.

As each service is found, it is resolved to produce a list of service IP addresses and its TXT record data solicited (via startMonitoring) for further service details. When testing this recipe, try viewing the data for an attached printer service (such as the Brother HL-5040 shown in Figure 12-10) to produce a particularly detailed set of service info.

Beyond the TXT callback, which was not used in previous recipes, the methods shown in this recipe mirror their earlier uses in the chapter.

Summary

GameKit offers an exciting new player in the iPhone development arena. Its easy-to-use ad hoc Bluetooth connections make it simple for you to deliver applications that communicate outside traditional networks. In this chapter, you saw how to build those connections and produce real-time data transfers that allow games and other applications to coordinate information between separate devices. You also saw examples of iPhone and Mac-based Bonjour applications that don’t rely on GameKit’s proprietary connections and examples of GameKit’s “bring your own technology” Online connections. Here are a few last minute thoughts on these technologies:

• Although Apple has not yet delivered GameKit for Macintosh, it’s probably on their to-do list. GameKit is an exciting new technology and it’s sure to grow.

• Although a full Internet-based GameKit connection fell outside the scope of this chapter, I expect to see great things from connected games developers. With proprietary networking, users will be able to connect iPhones together to play no matter where players are located, nearby and online. It’s a real handheld gaming revolution from the point of view of you, the developer, and probably the user as well.

• When working with Voice Chat, remember that nearby users may produce sound loops creating feedback distortion unless they use headsets. Plus, people sitting 10 feet apart from each other can easily talk without the use of technology.

• Bonjour runs natively on Windows. Do a Google search for mDNSResponder for details.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
18.189.178.190