Welcome!
This is the community forum for my apps Pythonista and Editorial.
For individual support questions, you can also send an email. If you have a very short question or just want to say hello — I'm @olemoritz on Twitter.
Calling superclass method in objc_util subclass?
-
This may be a dumb question...it's late and I'm not thinking super clearly...
If I create a subclass of some Objective-C class via:
objc_util.create_objc_class(name, superclass=SuperClass, methods=[], ...
and define some method that I will implement in the 'methods' list, which will override a method defined on the superclass, how do I call the superclass's version of that method from my implementation?
Basically, I want to override the method to do some extra things, but still call the base method to do what it would normally do.
-
I wouldn’t over-complicate it, just:
SupClass = ObjCClass(SupClass) SupClass.theMethod_(withParameter)
Peter
-
I think he is talking the method on an instance.
You might be able to construct the ObjcInstanceMethod directly. But otherwise you need to use
objc_msgSendSuper
To formulate the objc message manually.
-
@JonB, so calling the superclass type method with _self as the first parameter will not work in objc? I had to try, but... No.
-
This post is deleted! -
I wonder if we shouldn't all switch over to pybee Rubicon. @dgelessus has helped make that library really complete and easy to use. For instance, there is a nice decorator system for defining classes, and I think they have a working super. . Also, their type parsing I think is a lot more complete, and memory management is more robust.
Anyway the discussion where they got super working us here
https://github.com/beeware/rubicon-objc/pull/108
Some of that might be useful to the op in getting it working, but you'll have to implement some of what Rubicon does using objc_util. -
@JonB, what would us moving to use Rubicon mean in practice? A fork of Rubicon where some things would need to be changed for Pythonista?
-
@mikael Rubicon runs without changes on Pythonista, as far as I know. The main use case for Rubicon is writing Python-based apps using BeeWare's Python-Apple-support, but the Python environment from that build is very similar to Pythonista (I think Pythonista even uses some of the patches from that repo), so Rubicon generally works in Pythonista just as it would in a standalone app.
If any parts of Rubicon don't work in Pythonista, please open an issue on the Rubicon repo. Although Pythonista isn't officially supported, I try to keep it as compatible as possible (Pythonista is my usual environment for testing/debugging Rubicon on iOS). Also, nearly all Rubicon bugs that appear in Pythonista also affect standalone iOS environments, so fixing them in Rubicon is better for everyone.
Though I want to point out a few things that you should know if you want to switch to Rubicon:
- Rubicon's API is not directly compatible with
objc_util
, so you can't just doimport rubicon.objc as objc_util
in your existing code. The API isn't that different though, for the most part you only need to change some function names and rewrite classes to the new syntax.- You could probably even write a "compatibility module" that has the same API as
objc_util
, but calls Rubicon internally. I wanted to try that at some point, but haven't gotten around to it.
- You could probably even write a "compatibility module" that has the same API as
- Rubicon doesn't integrate with other Pythonista modules like
objc_util
does.ui.View().objc_instance
will still return anObjCInstance
fromobjc_util
, and with Rubicon you can't doObjCInstance(ui.View())
(you have to writeObjCInstance(ui.View()._objc_ptr)
instead).- This could probably be added at runtime using monkey-patching though.
- Rubicon isn't very convenient to use in the REPL yet, you don't get code completion suggestions for ObjC methods (I think there's no proper introspection support at all right now). So at the moment
objc_util
is still better for exploring and debugging interactively. - The documentation is not quite complete yet, though I have a work in progress PR that significantly expands the docs. I really need to finish that at some point.
- Rubicon's API is not directly compatible with
-
@dgelessus, thanks. I am reading this that there is no driver to change existing code or adopting Rubicon wholesale, but if I create new ObjC classes (or some other specific situations?), I could well consider using Rubicon today, maybe peacefully co-existing with objc_util?
-
There should be no problems with using both Rubicon and
objc_util
at the same time, they don't interfere with each other. As usual, there's no proper compatibility between the two libraries, so for example to convert betweenobjc_util.ObjCInstance
andrubicon.objc.ObjCInstance
, you have to cast toc_void_p
in between. Anything on the ObjC side will interact properly though, so you can define an ObjC class using Rubicon and then look it up usingobjc_util
'sObjCClass
.I'd say the main advantages of Rubicon right now are really what @JonB has already said above: the class definition syntax (it's just much more convenient to use the standard Python syntax) and the improved type handling (almost all type encodings are predefined or handled automatically, and when you do need custom structure types, they only need to be "registered" once instead of having to set
restype
/argtypes
on every call). There are also a few other features that are not particularly exciting individually, but they are nice to have:- Slightly better Python interaction for standard ObjC types. Like with
objc_util
you can useobj[key]
forNSArray
andNSDictionary
, but you also get Python-style methods (append
,extend
, etc. forNSArray
anditems
,values
, etc. forNSDictionary
).NSString
also gets operators/methods similar to Pythonstr
, so you often don't need to explicitly convertNSString
s to/fromstr
. isinstance
andissubclass
work with ObjC classes - you can doisinstance(thing, NSString)
instead ofthing.isKindOfClass(NSString)
.- Class definitions can have properties and ivars (though you don't need this very often in practice).
- You can define custom protocols, just like classes (though you don't need this very often in practice, either).
- Calling low-level ObjC runtime functions is more convenient - there are proper classes for
objc_id
,Class
,SEL
, etc. instead of justc_void_p
. - You can return structs from
ctypes
callbacks (CFUNCTYPE
) and from things that use callbacks internally (custom ObjC methods and blocks), which is normally not possible because of actypes
limitation. - Rubicon is not specific to Pythonista, so Rubicon-based code can be reused in Pyto (which has Rubicon bundled as its
objc_util
equivalent) or even in a standalone app.
Of course there's also the disadvantage that Rubicon is not installed by default in Pythonista, so to run Rubicon-based code you need to install Rubicon first (but this should be easy to do using Stash
pip
, because Rubicon is on PyPI). - Slightly better Python interaction for standard ObjC types. Like with
-
@dgelessus, just out of curiosity & if you feel like it, could you share an example of defining an ObjC subclass in Rubicon, complete with some callback block that needs to talk to the rest of the Python-side program?
-
@mikael I'm not very creative with these things - do you have some example code based on
objc_util
that I could translate to Rubicon?In general the syntax for classes looks like this:
from rubicon.objc import NSInteger, NSObject, ObjCProtocol, objc_method WhateverDelegate = ObjCProtocol("WhateverDelegate") class MyCustomDelegate(NSObject, protocols=[WhateverDelegate]): # the protocols=[...] part is optional of course, if you don't need to implement any protocol @objc_method def someThing_didAThingWithCount_(self, something, count: NSInteger) -> None: print(something, count) something.delegate = MyCustomDelegate.new()
The Python 3 type annotation syntax is used to set each method's return and argument types. You use regular
ctypes
type objects for the types, and Rubicon predefines some common typedefs likeNSInteger
. Anything you don't annotate is assumed to be an Objective-C object (ObjCInstance
).Blocks are similar:
from rubicon.objc import Block, NSInteger @Block def thing_callback(number: NSInteger) -> None: print(number) whatever.doThing("abc", withCallback=callback)