Wednesday, May 5, 2010

Touch Attack! Games 1.2 will push your brains to limits!

ProRattaFactor announced release of Touch Attack! Games best memory and reflex family game for iPhone, iPod touch, and iPad. Touch Attack offers updated version 1.2 of OpenFeint that allows users to compete in “global leader boards” and win “achievements.
The app consists of three mini game types-Touch Attack, Memory Attack, and Match Attack. Each testing your hand-eye coordination, memory and matching capabilities. In Touch Attack you can only touch characters with targets on them, while in Memory Attack you have to remember in which order you have to press colored buttons. The last-Match Attack where you've got to remember which characters came from which hole and in what order is locked but you're able to unlock it by winning tickets. With tickets you can also unlock sound effects, music tracks and additional themes.
Touch Attack is most addicting game play pushing players reflexes and brains to their limits. Just check your memory skills or how fast your brain reacts. App is well designed with smooth interface making the best family game more attractive.

Feature Highlights:
* Diverse style of gameplay between mini games: (Ranging from quick reflexes, memory pattern, and spatial memory)
* Touch Attack! Games is OpenFeint enabled (Global leader boards and achievements)
* Fun graphics, sound effects, and music
* Continue game at current round after interruption or when closing application
* Long replay value! Trade in-game tickets for new themes, sounds effects, and music
Requirements:
* iPhone, iPod touch, and iPad
* Requires iPhone OS 3.1.2 or later
* 9.8 MB


Pricing and Availability:
Touch Attack! Games 1.2 is only $0.99 USD and available worldwide exclusively through the App Store in the Games category.

Tuesday, May 4, 2010

Voodo 2 is new Magic App for your iPhone!

Developers of GTD/todo app for iPhone and iPod touch that is known for it's simplicity and unique features, are now offering new universal Magic app called Voodo 2 with a 50%-off price in the App Store. Voodo 2 is updated version of GTD/todo but with additional various Magic capabilities such as: Sync with Google Calendar automatically, wonderful Tag System, Task Search, Quick Add, Contacts Integration, Geo Locations, and Nearby Tasks and many more. With this additional set of features Voodo 2 Magic maintains simplicity and clear, intuitive user interface of it's previous version.
Independent developer Ling Wang (from China) and the founder of I Wonder Phone found GTD/todo app a little complicated so the idea of new version with more features and existing simplicity was best solution. It should get things done and the app was called Voodo as it was created to help people to get things done like Voodoo Magic.

Features:
* Sync with Google Calendar non-intrusively and automatically
* Due Task Push Reminders via Google Calendar Notifications
* Wonderful Tag System, more flexible than Folders
* Task Search
* Quick Add
* Contacts Integration
* Geo Locations, more powerful than Contexts
* 4 Browsing Modes: Time Mode, Priority Mode, Location Mode, Nearby Mode
* Forget the Edit/Done button - just tap any field to edit
* Tap numbers to call, links to go, addresses to mail, in the Smart Note
* Customizable "Now"
* Show the number of Now tasks or all to-do tasks in the app icon
* Flexible scheduling including timeless tasks and all-day tasks
* Undo-able task check-off and task deletions
* Comprehensive Manual with illustrations


Requirements:
* iPhone, iPod touch, and iPad
* Requires iPhone OS 3.0 or later
* 2.1 MB

Pricing and Availability:
Voodo 2.0 is normally $6.99 (USD) exclusively through Apple iTunes App Store in the Productivity category. But it is now on sale at $3.99 (USD) for a limited time to celebrate the release of the great new version. 

Monday, May 3, 2010

NSStream: TCP and SSL

In Chapter 9 of More iPhone 3 Development, we showed how to use Bonjour and NSInputStream / NSOutputStream to do network communications. In that case, it was for making a simple networkable game, but the technique is essentially the same for any kind of low-level network communications.

On Mac OS X, NSStream, the superclass of both NSInputStream and NSOutputStream, has a class convenience method called getStreamsToHostNamed:port:inputStream:outputStream which creates a stream pair to a specified remote host. For some reason unbeknownst to me¹, Apple chose not to include this convenience method in the iPhone SDK. The underlying functionality is still there in the iPhone SDK, they just removed the method that make it easy to establish the streams. Apple rectified that situation shortly thereafter by issuing Technote TN QA1652, which gives a category on NSStream that restores the missing functionality.

Using this method really couldn't be easier. In our TicTacToe application, we could have established an OnlineSession object to another game specified by IP address and port rather than through Bounjour, like so:

    NSInputStream *is;
NSOutputStream *os;
[NSStream getStreamsToHostNamed:address
port:port
inputStream:&is
outputStream:&os
]
;
OnlineSession *session = [OnlineSession initWithInputStream:is outputStream:os];

Of course, there would still need to be an instance of TicTacToe running on the remote client listening for connections. With internet play, you're generally going to need some way of finding the address and port of the machine to connect to, but if a game is running and isn't firewalled, that will connect you to it. In fact, you can use this same technique for pretty much any kind of low-level network communications.

One thing that's not obvious, however, is how you can encrypt your network communications using SSL. It's pretty easy, but there is a significant gotcha that's worth mentioning. Let's look at OnlineSession's initWithInputStream:outputStream: and then add SSL support for it. Here is the original:

- (id)initWithInputStream:(NSInputStream *)theInStream outputStream:(NSOutputStream *)theOutStream
{
if (self = [super init]) {

inStream = [theInStream retain];
outStream = [theOutStream retain];

[inStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

inStream.delegate = self;
outStream.delegate = self;

if ([inStream streamStatus] == NSStreamStatusNotOpen)
[inStream open];

if ([outStream streamStatus] == NSStreamStatusNotOpen)
[outStream open];

packetQueue = [[NSMutableArray alloc] init];

}

return self;
}

That version creates a plaintext, unencrypted connection to the remote server. The way that we enable SSL encryption is to simply use setProperty:forKey: on both streams, setting the key NSStreamSocketSecurityLevelKey to a value that specifies the version of SSL to use. If you want to tell NSStream to use the highest version supported in common with the remote connection, specify NSStreamSocketSecurityLevelKey. That's what you'll usually want. Here's a new version of our init method that lets us specify whether to use SSL or not:
- (id)initWithInputStream:(NSInputStream *)theInStream outputStream:(NSOutputStream *)theOutStream useSSL:(BOOL)useSSL 
{
if (self = [super init]) {

inStream = [theInStream retain];
outStream = [theOutStream retain];

[inStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

inStream.delegate = self;
outStream.delegate = self;

if ([inStream streamStatus] == NSStreamStatusNotOpen)
[inStream open];

if ([outStream streamStatus] == NSStreamStatusNotOpen)
[outStream open];

packetQueue = [[NSMutableArray alloc] init];

if (useSSL)
{
[inStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL
forKey:NSStreamSocketSecurityLevelKey
]
;
[outStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL
forKey:NSStreamSocketSecurityLevelKey
]
;

}

}

return self;
}

And, we're done! Well, at least insofar as the NSStream documentation would have you believe. This code will work if everything is perfect. However, by default, SSL support in NSStream is a little paranoid. It won't, for example, use a self-signed certificate or an expired certificate to establish a secure connection. NSStream does a number of validity checks when establishing the secure connection, and if they don't all pass, the streams appear to be valid, but no data gets sent or received. This is somewhat frustrating, and it could be there's a way to find out when the secure connection failed, but I haven't been able to find it in the documentation, or using Google. There is an error domain declared for these errors (NSStreamSocketSSLErrorDomain), but in my experimentation, no errors gets generated, the streams even accept bytes for transfer, but nothing happens².

When it comes to security, a little paranoia is good, but there are many situations where all you want is the encryption provided by SSL, and you don't really care about whether the root certificate is valid (the root certificate identifies the certificate authority that issued the certificate). If you're doing an e-Commerce transaction, then you almost certainly want to make sure the certificate is valid and was issued by a valid authority. But if all you're trying to do is provide a little privacy from prying eyes, a self-signed certificate is often perfectly acceptable.

Unfortunately, NSStream doesn't provide a way to say "don't check the root certificate". Fortunately, CFStream does, and remember: CFStream and NSStream are toll-free bridged.

The way we can turn off these validity checks is by creating an instance of NSDictionary. In this dictionary, we'll specify Boolean values for a number of system-defined key values like kCFStreamSSLAllowsAnyRoot, kCFStreamSSLAllowsExpiredCertificates, and kCFStreamSSLValidatesCertificateChain, specifying either YES or NO as appropriate to your situation. SSL also does verification on the name of the remote peer. We can turn that off by overriding the peer name using the key kCFStreamSSLPeerName and setting it to null (well, technically, kCFNull).

Once we have this dictionary, we can feed it to our stream pair by casting them to their CF counterpart using CFReadStreamSetProperty and CFWriteStreamSetProperty. If we wanted to turn off all the validity checks and allow any certificate signed by any root certificate, we would do this:

- (id)initWithInputStream:(NSInputStream *)theInStream outputStream:(NSOutputStream *)theOutStream useSSL:(BOOL)useSSL 
{
if (self = [super init]) {

inStream = [theInStream retain];
outStream = [theOutStream retain];

[inStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];
[outStream scheduleInRunLoop:[NSRunLoop currentRunLoop] forMode:NSDefaultRunLoopMode];

inStream.delegate = self;
outStream.delegate = self;

if ([inStream streamStatus] == NSStreamStatusNotOpen)
[inStream open];

if ([outStream streamStatus] == NSStreamStatusNotOpen)
[outStream open];

packetQueue = [[NSMutableArray alloc] init];

if (useSSL)
{
[inStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL
forKey:NSStreamSocketSecurityLevelKey
]
;
[outStream setProperty:NSStreamSocketSecurityLevelNegotiatedSSL
forKey:NSStreamSocketSecurityLevelKey
]
;

NSDictionary *settings = [[NSDictionary alloc] initWithObjectsAndKeys:
[NSNumber numberWithBool:YES], kCFStreamSSLAllowsExpiredCertificates,
[NSNumber numberWithBool:YES], kCFStreamSSLAllowsAnyRoot,
[NSNumber numberWithBool:NO], kCFStreamSSLValidatesCertificateChain,
kCFNull,kCFStreamSSLPeerName,
nil
]
;

CFReadStreamSetProperty((CFReadStreamRef)inStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);
CFWriteStreamSetProperty((CFWriteStreamRef)outStream, kCFStreamPropertySSLSettings, (CFTypeRef)settings);

}

}

return self;
}

If you're attempting to use SSL, but your network connection doesn't seem to do anything, try turning off the validity checks one at a time to see if that changes your result, or use the code above to turn them all off, which should at least tell you if the problem you're having is a failure to establish a secure connection. Be careful shipping applications with these checks turned



1 The reason is now knownst to me. The original method used NSHost which isn't available on the iPhone - thanks Chris!
2 If anyone knows how to determine when this is happening, please let me know and I'll post the information. I'm sure there has to be a way to detect the failure to establish an encrypted connection so you can "fallback" to a less secure option if you want to, but I haven't found it yet