Skip navigation

I’ve learned a lot since my last post about writing Python apps for the (jailbroken) iPhone. My current focus is in building a viable UI to house the app that I want to write. This has so far involved heaping servings of UIKit with a dash of CoreGraphics to write a custom dialer control

Through the course of this, I’ve noticed a lot of caveats and gotchas that are not well described in the existing iPhone Python information available on the web. Hopefully this info will be useful for the next iPhone Python dev who is desperately googling for the information they need!

Firstly, Apple is very bad at reorganizing data without thoroughly breaking URLs. The Objective C documentation specifies a compact encoding for representing method signatures, which is used by PyObjC as a guide when marshalling data between Python and the C world. Normally the user of an ObjC class need not worry about these encoded signatures, as Objective-C and Apple’s frameworks provide enough metadata to figure them out automagically. However, some frameworks (such as CoreGraphics and UIKit) export functions which are not part of any Objective-C class, but are instead normal C accessible functions. In the case of CoreGraphics, I presume this is to allow C applications to easily leverage the same API as Objective-C ones.

PyObjC is unable to infer the signatures of these methods, because they are not covered by any Objective-C metadata. This makes sense, since they aren’t Objective-C signals anyway. So in these situations, the functions need to be imported from the Python side using a special method of the ‘objc’ object like so:


objc.loadBundleFunctions(BundleObject, globals(), [("Method1", "Signature1"), ...])

The format for the ‘Signature1’ part is documented by Apple. Several blogs about this subject linked to the information, but Apple reorganized the whole document and left a broad redirect from the old locations to the index of the new version. It took me a very long time to find the information in the new structure, because there was no obvious name for the feature! For the record, they are “encoded signatures” and the process is “signature encoding”. I’m putting the information here to avoid relying on Apple for it.

Signature Encoding Format

A signature consists of a set of characters, which each represent an element of the method’s signature. The elements include the return type, implicit arguments (like the instance pointer and selector for Objective-C messages), and explicit arguments. A number of modifiers are supported as well.

@   - object
i,I - int, unsigned int
:   - method selector (SEL)
@:  - instance and selector, means function is an
instance method (use after return type, before args)
V,v - void
B   - C++ bool (0 = false, other = true)
f   - float
d   - double
*   - charptr
s,S - short, unsigned short
l,L - long, unsigned long
q,Q - long long, unsigned long long
bn  - bit field of n bits
?   - unknown type, void*, function pointers
c   - signed char
C   - unsigned char
^t  - pointer to type t
#   - objc Class
[nt]- array of length n containing type t
{name=t..} - structure, where 't..' is the order and type of fields
(name=t..) - union, where 't..' is the types of union contents

Marshalling Strings
PyObjC accepts normal python strings for char* elements (‘*’). NSString is exposed to Python as a subclass of the standard unicode type, which allows them to be immediately usable. You can also provide any Python string in place of an actual NSString. Getting an *actual* NSString object is hairy however, so it seems the best calling method to use for NSString methods is NSString.methodName_(stringObject, arg1, …)

Subclassing
In Python, you instantiate Objective-C objects the same way you would in Objective-C. You use alloc() and an init() method or a combined create() method depending on the provided API. This doesn’t change when inheriting Objective-C classes in Python. You need only create an init() method for your subclass which calls the appropriate init() of the superclass, and don’t forget to return self at the end! PyObjC takes care of creating an appropriate alloc() which returns an object of your new type.

So an example constructor might look like:


def MyClass:
def initWithFrame_(self, frame):
self = super(MyClass, self).initWithFrame_(frame)
return self

Note that the call to the superclass method initWithFrame_ will differ depending on what class you are inheriting from.

CoreGraphics
CoreGraphics is an ugly API. To work well in all languages, the API is function-oriented– which is all well and good until you add Apple’s ridiculously bloated naming conventions (“CGContextGetUserSpaceToDeviceSpaceTransform” anyone?) Since PyObjC can’t discover normal
functions on it’s own, you have to import them individually using objc.loadBundleFunctions (see above). PyObjC provides a wrapper module for Quartz but unfortunately it is not provided in the pyobjc distribution I have installed on my iPod, and it uses Objective-C helper methods which need to be compiled (I still do not have a working toolchain), so I cannot yet use it.

To make CoreGraphics integrate better with Python I am building an object-oriented wrapper API which resembles cairomm. It strips operation names of unnecessary words and makes the details easier to remember. PyObjC by default marshals CGRect, CGPoint, CGSize etc into tuples. CGRect is actually a tuple of 2 tuples which each have 2 floats (ie, ((x, y), (w, h))). I’ve made Python types which can take a tuple representation and make it more accessible with named fields:


rect = CGRect(window.frame())
print "the x coord: " + str(rect.x)
print "the whole thing: " + rect.toString()
window.setFrame_(rect.toTuple())

Enums are also not imported automatically by PyObjC, so to retain the sanity of the code, I am building subclasses of int to pin the proper values to their names.

I plan to clean up the code and release it as a reusable iPhone Python toolkit, but that’s for the next part of this ongoing series. I’ll also talk about performance and Springboard integration, among probably many other things.

Happy Hacking!

The state of unsanctioned (read: jailbroken) iPhone toolchains is still very poor. There are currently two projects available to aid (not to create literally as this is not strictly legal because Apple-owned files are required) in creating a viable iPhone toolchain, but both are frought with problems and barely tested on the latest iPhone firmware and SDK (version 3.1.2). Even if (when?) this changes, issues will still arise for each new SDK/firmware release from Apple.

Luckily, thanks to saurik and crew, we have a Python port available from Cydia/Rock. Very little work has gone into discovering what it takes to write Python applications for the iPhone, and thus very little information is available. I hope to remedy that with this new series of posts. I will be adding new posts as soon as I have new information so more people can begin prototyping their apps in Python (and thus avoid the immediate need for a working Objective-C toolchain)

Getting Started with iPhone Python
To begin you will need the iPhone Python port which can be found in the iphone-python APT package, or just search for iPhone Python from Cydia, Rock, or Icy.

Once you’ve installed it and resprung, you will notice a new application called HelloPython. This application, like all jailbreak apps (and in fact all of Apple’s stock apps) are found in the /Applications directory in the root filesystem of your device. This directory is actually a symlink to /var/stash/Applications.XXX, so if you are interacting with your filesystem using Linux’s sshfs like I am, you will need to use the full name since the symlink will be broken in this situation.

All iPhone apps are OSX bundles. The bundle names end with ‘.app’, ie MobileSafari.app, MobileNotes.app etc. Each application bundle contains an Info.plist file which defines a number of properties of the application including the title shown in the Springboard, the bundle identifier, the filename of the executable to start when the application is launched etc.

HelloPython simply displays your contacts. There is another more advanced example Python application which was released to the iphone-python mailing list at telesphoreo called pyExamp. You can find it here (http://www.telesphoreo.org/pipermail/iphone-python/2009-February/000324.html).

Neither application is even close to useful, nor are they typical of a real application’s structure. They do however illustrate the PyObjC support which allows you to interface with the iPhone APIs. For more information on the iPhone APIs, visit http://developer.apple.com and sign up as an iPhone app developer if you haven’t already.

The first step in writing your app is to create a viable template in which to write and test the code. There is no iPhone simulator that can test your Python apps, the only way to do it is to put your app on your (jailbroken) iPhone and run it. The iPhone OS does not directly give you error messages, nor is there any way to view the output of your application directly when it is run from the Springboard. It is also not possible to start iPhone apps via the console. Erica Sadun’s “Erica Utilities” package features a ‘launch’ application which unfortunately does not work for OS 3.1.2. Running your app from SSH or MobileTerminal.app still has advantages: the app should launch completely normally, but it will not display anything (headless).

When an iPhone-Python app launches, typically the Springboard will run a BASH script (see the ‘HelloPython’ and ‘pyExamp’ scripts for examples), which launches Python and points it at your main application script (HelloPython.py or pyExamp.py). A UNIX developer’s first instinct for reading output from an app run would be to use ‘tee’ so that output is visible when run from the command line as well as logged to a file when run from Springboard. Unfortunately this will not work!! If you use ‘tee’ your applications will not launch. You will likely see the error ‘Failed to register com.company.appName with bootstrap server: Unknown error’ in your log. This is because of the redirection BASH does to send the app stdout/stderr to ‘tee’.

Normal redirection like ‘1>&2 2>/var/mobile/myApp.log’ will work just fine. Unfortunately this means you will have to display the log file to get the output, even when you run the app from the command line. My apps use a generic script which does not need to be modified when used in a new application, feel free to use and improve it:
#!/bin/bash
exec “$(dirname “$0”)”/Python -u “$(dirname “$0”)”/`basename $0`.py 2>&1 1>/var/mobile/`basename $0`.err.log

Important: Notice the ‘-u’ option to python. This causes Python to run in unbuffered mode. If you don’t do this, your app output may never get written to the log file (or even screen) when Python is killed or terminated abruptly. So if the script is called ‘MyApp’, then the log will be /var/mobile/MyApp.err.log.

This setup is sufficient for getting your program output. Some people prefer redirecting Python’s stdout from within Python itself like this:

sys.stdout = open(‘/var/mobile/myApp.log’, ‘w’)
sys.stderr = sys.stdout

However you will not see Python parse errors using this technique (obviously), so you would still need to use the first mechanism to capture parse/global errors which occur before the above code is even executed.

Before running your new application you will need to change the bundle identifier (and whatever else you want to change) in Info.plist. This is only needed to prevent the iPhone OS from thinking your app is already running (of course, not a problem unless you are backgrounding HelloPython for some odd reason).

With this info in mind, you should be able to make a copy of HelloPython.app or pyExamp.app and get it to run. Next you’ll have to actually write the application!

My next post will talk about the awkwardness of PyObjC and how to make iPhone-Python development smoother.

Dear Apple:

I understand that Wifi location services is the only method the iPod Touch can use to determine it’s location. I also understand that mapping the location of every access point in the US is a difficult, if untenable goal. In this age, this sort of huge effort is much improved by using crowd-sourcing. As a geek who loves organizing information, I would be honored to help Apple and Skyhook improve their access point maps, but this is very difficult because of the lack of effort on the part of Skyhook, and to a much lesser extent, yourselves.

I live in the upper peninsula of Michigan, and it is very clear that Skyhook has never sent their trucks through my area. My home access points, those I use at my university, the public APs found at grocery stores and coffee shops all provide no location information on my iPod Touch. What’s worse, is the location check actually spins for about 40 seconds before just giving up, instead of quickly recognizing that the AP is not mapped. Location-aware applications ask to check my location multiple times, near constantly, and some applications simply do not work if location detection fails.

To begin to remedy this situation I have begun submitting MAC addresses with location information directly to Skyhook but the process is very rough. Their web page for doing so is very inconvenient as it lacks any sort of location search, and instead requires you to zone into your location from a global view, which is error prone and time consuming. None of my submissions thus far (the first sent in 2 weeks ago) have been added to your database yet (and thus location-based apps DO NOT work at all for anyone in my area). There is no iPod app for submitting access points, and the iPod does not reveal the AP’s MAC address to me. Therefore, I must boot my laptop to find the MAC address of my current AP, which means using the Skyhook submission page on my iPod is pretty useless.

On top of that they have totally botched the Linux support for their Loki toolbar, meaning I must use ‘iwconfig’ to get my AP MAC and plug it into that terrible web page each and every time I want to submit a new AP. Mac OS X is a UNIX system like Linux, do you really want to work with a partner who offers a UNIX “installation script” that looks like this?

(the file is called “LokiPlugin_Installer.sh”):

— beginning of file —
Self Extracting Installer

installer_actions.sh
npplugin.tar

Running Installer

libnploki.so
loki_logo_dialog.png
Installation complete
— end of file —
Figure 1: This is what happens when you let a moron manage your Linux support

My university has one of the most advanced WiFi networks in the central United States. Intel frequently does WiFi-related internships here and just this year they launched a WiMax testing program. As you can imagine, there are literally hundreds of APs spread across our fairly large campus, which would be a massive amount of wonderful data for Skyhook and yourselves, and could offer very (VERY) fine grained location data to our students and staff.

I posit to you Apple, that you replace Skyhook Wireless as the location data provider for the iPod Touch for one which is more capable of managing the ends of the business that affect your customers. I know it is very possible for Apple to change location providers or completely drop the features with an OS update, and I suggest you do so. With the exception of the location services system, I have been incredibly impressed and happy with my new iPod Touch 3G, which makes the utter failure of such services even more in sharp contrast to Apple’s usual reputation for ridiculously good quality (I have *never* gotten a proper location).

Even if there are no viable databases you could adopt that would provide the scope that Skyhook already does, there is no reason you could not improve upon their reporting by integrating location submission into the iPod Maps application. There is no reason the dialog which says “Could not determine your location” couldn’t have a button which says “Map my current location”, which opens Maps and allows you to stick the pin on your current location, which is submitted to you, and eventually to Skyhook. Please AT LEAST make it possible to use Location-aware apps even when Skyhook inevitably fails to determine your location.

Thank you for your time. I hope you address this problem as soon as possible.

Sincerely, your devoted iTunes and iPod Touch customer,

William Lahti

(This letter was sent to Apple Feedback and Support shortly after posting this.)

I am writing a media metadata subsystem capable of understanding a vast amount of detail about the content that a media file contains. It uses the file name information to produce a metadata hint, then uses pluggable services to retrieve potential matches as to the content. The result (at least so far) is a tool that can examine a media file and tell you exactly what movie, television episode, etc it contains with very good accuracy, including all the appropriate metadata (even individual episode descriptions!)

If this sounds a lot like what Boxee does, that’s because it does do this. Unfortunately though, Boxee analysis is rudimentary, and requires particular ordering and formatting of the file name elements (title, year, episode number, etc) to achieve decent reliability of listings retrieved by the client.

Can’t find a listing? Those Scene Tags Are In The Way.

MediaExpert’s approach is different: it filters the “title string” (initially, the file name without the extension, and all non-alphanumeric characters replaced with spaces) until it has something it believes is very likely the title of the content. It does this by recognizing common scene tags and removing them from the title string which will be searched by metadata services. The tags are stored in the hint information, allowing the application or other filter plugins to make use of the information. It already recognizes a great deal of tags which are formatting or source related (ex. 480p, DVDRip). It also recognizes some of the most popular scene release groups. Finally, it treats any words found within square brackets ([, ]) as scene tags. It looks for a four digit number which starts with either 1 or 2 and if found, considers it the year the content was released.

The system supports pluggable “scraper” filters as well, with one builtin one: TelevisionScraper, which looks for season/episode information in a variety of formats including sNeN, eN, NxN, and more. Unlike Boxee, a minimum of 1 digit is allowed with a maximum of 3 for seasons, and 3 for episodes. This information is stored within the metadata hint.

Now the metadata services query their respective providers and return the results. The system is then able to narrow down the possibilities via merit, that is, a best-score-wins heuristic based on things like how close the actual year is to the one provided in the file name, whether the content type (episode, movie, etc) matches the metadata provided in the hint (like season/episode numbers), how close the expected title is to the real title (using Levenschtein distance).

Altogether this makes for a powerhouse of media detection capability, without any compelling need to compulsively rename your media collection (hey, feel free if you want to).

The system will also work with many other media types like music, news, adult content, etc. The media-expert tool will also be capable of exporting the metadata in XML format for caching and distribution.

When I was young, in the days before live CDs and Linux netbooks, I lost Windows and all of my data. I had already been upset with the poor craftsmanship, sportsmanship, and lack of competitive decency shown by the players at Redmond and although it wasn’t really Microsoft’s fault that I had just lost a few years worth of projects, writings, and data, I was ready for a change. As I started from ground zero, with a Slackware 7(ish) install not yet able to drive my computer’s video, sound, or network and zero experience with Linux, UNIX, or even DOS, it was a very steep battle but I pressed on.

I read about how this whole Free Software thing got started. I learned about Richard Stallman, and his early work developing a ridiculously impressive text editor. He decided to steward it’s source code differently than anyone else at the time. Instead of charging for the program binaries or merely freely distributing the source, he would instead codify a new set of principles which would ensure the software remained free and accessible for all users.

Merely open source would not do, no one would be allowed to shackle his code. In this act, a community was born. A user could enjoy the use of a huge library of software, a user-developer could work to improve it, and everyone benefits. Oftentimes the user-developers would be so enamored by the grace of the gesture and the sweat of hard work invested by the developers, and they would begin their own separate projects to expand the coverage of what would quickly become a veritable ecosystem of code. Stallman made no assertions as to the sale of the software, only that all modified source code would be available to all users. It did not take long for the economically-minded to craft business models which resonated with the principles of Free Software.

Though I learned of the power of free software long after the movement was long underway, my commitment to the community and tenants of free and open source software is very strong. A great many others feel this way, most even more than I with a resume of community participation and contribution which greatly outweighs my own. I myself have contributed free code to such edge projects as Slicker and Y, and have from time to time submitted patches to a few of the larger desktop projects such as Xorg.

But the work which I am most dearly fond and proud of was my work on the SharpOS kernel. We did something incredibly novel and intriguing, and we managed to meet and exceed our initial goals. The project was truly open source in it’s creation, evolving from a shared discussion of a number of open source C# developers participating in the Mono Project at the time. During my tenure there I also introduced the foundations for a new windowing system (SharpWS), BASH-like command shell (Nash), and a set of UNIX base utilities to complement the SharpOS base system.

For those who weren’t in-the-know during it’s development (and I commend you, then, for reading my blog anyway), SharpOS became dormant because of a clash between the commitments to free software of some of the developers, and the pull away toward an open source model. Mircea Cristian Racasan (Chriss) led the majority of the core developers in the notion that our considerable work should remain protected under the terms of the General Public License, but outside influences wanted to see the code become open source instead. I did offer to switch licenses for the secondary projects I had contributed (SharpWS, Nash, and SharpOS-CoreUtils), but could not advocate the same for the SharpOS kernel, nor the AOT which Chriss had mostly developed.

As a result of the unavoidable schism, more projects with similar goals to SharpOS appeared, first with a fellow Chad Hower, much-aligned to Microsoft (and thus non-copyleft open source) and then with one Scott Balmos, with whom we merely had design disagreements (licensing was still an issue, but to a lesser extent). Considerable effort was applied to these initiatives, and the scene fragmented. Harsh words were exchanged, a lot of difficult effort was duplicated, wheels were reinvented. I advocated for friendship and respect between the factions, and I among others helped push for a joint forum where code could be shared, topics discussed openly, and mutual respect could eventually lead to more progress for all of the groups involved.

And here is where my conflict begins, o friends, for despite my dedication to the ways of Free Software, the church of Richard Stallman has made my fellow C# developers and I to be extant. I am a principled man, once a radically anti-Microsoft boy. My principles include that of tolerance and community. Another of my principles is recognizing the accomplishments of a particular technical/engineering solution. Microsoft made an excellent development platform with their .NET efforts which far surpasses Java in design, coherence, usability, and features. The CLI is easier to extend and to implement. The behavior of Microsoft towards CLR licensing and standardization has been more than adequate, and any of Microsoft’s patents which cover the standardized portion of the CLI have been waived under a royalty-free[sic]* license required by the ECMA. This leaves the nonstandard portions like System.Windows.Forms, System.Data, and others which are distinct and easily removed if Microsoft decides to return to it’s dirty bastard ways. Listen, Microsoft is not trustworthy, they are not our friend, and they are competition for the Linux desktop. But for the most part Microsoft is reaching out in a positive way, and this needs to be acknowledged.

The truth of the matter is that the code that we write in C# cannot be taken away from us. Microsoft cannot sue anyone for writing software in C#. They cannot sue anyone for including a Common Language Runtime, and they cannot sue anybody for using software written in C#. The FSF knows this enough to implement the CLI in their DotGNU project. Yet still Stallman preaches not using C# software and not developing C# software. To me, this means some people who might find my software useful will fail to try it out because Richard Stallman told them to avoid C#. What do you have against me, RMS? This is incredibly insulting to True Believers of FOSS who code C#, and I am left hurt.

Microsoft can sue people for using patents via unofficial clones of their proprietary APIs, which the community acknowledges. Why isn’t this where Richard focuses? He should be advocating the blacklisting of Microsoft’s extra APIs which do offer an actual *threat* to the Linux companies which are distributing them right now. It seems he has grown out of touch with what is relevant, opting instead to merely draw a black line between himself and anyone vaguely associated with Microsoft, taking no exception to hurling insults and generally ignoring the voices of the Free Software fellows who see C# and the CLR for what they are: an excellent programming language on top of an excellent development platform.

With his recent comments about Miguel de Icaza and his continued effort to make pariahs of C# supporters in the FOSS community, Stallman is alienating the entirety of the Mono, MOSA, Axiom, Grammatica, Tomboy, and Banshee communities among many others.

Look us all in the eyes, Stallman, and tell us that our contribution means nothing.

* UPDATE: Having run into a bit more reading around forums and stuff I realized that I was wrong about ECMA requiring royalty-free patent licenses for patents related to the standards. While this certainly produces some doubts in my mind, I still have to stand by the CLR, as the wonderful platform that it is. Microsoft does have the Community Promise, which basically says we won’t sue you if you implement enough of the specs, but I was left with a sense of ambiguity after reading into it. The solution though is pressing Microsoft to follow the tried and true open source patent licenses like those used by the Open Invention Network and IBM. The solution is not to just push Microsoft away and ignore the progress, alienating the developers who code with Microsoft’s frameworks.