RSU

[Thursday, July 9, 2009]

iPhone Silent Mode/Switch state detection using SDK.

1 comments

I wanted my app to "buzz" only when the phone was in silent mode, or in other words, the button was set to vibration. To find this out pragmatically you can use the following code snippet, that I figured out after some struggling with the API.



CFStringRef state;
UInt32 propertySize = sizeof(CFStringRef);
AudioSessionInitialize(NULL, NULL, NULL, NULL);
AudioSessionGetProperty(kAudioSessionProperty_AudioRoute, &propertySize, &state);
if(CFStringGetLength(state) > 0)
NSLog(@"Vibration Off");
else
NSLog(@"Vibration On");

The string returned by querying the property kAudioSessionProperty_AudioRoute can be blank or Speaker. It can also be Lineout depending on if the phone is docked or not. So if its blank, length is 0, and hence no audio route is currently available. i.e. silent mode is active.

[Monday, July 6, 2009]

iPhone Programming: Creating Network Socket Connections

5 comments

My current project involves creating an iPhone application that communicates with another application (hosted on a PC). Ideally, I wanted to do this via Bluetooth, but like a lot of other (useful) stuff, Apple's API doesnt expose it for any communication other than peer-to-peer (iphone to iphone) and audio gateway. So using that was out of question, though my personal side project is to write a program for PC that fools the iphone into thinking that its talking to one of its own. So for now I am using the WiFi/Network connection.

My goal: Create a simple app, that connect to a TCP host, sends "Hello World" and closes the connection.

Being a complete beginner in iPhone development, my first step was check out the Apple's official Networking guides. I found and followed Cocoa Streams how-to. This document is filed under iPhone Reference, and can be accessed from the developer webpage. After hours of struggling with the seemingly simple code, I realized that iPhone OS does not support +getStreamsToHost:. I have filed a bug regarding this document's classification. After some chitchatting and more documentations, I found the correct way to create a connection is to use the CFNetwork framework, which is well explained by Apple here.

Following the sample code, I created an app that uses CFStreams to create a socket connection.



-(IBAction) connect_button {

CFWriteStreamRef writeStream = NULL;

CFStringRef host = CFSTR("10.212.97.159 ");
UInt32 port = 22701;

CFStreamCreatePairWithSocketToHost(kCFAllocatorDefault, host, port, NULL, &writeStream);

CFWriteStreamSetProperty(writeStream, kCFStreamPropertyShouldCloseNativeSocket, kCFBooleanTrue);

if(!CFWriteStreamOpen(writeStream)) {
NSLog(@"Error Opening Socket");
}
else{
UInt8 buf[] = "Hello WOrld!";
int bytesWritten = CFWriteStreamWrite(writeStream, buf, strlen((char*)buf));
NSLog(@"Written: %d", bytesWritten);

if (bytesWritten < 0) {
CFStreamError error = CFWriteStreamGetError(writeStream);
/** How do I print out description? All i get is -1 here. i.e What is perror() equivalent? **/
}

}

CFWriteStreamClose(writeStream);

}


Very simple and familiar looking code, so this should definitely work. Right? Well no it did not. I kept getting an error, which is stored in CFStreamError type. Problem is I had no way of interpreting that error. The CFError type is very well documented, but CFStreamError has a lot of loose ends. I spent quite a time trying to figure out what the error meant so that I could debug my application.

Update: The above code works! I still do not know how to print out the description of CFStreamError, but I found the error when I rewrote the code. The culprit is Line 5. Do you see it? :). Its the whitespace after the IP. Damn! That little error wasted atleast a few hours of my coding time.

Finally, I gave up on apple's way of networking and decided to do it the classic C-style way. I found an amazing library called the SmallSockets that uses the linux style of sockets under the hood, wrapped inside a Obj-C class. Downloaded it, added the headers file in my project and Voila!, it worked! All under 5 minutes!


-(IBAction) connect_button {
Socket *socket;
int port = 22701;
NSString *host = @"10.212.97.159";

socket = [Socket socket];
@try{
[socket connectToHostName:host port:port];
[socket writeString:@"Hello World!"];
//** Connection was successful **//
//[socket retain]; // Must retain if want to use out of this action block.
}
@catch (NSException* exception) {
NSString *errMsg = [NSString stringWithFormat:@"%@",[exception reason]];
NSLog(errMsg);
socket = nil;
}

//** Disconnect **//
[socket close];
}


Though my project is now up and running with 2-way network comm, I still want to get back to the CFNetwork and figure out where I went wrong, and try to implement a bare bone hello world app, the apple way.