Sunday, March 25, 2012

Connecting external devices to iPhone

Recently I heard about some opportunities involving creating apps running on iPhone, meant to talk to specialized hardware devices, so that got me thinking about ways in which the iPhone can connect and talk to external devices.

First category is wired connections. Here the choices are simple, since there's only a single connection port, the docking port (also used to charge the device). In order to be able to communicate through that port to the external device you'd need to be enrolled in Apple's MFi program. However there's another option, that doesn't require participation in this program, and uses the audio jack available on iPhone. That jack outlet provides connectivity to both headsets and microphone, so that gives you access to an input/output channel. Your external device should be capable to generate the required voltage that applied on the microphone pin would be detected in the iOS app as a sound, and should be able to understand the audio output received on the headset pins, as generated by the app. This is nothing new, as some people have already implemented it as either dedicated solutions like Square, or more general-purpose solutions like HiJack. For low data rates you may be able to get by with a simple detection technique, whereas for higher data rates you may need to implement a full-blown modem.

The other category is wireless connections. Here your best bet may be using the network connectivity (either WiFi or 3G) to connect to a server over TCP/IP or higher protocols. That's probably the approach that involves the least effort. Another appealing avenue is to use Bluetooth, however the public SDK only allows you to talk to another iOS on Mac device, via Game Center, or Bonjour. If you wanted to talk to your specialized external device over bluetooth, you would also need to be enrolled in the MFi program, and use the External Accessory Framework to talk to the device.

Another wireless channel, albeit one-way, is to use the camera to interpret either a pattern like QR code or barcode, or a pulsating light. Another one-way channel in the opposite direction is the screen of the iPhone. You could have your app display a sequence of patterns that could be interpreted by an external devices. Some of you greybeards may remember a version of Windows transferring data to a Timex watch in that manner.

The accelerometer falls in a category of its own, since it's not quite wired, neither wireless. It's obviously an input channel into the app, whereby the app can detect movement, if the iPhone is physically attached to an external device. That could be used for a monitoring/tracking the movement of physical goods, either intentional or due to theft or mishandling.

That pretty much sums it up. If you can imagine other ways of communication between the iPhone and external devices, please comment.


Wednesday, March 14, 2012

Using gdb to debug Objective-C code

I've been using gdb for many years, initially for debugging code for embedded software, written in C/C++ and running on specialized hardware, and more recently for software meant to run on iOS devices, written in Objective-C. Knowing the way to use it for C/C++ served me well, as most of that stuff is applicable to Objective-C too, including the most commonly used commands. I was aware of, and had been using some commands specific to Objective-C, like print-object, but not much beyond that.

At some point I came across a presentation that informed me about the ability to evaluate and print the value of complex expressions, including method calls. Of course you have to be mindful of possible side-effects, but that's a pretty powerful capability to have in the middle of a debug session.

So recently I had to inspect the number of entries in a dictionary. Even though I could have used the Xcode's ability to hover over variable's name and "inspect" some of its content, which for NSDictionary works really well, in that it shows how many key/value pairs it contains (in my case 10), I thought I should use the command-line gdb to find that out. So I did:


(gdb) po [existentMetadataForPrefix count]
0xa does not appear to point to a valid object.


Hmm, not what I expected. The 0xa mentioned there in hex is 10 in decimal, so the value appears OK. It looks like po (shorthand for print-object) tries to send a message to the object pointed by 0x10. In fact that message is "description", so what I get is fair enough. Obviously I shouldn't be using print-object on an integer value (NSUInteger) returned by count.

To print primitive values in C/C++ one would use straight print. Doing so yields:


(gdb) p [existentMetadataForPrefix count]
Unable to call function "objc_msgSend" at 0x1b1e08c: no return type information available.
To call this function anyway, you can cast the return type explicitly (e.g. 'print (float) fabs (3.0)')

What the ... ? This doesn't make any sense. That 0x1b1e08c address is not the address of existentMetadataForPrefix, as shown by:

(gdb) p existentMetadataForPrefix
$2 = (NSMutableDictionary *) 0xf520c10

Then, what's at that address ? Can find out, like so:

(gdb) x 0x1b1e08c
0x1b1e08c : 0x08244c8b

Huh, looks like that might be the actual address of the function objc_msgSend. Can that be ? Let's double-check:

(gdb) x objc_msgSend
0x1b1e08c : 0x08244c8b

It is indeed, so what's going on here ? AFAIK that objc_msgSend function is called when a message like count is sent to an object like existentMetadataForPrefix.  Let's set a breakpoint:

(gdb) b objc_msgSend
Breakpoint 3 at 0x1b1e08c

And try again:

(gdb) p [existentMetadataForPrefix count]
Unable to call function "objc_msgSend" at 0x1b1e08c: no return type information available.
To call this function anyway, you can cast the return type explicitly (e.g. 'print (float) fabs (3.0)')

Nope, the breakpoint doesn't get hit. Ok, at this point it's pretty clear that I need to learn more about how to use gdb with Objective-C. In doing so I discovered this excellent blog post that explains that:
We can also print the result of messages which return primitive values. However, gdb is not smart enough to figure out the return type in this case, so we have to tell it by adding a cast to the expression:
So all I had to do was this:


(gdb) p (int)[existentMetadataForPrefix count]
$1 = 10


Which, in all fairness was in fact suggested by gdb, but was shrouded in confusion, at least for me, by the mention of objc_msgSend.

I hope this would help you in case you run into a similar problem, and I can definitely recommend checking out that blog post. It's full of very interesting gotchas on this topic.


Tuesday, March 06, 2012

Following through the examples in the "Agile Web Application Development with Yii 1.1 and PHP5" book


I just started reading the Agile Web Application Development with Yii 1.1 and PHP5 book. Chapter 3 explains the virtues of TDD (test-driven development) and tries to walk us through setting up a development environment that supports automated testing through PHPUnit and Selenium.

At the same time, right at the beginning of the chapter 2 there's this note:

"There are several versions of Yii from which to choose when downloading the framework. We will be using version 1.1.2 for the purposes of this book, which is the latest stable version as of the time of writing. Though most of the sample code should work with any 1.1.x version of Yii, there may be some subtle differences if you are using a different version. Please use 1.1.2 if you are following along with the examples."

This clearly makes sense - it's better to use the same version of the tools, as the author, if we're expecting to see the same results as shown in the book. The realty is that by the time some readers, like myself, get to read a book some of the software tools, and their dependencies, have evolved to the point where what they produce does not even remotely match what the version that the author used produced.

So I followed the advice, got Yii version 1.1.2, and I tried to follow along. It wasn't a smooth ride. I'll try to walk you through some of the pitfalls that I experienced, and the ways I overcame them, hopefully helping you if you're experiencing similar problems.

First problem appeared when I tried:

sudo pear install phpunit/PHPUnit 

shown on page 45

I got an error telling me that the version of pear I've been using was too old (had been using a MAMP setup common on Macs, which may have grown old, at version 1.*). Even though I don't have the exact message, based on my googling history it said something along these lines: "requires PEAR Installer (version >= 1.9.4)".

The solution for that was to upgrade pear, like so:

sudo pear upgrade pear

and do a
pear --version 
to check that the version went up.

However, if we follow what the book says, literally, when it tells us to install  phpunit/PHPUnit, like so:

sudo pear install phpunit/PHPUnit

we'll get the latest version of PHPUnit, which, of course, doesn't match the older version of the Yii framework, which we were advised to install. This opens a whole can of worms.

First off, when you try to run the functional test cases, by doing:
phpunit functional/SiteTest.php 
you'll be informed that:

PHP Warning:  require_once(PHPUnit/Extensions/SeleniumTestCase.php): failed to open stream: No such file or directory

If you check the location of PHPUnit install, which on my system is at /Applications/MAMP/bin/php5/lib/php/PHPUnit, you'll see that indeed that file is missing. How so ? Turns out that most recent versions of PHPUnit and Selenium now get that file installed by doing:

sudo pear install phpunit/phpunit_selenium

but don't do that, because you'd just be wasting your time ! The thing is, you don't want to use the most recent PHPUnit, because if you do, as you would be if you followed the instructions in the book, you'll next hit this error:

Warning: require_once(PHPUnit/Framework.php): failed to open stream: No such file or directory in .../framework/test/CTestCase.php on line 11

Yeah, Yii version 1.1.2 requires PHPUnit/Framework.php, which is not present in the most recent versions of the PHPUnit.

So, "yes it is" - a complete mess, that is. To save your sanity, uninstall the newer versions of the tools:

sudo pear uninstall phpunit/PHPUnit_selenium (if you installed it already)
sudo pear uninstall phpunit/PHPUnit

Then install the version that matches the one used in the book:

sudo pear install --alldeps phpunit/PHPUnit-3.3.17

This should bring you to clear sailing through chapter 3.