1. 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 1 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.
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 1. Monitoring GameKit
@implementation TestBedViewController
@synthesize textView;
- (void) listenForStderr: (NSTimer *) timer;
{
// Monitor the stderr output for new information
NSString *contents = [NSString
stringWithContentsOfFile:STDERR_OUT];
contents = [contents stringByReplacingOccurrencesOfString:@"\n"
withString:@"\n\n"];
if ([contents isEqualToString:self.textView.text]) return;
[self.textView setText:contents];
self.textView.contentOffset = CGPointMake(0.0f,
MAX(self.textView.contentSize.height -
self.textView.frame.size.height, 0.0f));
}
- (void) viewDidLoad
{
// Establish the GameKit session
[GameKitHelper sharedInstance].sessionID = @"Peeking at GameKit";
[GameKitHelper assignViewController:self];
// Redirect stderr output to file
freopen([STDERR_OUT fileSystemRepresentation], "w", stderr);
[NSTimer scheduledTimerWithTimeInterval:1.0f target:self
selector:@selector(listenForStderr) userInfo:nil repeats:YES];
}
@end
|
2. Recipe: Sending Complex Data Through GameKit
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.
@implementation UIColor (utilities)
- (NSString *) stringFromColor
{
// Recover the color space and store RGB or monochrome color
const CGFloat *c = CGColorGetComponents(self.CGColor);
CGColorSpaceModel csm =
CGColorSpaceGetModel(CGColorGetColorSpace(self.CGColor));
return (csm == kCGColorSpaceModelRGB) ?
[NSString stringWithFormat:@"%0.2f %0.2f %0.2f %0.2f",
c[0], c[1], c[2], c[3]] :
[NSString stringWithFormat:@"%0.2f %0.2f %0.2f %0.2f",
c[0], c[0], c[0], c[1]];
}
+ (UIColor *) colorWithString: (NSString *) colorString
{
// Read a color back from a string
const CGFloat c[4];
sscanf([colorString cStringUsingEncoding:NSUTF8StringEncoding],
"%f %f %f %f", &c[0], &c[1], &c[2], &c[3]);
return [UIColor colorWithRed:c[0] green:c[1] blue:c[2] alpha:c[3]];
}
@end
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 2 shows the transmit and receivedData:
methods that handle this. This code comes from a sample that stores a
series of drawing points 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.
By storing both the points and colors as strings,
this data can easily be converted into a form better suited for
transmission via GameKit.
Recipe 2. Serializing and Deserializing Property Lists
- (void) transmit
{
if (![GameKitHelper sharedInstance].isConnected) return;
NSString *errorString;
// Send a copy of the local points to the peer
// by serializing the property list into data
NSData *plistdata = [NSPropertyListSerialization
dataFromPropertyList:self.points
format:NSPropertyListXMLFormat_v1_0
errorDescription:&errorString];
if (plistdata)
[GameKitHelper sendData:plistdata];
else
CFShow(errorString);
}
- (void) receivedData: (NSData *) thedata
{
// Deserialize the data back into a property list
CFStringRef errorString;
CFPropertyListRef plist =
CFPropertyListCreateFromXMLData(kCFAllocatorDefault,
(CFDataRef)thedata, kCFPropertyListMutableContainers,
&errorString);
if (!plist)
{
CFShow(errorString);
return;
}
// Assign the received data to foreignPoints
self.foreignPoints = (NSArray *)plist;
}
|