omz:forum

    • Register
    • Login
    • Search
    • Recent
    • Popular

    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?

    Pythonista
    subclass objective-c objcutil
    5
    12
    6397
    Loading More Posts
    • Oldest to Newest
    • Newest to Oldest
    • Most Votes
    Reply
    • Reply as topic
    Log in to reply
    This topic has been deleted. Only users with topic management privileges can see it.
    • shinyformica
      shinyformica last edited by

      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.

      1 Reply Last reply Reply Quote 0
      • pulbrich
        pulbrich last edited by

        I wouldn’t over-complicate it, just:

        SupClass = ObjCClass(SupClass)
        SupClass.theMethod_(withParameter)
        

        Peter

        1 Reply Last reply Reply Quote 0
        • JonB
          JonB last edited by

          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.

          mikael 1 Reply Last reply Reply Quote 0
          • mikael
            mikael @JonB last edited by mikael

            @JonB, so calling the superclass type method with _self as the first parameter will not work in objc? I had to try, but... No.

            1 Reply Last reply Reply Quote 0
            • pulbrich
              pulbrich last edited by

              This post is deleted!
              1 Reply Last reply Reply Quote 0
              • JonB
                JonB last edited by

                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.

                mikael 1 Reply Last reply Reply Quote 1
                • mikael
                  mikael @JonB last edited by

                  @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?

                  1 Reply Last reply Reply Quote 0
                  • dgelessus
                    dgelessus last edited by dgelessus

                    @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 do import 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.
                    • Rubicon doesn't integrate with other Pythonista modules like objc_util does. ui.View().objc_instance will still return an ObjCInstance from objc_util, and with Rubicon you can't do ObjCInstance(ui.View()) (you have to write ObjCInstance(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.
                    mikael 1 Reply Last reply Reply Quote 0
                    • mikael
                      mikael @dgelessus last edited by

                      @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?

                      1 Reply Last reply Reply Quote 0
                      • dgelessus
                        dgelessus last edited by dgelessus

                        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 between objc_util.ObjCInstance and rubicon.objc.ObjCInstance, you have to cast to c_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 using objc_util's ObjCClass.

                        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 use obj[key] for NSArray and NSDictionary, but you also get Python-style methods (append, extend, etc. for NSArray and items, values, etc. for NSDictionary). NSString also gets operators/methods similar to Python str, so you often don't need to explicitly convert NSStrings to/from str.
                        • isinstance and issubclass work with ObjC classes - you can do isinstance(thing, NSString) instead of thing.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 just c_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 a ctypes 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).

                        mikael 1 Reply Last reply Reply Quote 1
                        • mikael
                          mikael @dgelessus last edited by

                          @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?

                          1 Reply Last reply Reply Quote 0
                          • dgelessus
                            dgelessus last edited by

                            @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 like NSInteger. 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)
                            
                            1 Reply Last reply Reply Quote 0
                            • First post
                              Last post
                            Powered by NodeBB Forums | Contributors