An important part of the approach is that although we want to do this:
ObjC => Py
and we define an Abstract class in Objective-C from which the Python class inherits, so we have this:
ObjC => Py Concrete (ObjC Abstract)
we actually start from Python. This gives us the running interpreter:
Py => ObjC => Py Concrete
But the essential aspect of this method, that was really new to me, is a pair of functions that take the name of a class (as a string), and return the class object itself. In Python (using objc):
objc.lookUpClass("Abstract")
And in Objective-C:
Class c = NSClassFromString(@"Concrete");
I probably should have known about the first one. After all, it is mentioned very early in the Userguide for PyObjC, but after some searching, it is not clear to me where the latter is defined, let alone documented.
I have stripped bbum's example down a bit, and also, just pass in a dictionary to the class we will instantiate in Python.
Part One
an Objective-C class---Abstract. It contains a class method that will allocate and return an instance. This method takes a dictionary that can hold any arguments we want to pass in.
I put this code into files Abstract.h and Abstract.m in a new Xcode project that builds a bundle (what the docs call a "loadable bundle"). Make sure you select the Cocoa framework to link against. Select Build from the Build menu (the Toolbar Item is not enabled). I copied Abstract.bundle to the top-level directory that contains the Python script which will use it.
Part Two
a Python class---Concrete.py. The first section loads the Abstract bundle. This somehow makes the class definition "available" to the Objective-C runtime, so we can use a the handy method lookUpClass to actually load it. This allows us to subclass Abstract in Python.
Part Three
another Objective-C class---Caller. This is in a separate Xcode project that is built exactly as above. The code couldn't be simpler.
The class contains a single class method that calls the Python class method myInstance. We set one key-value pair in a dictionary on the Objective-C side, then pass it into Python. The handy method NSClassFromString makes the Python class available. We try two approaches to access the dictionary: via the getter method that is set up for us via @property and @synthesize (see Abstract), and direct access via report instead, breaking encapsulation.
Part Four
a Python script that sets everything in motion.
And the output:
The Caller class (ObjC) adds one key-value pair to the dictionary, while the Concrete class (Python) adds the second, and the dictionary is still available from the returned Python instance. One thing: the getter implemented via @property does not seem to work from Python, but an explicit report method does.
The Python Concrete class could then call any further Python code you want. Neat! Thanks, Bill.