Archive for Objective C

ADC Core Data Video Tutorial

See here for details on a new tutorial on Core Data.

In the vein of Ruby on Rails style video presentations, you can download the narrated video (38Mb) and learn very quickly how to put together a Core Data application.

Note: for the bandwidth challenged, it is broken down into smaller pieces on the ADC site.

As someone who is largely self taught on the Cocoa side of things, it was instructive to watch another developer at work with Apple’s tools. Aside from learning about Core Data, I learnt some productivity improvements for using Interface Builder and XCode.

If you are writing Cocoa apps, this is well worth a watch!

Elementary Bluetooth using PyObjC

Bluetooth connectivity to devices is supported on Mac OS X using Cocoa APIs, so is possible to access from Python. Bluetooth itself opens up a large body of knowledge that I am only starting to investigate.

This article outlines some basics for using Bluetooth in Python.

Bluetooth access is achieved through two frameworks, IOBluetooth and IOBluetoothUI. Neither of these are included in PyObjC by default, so a basic wrapper class makes life easier. (see the PyObjC docs)

Add the following to IOBluetooth.py:


import objc as _objc

_objc.loadBundle('IOBluetooth', globals(),
  bundle_path=u'/System/Library/Frameworks/IOBluetooth.framework')

The loadBundle pattern is very useful, and allows the classes from any Objective C framework to be loaded into the PyObjC bridge. By putting it into a separate Python module, usage becomes less painful.

For example, the most recent device is:


>>> from IOBluetooth import *
>>> devs = IOBluetoothDevice.recentDevices_(1)
>>> devs[0].getNameOrAddress()
u'gmwils 6600'
>>> devs[0].isConnected()
1
>>>

In this case, my Nokia 6600 is still connected in AddressBook.

To create a new connection to a device, Apple provides access to their user interface for Bluetooth management. It is strongly recommended that these controls are used to provide consistency for the user and detailed error handling.

For example, selecting a service from a device with a dialog:


>>> from IOBluetoothUI import *
>>> browser = IOBluetoothServiceBrowserController.serviceBrowserController_(0)
>>> browser.runModal()
-1000
>>> results = browser.getResults()
>>> results[0].getServiceName()
u'Bluetooth Serial Port'
>>>

Note: you will need a IOBluetoothUI wrapper, similar to the IOBluetooth wrapper from earlier.

Useful documentation links:

Paul Graham: What Languages Fix

Paul has an interesting article about describing languages:

Kevin Kelleher suggested an interesting way to compare programming languages: to describe each in terms of the problem it fixes.

I’d like to add to his list:

  • Haskell: Lisp doesn’t have types
  • Mercury: Prolog doesn’t have types and requires too much knowledge of the internals

There should be a nice concise summary of Objective-C, but as I’ve been avoiding it I’ve struggled to come up with one. It doesn’t solve the problem of C being too low level. The closest I could get was that it added Object Oriented programming concepts to C.

Evening at Adler - Some guys talking about the Mac

After watching the Evening at Adler video, I can safely say that it is a must see for anyone developing software for the Apple platform. DB managed to assemble the who’s who of Mac software development for a round table chat.

Inspirational and informative, with a touch of controversy and some laugh out loud comedic moments. Can’t go wrong really. Although I suspect the background music was chosen for the closing visuals.

Showing a NSSavePanel as a sheet

Using a sheet to display a save panel adds for a significant user interface improvement. Based on the simplicity of a range of other Cocoa features, I was surprised at how tricky it was to get working.

The issues had nothing to do with Cocoa per-se, but with the PyObjC bridge.

Creating the panel and creating the sheet is as follows:


sp = NSSavePanel.savePanel()
sp.setRequiredFileType_('ics')
sp.setNameFieldLabel_('Export As:')

sp.beginSheetForDirectory_file_modalForWindow_modalDelegate_didEndSelector_contextInfo_(
    userDocumentFolder(),'default.ics',
    NSApp().mainWindow(),self,
    'didEndSheet:returnCode:contextInfo:',0)

The tricky bit is the asynchronous callback, which is passed in as an Objective C selector: 'didEndSheet:returnCode:contextInfo:'

In Python terms, the following method does the trick:


def didEndSheet_returnCode_contextInfo_(self, sheet, returnCode, info):
    if returnCode == NSCancelButton:
        return

    # Save the file returned by sheet.filename()
    print sheet.filename()

I only had one slight problem. The application crashed when the user clicked a button on the sheet.

The issue is that the Python bridge has no idea as to the type of the arguments in the method listed as the callback from the sheet. When the callback occurs from Cocoa, it goes searching for a method which matches a specific type signature, as documented in NSSavePanel.

An explicit type signature is required for the method to be discoverable. PyObjC provides a few ways of doing this.

Note: the name of the callback function is up to programmer, which is why the type signature is important.

The Python 2.4 version (using decorators) looks like:


from objc import *

@objc.signature('v@:@ii')
def didEndSheet_returnCode_contextInfo_(self, sheet, returnCode, info):
    if returnCode == NSCancelButton:
        return

    # Save the file returned by sheet.filename()

With the standard version like:


from objc import *

def didEndSheet_returnCode_contextInfo_(self, sheet, returnCode, info):
    if returnCode == NSCancelButton:
        return

    # Save the file returned by sheet.filename()
    print sheet.filename()
didEndSheet_returnCode_contextInfo_ = objc.selector(
        didEndSheet_returnCode_contextInfo_ , signature='v@:@ii')

With the type information added in, the callback can locate the method on the object and all is well in the world. Files are saved, memory protected, everybody is happy.

Almost.

I had a simple question, where did this type string come from?

Searching on Google had resulted in two different strings. Both worked. However they were quite different.


@objc.signature('v16@4:8@12i16i20')
@objc.signature('v@:@ii')

Somewhere, there had to be an explanation for all of this.

The PyObjC documentation provided some good hints. The intro mentions the different syntax for specifying the type signature, and provides an example.

Using a convenience method on AppHelper, the type information does not need to be explicitly included in the code for the endSheet case:


from PyObjCTools import AppHelper

# Python 2.4
@AppHelper.endSheetMethod
def didEndSheet_returnCode_contextInfo_(self, sheet, returnCode, info):
	pass

# Python 2.3
def didEndSheet_returnCode_contextInfo_(self, sheet, returnCode, info):
    pass
didEndSheet_returnCode_contextInfo_ = AppHelper.endSheetMethod(
        didEndSheet_returnCode_contextInfo_)

This third option provides cleaner code, but still didn’t explain where type signatures come from.

(I must have been a difficult child, a solution isn’t enough, the need to know why/how is the motivator)

The key came in reading through the documentation on wrapping an Objective C class. When the documentation refers to a ‘raw Objective-C method signature’, it means what it says. The string appears to be Objective-C’s internal representation of the type signature.

Apple provides documentation on the syntax. (Note: Try here for updated docs)

Probably a bit further into Objective-C than I wanted to delve, but much was learnt along the way. A good abstraction allows you to peek through it. I’m happy to learn that PyObjC provides that flexibility, and that most of the time, it isn’t needed.