rubicon.objc.api
— The high-level Rubicon API¶
This module contains Rubicon’s main high-level APIs, which allow easy interaction with Objective-C classes and objects using Pythonic syntax.
Nearly all attributes of this module are also available on the main
rubicon.objc
module, and if possible that module should be used instead
of importing rubicon.objc.api
directly.
Objective-C objects¶
- class rubicon.objc.api.ObjCInstance(ptr)¶
Python wrapper for an Objective-C instance.
The constructor accepts an
objc_id
or anything that can be cast to one, such as ac_void_p
, or an existingObjCInstance
.ObjCInstance
objects are cached — this means that for every Objective-C object there can be at most oneObjCInstance
object at any time. Rubicon will automatically create newObjCInstance
s or return existing ones as needed.The returned object’s Python class is not always exactly
ObjCInstance
. For example, if the passed pointer refers to a class or a metaclass, an instance ofObjCClass
orObjCMetaClass
is returned as appropriate. Additional customObjCInstance
subclasses may be defined and registered usingregister_type_for_objcclass()
. Creating anObjCInstance
from anil
pointer returnsNone
.Rubicon retains an Objective-C object when it is wrapped in an
ObjCInstance
and autoreleases it when theObjCInstance
is garbage collected.The only exception to this are objects returned by methods which create an object (starting with “alloc”, “new”, “copy”, or “mutableCopy”). We do not explicitly retain them because we already own objects created by us, but we do autorelease them on garbage collection of the Python wrapper.
This ensures that the
ObjCInstance
can always be used from Python without segfaults while preventing Rubicon from leaking memory.- ptr¶
- _as_parameter_¶
The wrapped object pointer as an
objc_id
. This attribute is also available as_as_parameter_
to allowObjCInstance
s to be passed intoctypes
functions.
- __str__()¶
Get a human-readable representation of
self
.By default,
self.description
converted to a Python string is returned. Ifself.description
isnil
,self.debugDescription
converted to a Python is returned instead. If that is alsonil
,repr(self)
is returned as a fallback.
- __repr__()¶
Get a debugging representation of
self
, which includes the Objective-C object’s class anddebugDescription
.
- __getattr__(name)¶
Allows accessing Objective-C properties and methods using Python attribute syntax.
If
self
has a Python attribute with the given name, its value is returned.If there is an Objective-C property with the given name, its value is returned using its getter method. An attribute is considered a property if any of the following are true:
A property with the name is present on the class (i.e. declared using
@property
in the source code)There is both a getter and setter method for the name
The name has been declared as a property using
ObjCClass.declare_property()
Otherwise, a method matching the given name is looked up.
ObjCInstance
understands two syntaxes for calling Objective-C methods:“Flat” syntax: the Objective-C method name is spelled out in the attribute name, with all colons replaced with underscores, and all arguments are passed as positional arguments. For example, the Objective-C method call
[self initWithWidth:w height:h]
translates toself.initWithWidth_height_(w, h)
.“Interleaved” syntax: the Objective-C method name is split up between the attribute name and the keyword arguments passed to the returned method. For example, the Objective-C method call
[self initWithRed:r green:g blue:b]
translates toself.initWithRed(r, green=g, blue=b)
.
The “interleaved” syntax is usually preferred, since it looks more similar to normal Objective-C syntax. However, the “flat” syntax is also fully supported. If two arguments have the same name (e.g.
performSelector:withObject:withObject:
), you can use__
in the keywords to disambiguate (e.g.,performSelector(..., withObject__1=..., withObject__2=...)
. Any content after and including the__
in an argument will be ignored.
- __setattr__(name, value)¶
Allows modifying Objective-C properties using Python syntax.
If
self
has a Python attribute with the given name, it is set. Otherwise, the name should refer to an Objective-C property, whose setter method is called withvalue
.
- rubicon.objc.api.objc_const(dll, name)¶
Create an
ObjCInstance
from a global pointer variable in aCDLL
.This function is most commonly used to access constant object pointers defined by a library/framework, such as NSCocoaErrorDomain.
Objective-C classes¶
- class rubicon.objc.api.ObjCClass(name_or_ptr[, bases, attrs[, protocols=(), auto_rename=None]])¶
Python wrapper for an Objective-C class.
ObjCClass
is a subclass ofObjCInstance
and supports the same syntaxes for calling methods and accessing properties.The constructor accepts either the name of an Objective-C class to look up (as
str
orbytes
), or a pointer to an existing class object (in any form accepted byObjCInstance
).If given a pointer, it must refer to an Objective-C class; pointers to other objects are not accepted. (Use
ObjCInstance
to wrap a pointer that might also refer to other kinds of objects.) If the pointer refers to a metaclass, an instance ofObjCMetaClass
is returned instead. Creating anObjCClass
from aNil
pointer returnsNone
.ObjCClass
can also be called liketype
, with three arguments (name, bases list, namespace mapping). This form is called implicitly by Python’sclass
syntax, and is used to create a new Objective-C class from Python (see Creating custom Objective-C classes and protocols). The bases list must contain exactly oneObjCClass
to be extended by the new class. An optionalprotocols
keyword argument is also accepted, which must be a sequence ofObjCProtocol
s for the new class to adopt.If the name of the class has already registered with the Objective C runtime, the
auto_rename
option can be used to ensure that the Objective C name for the new class will be unique. A numeric suffix will be appended to the Objective C name to ensure uniqueness (for example,MyClass
will be renamed toMyClass_2
,MyClass_3
etc until a unique name is found). By default, classes will not be renamed, unlessObjCClass.auto_rename
is set at the class level.- protocols¶
The protocols adopted by this class.
- auto_rename = False¶
A
bool
value describing whether a defined class should be renamed automatically if a class with the same name already exists in the Objective C runtime.
- declare_property(name)¶
Declare the instance method
name
to be a property getter.This causes the attribute named
name
on instances of this class to be treated as a property rather than a method — accessing it returns the property’s value, without requiring an explicit method call. SeeObjCInstance.__getattr__
for a full description of how attribute access behaves for properties.Most properties do not need to be declared explicitly using this method, as they are detected automatically by
ObjCInstance.__getattr__
. This method only needs to be used for properties that are read-only and don’t have a@property
declaration in the source code, because Rubicon cannot tell such properties apart from normal zero-argument methods.Note
In the standard Apple SDKs, some properties are introduced as regular methods in one system version, and then declared as properties in a later system version. For example, the
description
method/property ofNSObject
was declared as a regular method up to OS X 10.9, but changed to a property as of OS X 10.10.Such properties cause compatibility issues when accessed from Rubicon:
obj.description()
works on 10.9 but is aTypeError
on 10.10, whereasobj.description
works on 10.10 but returns a method object on 10.9. To solve this issue, the property can be declared explicitly usingNSObject.declare_property('description')
, so that it can always be accessed usingobj.description
.
- declare_class_property(name)¶
Declare the class method
name
to be a property getter.This is equivalent to
self.objc_class.declare_property(name)
.
- __instancecheck__(instance)¶
Check whether the given object is an instance of this class.
If the given object is not an Objective-C object,
False
is returned.This method allows using
ObjCClass
es as the second argument ofisinstance()
:isinstance(obj, NSString)
is equivalent toobj.isKindOfClass(NSString)
.
- __subclasscheck__(subclass)¶
Check whether the given class is a subclass of this class.
If the given object is not an Objective-C class,
TypeError
is raised.This method allows using
ObjCClass
es as the second argument ofissubclass()
:issubclass(cls, NSValue)
is equivalent toobj.isSubclassOfClass(NSValue)
.
- class rubicon.objc.api.ObjCMetaClass(name_or_ptr)¶
Python wrapper for an Objective-C metaclass.
ObjCMetaClass
is a subclass ofObjCClass
and supports almost exactly the same operations and methods. However, there is usually no need to look up a metaclass manually. The main reason whyObjCMetaClass
is a separate class is to differentiate it fromObjCClass
in therepr()
. (Otherwise there would be no way to tell classes and metaclasses apart, since metaclasses are also classes, and have exactly the same name as their corresponding class.)The constructor accepts either the name of an Objective-C metaclass to look up (as
str
orbytes
), or a pointer to an existing metaclass object (in any form accepted byObjCInstance
).If given a pointer, it must refer to an Objective-C metaclass; pointers to other objects are not accepted. (Use
ObjCInstance
to wrap a pointer that might also refer to other kinds of objects.) Creating anObjCMetaClass
from aNil
pointer returnsNone
.
Standard Objective-C and Foundation classes¶
The following classes from the Objective-C runtime and the
Foundation
framework are provided as ObjCClass
es for convenience. (Other classes
not listed here can be looked up by passing a class name to the
ObjCClass
constructor.)
Note
None of the following classes have a usable Python-style constructor - for
example, you cannot call NSString("hello")
to create an Objective-C
string from a Python string. To create instances of these classes, you
should use ns_from_py()
(also called at()
):
ns_from_py("hello")
returns a NSString
instance with the value
hello
.
- class rubicon.objc.api.NSObject¶
The NSObject class from
<objc/NSObject.h>
.Note
See the
ObjCInstance
documentation for a list of operations that Rubicon supports on all objects.- debugDescription¶
- description¶
These Objective-C properties have been declared using
ObjCClass.declare_property()
and can always be accessed using attribute syntax.
- class rubicon.objc.api.Protocol¶
The Protocol class from
<objc/Protocol.h>
.Note
This class has no (non-deprecated) Objective-C methods; protocol objects can only be manipulated using Objective-C runtime functions. Rubicon automatically wraps all
Protocol
objects usingObjCProtocol
, which provides an easier interface for working with protocols.
- class rubicon.objc.api.NSNumber¶
The NSNumber class from
<Foundation/NSValue.h>
.Note
This class can be converted to and from standard Python primitives (
bool
,int
,float
) usingpy_from_ns()
andns_from_py()
.
- class rubicon.objc.api.NSDecimalNumber¶
The NSDecimalNumber class from
<Foundation/NSDecimalNumber.h>
.Note
This class can be converted to and from Python
decimal.Decimal
usingpy_from_ns()
andns_from_py()
.
- class rubicon.objc.api.NSString¶
The NSString class from
<Foundation/NSString.h>
.This class also supports all methods that
str
does.Note
This class can be converted to and from Python
str
usingpy_from_ns()
andns_from_py()
. You can also callstr(nsstring)
to convert aNSString
tostr
.NSString
objects consist of UTF-16 code units, unlikestr
, which consists of Unicode code points. AllNSString
indices and iteration are based on UTF-16, even when using the Python-style operations/methods. If indexing or iteration based on code points is required, convert theNSString
tostr
first.- UTF8String¶
This Objective-C property has been declared using
ObjCClass.declare_property()
and can always be accessed using attribute syntax.
- class rubicon.objc.api.NSData¶
The NSData class from
<Foundation/NSData.h>
.Note
This class can be converted to and from Python
bytes
usingpy_from_ns()
andns_from_py()
.
- class rubicon.objc.api.NSArray¶
The NSArray class from
<Foundation/NSArray.h>
.Note
This class can be converted to and from Python
list
usingpy_from_ns()
andns_from_py()
.py_from_ns(nsarray)
will recursively convertnsarray
’s elements to Python objects, where possible. To avoid this recursive conversion, uselist(nsarray)
instead.ns_from_py(pylist)
will recursively convertpylist
’s elements to Objective-C. As there is no way to store Python object references as Objective-C objects yet, this recursive conversion cannot be avoided. If any ofpylist
’s elements cannot be converted to Objective-C, an error is raised.
- class rubicon.objc.api.NSMutableArray¶
The NSMutableArray class from
<Foundation/NSArray.h>
.Note
This class can be converted to and from Python exactly like its superclass
NSArray
.
- class rubicon.objc.api.NSDictionary¶
The NSDictionary class from
<Foundation/NSDictionary.h>
.Note
This class can be converted to and from Python
dict
usingpy_from_ns()
andns_from_py()
.py_from_ns(nsdict)
will recursively convertnsdict
’s keys and values to Python objects, where possible. To avoid the recursive conversion of the values, use{py_from_ns(k): v for k, v in nsdict.items()}
. The conversion of the keys cannot be avoided, because Pythondict
keys need to be hashable, whichObjCInstance
is not. If any of the keys convert to a Python object that is not hashable, an error is raised (regardless of which conversion method you use).ns_from_py(pydict)
will recursively convertpydict
’s keys and values to Objective-C. As there is no way to store Python object references as Objective-C objects yet, this recursive conversion cannot be avoided. If any ofpydict
’s keys or values cannot be converted to Objective-C, an error is raised.- __getitem__(key)¶
- __len__()¶
- __iter__()¶
- __contains__(key)¶
- __eq__(other)¶
- __ne__(other)¶
- copy()¶
- get(key[, default=None])¶
- keys()¶
- items()¶
- values()¶
Python-style mapping interface.
Note
Unlike most Python mappings,
NSDictionary
’skeys
,values
, anditems
methods don’t return dynamic views of the dictionary’s keys, values, and items.keys
andvalues
return lists that are created each time the methods are called, which can have an effect on performance and memory usage for large dictionaries. To avoid this, you can cache the return values ofkeys
andvalues
, or convert theNSDictionary
to a Pythondict
beforehand.items
is currently implemented as a generator, meaning that it returns a single-use iterator. If you need to iterate overitems
more than once or perform other operations on it, you should convert it to a Pythonset
orlist
first.
- class rubicon.objc.api.NSMutableDictionary¶
The NSMutableDictionary class from
<Foundation/NSDictionary.h>
.Note
This class can be converted to and from Python exactly like its superclass
NSDictionary
.
Objective-C protocols¶
- class rubicon.objc.api.ObjCProtocol(name_or_ptr[, bases, attrs[, auto_rename=None]])¶
Python wrapper for an Objective-C protocol.
The constructor accepts either the name of an Objective-C protocol to look up (as
str
orbytes
), or a pointer to an existing protocol object (in any form accepted byObjCInstance
).If given a pointer, it must refer to an Objective-C protocol; pointers to other objects are not accepted. (Use
ObjCInstance
to wrap a pointer that might also refer to other kinds of objects.) Creating anObjCProtocol
from anil
pointer returnsNone
.ObjCProtocol
can also be called liketype
, with three arguments (name, bases list, namespace mapping). This form is called implicitly by Python’sclass
syntax, and is used to create a new Objective-C protocol from Python (see Creating custom Objective-C classes and protocols). The bases list can contain any number ofObjCProtocol
objects to be extended by the new protocol.If the name of the protocol has already registered with the Objective C runtime, the
auto_rename
option can be used to ensure that the Objective C name for the new protocol will be unique. A numeric suffix will be appended to the Objective C name to ensure uniqueness (for example,MyProtocol
will be renamed toMyProtocol_2
,MyProtocol_3
etc until a unique name is found). By default, protocols will not be renamed, unlessObjCProtocol.auto_rename
is set at the class level.- protocols¶
The protocols that this protocol extends.
- auto_rename = False¶
A
bool
value whether a defined protocol should be renamed automatically if a protocol with the same name is already exists.
- __instancecheck__(instance)¶
Check whether the given object conforms to this protocol.
If the given object is not an Objective-C object,
False
is returned.This method allows using
ObjCProtocol
s as the second argument ofisinstance()
:isinstance(obj, NSCopying)
is equivalent toobj.conformsToProtocol(NSCopying)
.
- __subclasscheck__(subclass)¶
Check whether the given class or protocol conforms to this protocol.
If the given object is not an Objective-C class or protocol,
TypeError
is raised.This method allows using
ObjCProtocol
s as the second argument ofissubclass()
:issubclass(cls, NSCopying)
is equivalent tocls.conformsToProtocol(NSCopying)
, andissubclass(proto, NSCopying)
is equivalent toprotocol_conformsToProtocol(proto, NSCopying))
.
Standard Objective-C and Foundation protocols¶
The following protocols from the Objective-C runtime and the
Foundation
framework are provided as ObjCProtocol
s for convenience. (Other
protocols not listed here can be looked up by passing a protocol name to the
ObjCProtocol
constructor.)
- rubicon.objc.api.NSObjectProtocol¶
The NSObject protocol from
<objc/NSObject.h>
. The protocol is exported asNSObjectProtocol
in Python because it would otherwise clash with theNSObject
class.
Converting objects between Objective-C and Python¶
- rubicon.objc.api.py_from_ns(nsobj)¶
Convert a Foundation object into an equivalent Python object if possible.
Currently supported types:
objc_id
: Wrapped in anObjCInstance
and converted as below
NSDecimalNumber
: Converted todecimal.Decimal
NSDictionary
: Converted todict
, with all keys andvalues converted recursively
Other objects are returned unmodified as an
ObjCInstance
.
- rubicon.objc.api.ns_from_py(pyobj)¶
Convert a Python object into an equivalent Foundation object. The returned object is autoreleased.
This function is also available under the name
at()
, because its functionality is very similar to that of the Objective-C@
operator and literals.Currently supported types:
None
,ObjCInstance
: Returned as-isdecimal.Decimal
: Converted toNSDecimalNumber
dict
: Converted toNSDictionary
, with all keys andvalues converted recursively
Other types cause a
TypeError
.
- rubicon.objc.api.at(pyobj)¶
Alias for
ns_from_py()
.
Creating custom Objective-C classes and protocols¶
Custom Objective-C classes are defined using Python class
syntax, by
subclassing an existing ObjCClass
object:
class MySubclass(NSObject):
# method, property, etc. definitions go here
A custom Objective-C class can only have a single superclass, since Objective-C
does not support multiple inheritance. However, the class can conform to any
number of protocols, which are specified by adding the protocols
keyword
argument to the base class list:
class MySubclass(NSObject, protocols=[NSCopying, NSMutableCopying]):
# method, property, etc. definitions go here
Note
Rubicon requires specifying a superclass when defining a custom Objective-C
class. If you don’t need to extend any specific class, use
NSObject
as the superclass.
Although Objective-C technically allows defining classes without a base
class (so-called root classes), this is almost never the desired behavior
(attempting to do so causes a compiler error by default).
In practice, this feature is only used in the definitions of core
Objective-C classes like NSObject
. Because of this, Rubicon does
not support defining Objective-C root classes.
Similar syntax is used to define custom Objective-C protocols. Unlike classes, protocols can extend multiple other protocols:
class MyProtocol(NSCopying, NSMutableCopying):
# method, property, etc. definitions go here
A custom protocol might not need to extend any other protocol at all. In this
case, we need to explicitly tell Python to define an ObjCProtocol
.
Normally Python detects the metaclass automatically by examining the base
classes, but in this case there are none, so we need to specify the metaclass
manually.
class MyProtocol(metaclass=ObjCProtocol):
# method, property, etc. definitions go here
Defining methods¶
- rubicon.objc.api.objc_method(py_method)¶
Exposes the decorated method as an Objective-C instance method in a custom class or protocol.
In a custom Objective-C class, decorating a method with
@objc_method
makes it available to Objective-C: a corresponding Objective-C method is created in the new Objective-C class, whose implementation calls the decorated Python method. The Python method receives all arguments (includingself
) from the Objective-C method call, and its return value is passed back to Objective-C.In a custom Objective-C protocol, the behavior is similar, but the method body is ignored, since Objective-C protocol methods have no implementations. By convention, the method body in this case should be empty (
pass
). (Since the method is never called, you could put any other code there as well, but doing so is misleading and discouraged.)
- rubicon.objc.api.objc_classmethod(py_method)¶
Exposes the decorated method as an Objective-C class method in a custom class or protocol.
This decorator behaves exactly like
@objc_method
, except that the decorated method becomes a class method, so it is exposed on the Objective-C class rather than its instances.
Method naming¶
The name of a Python-defined Objective-C method is same as the Python method’s
name, but with all underscores (_
) replaced with colons (:
) — for
example, initWithWidth_height_
becomes initWithWidth:height:
.
Warning
The Objective-C language imposes certain requirements on the usage of
colons in method names: a method’s name must contain exactly as many colons
as the method has arguments (excluding the implicit self
and _cmd
parameters), and the name of a method with arguments must end with a colon.
For example, a method called init
takes no arguments, initWithSize:
takes a single argument, initWithWidth:height:
takes two, etc.
initWithSize:spam
is an invalid method name.
These requirements are not enforced by the Objective-C runtime, but methods that do not follow them cannot easily be used from regular Objective-C code.
In addition, although the Objective-C language allows method names with
multiple consecutive colons or a colon at the start of the name, such names
are considered bad style and never used in practice. For example,
spam::
, :ham:
, and :
are unusual, but valid method names.
Future versions of Rubicon may warn about or disallow such nonstandard method names.
Parameter and return types¶
The argument and return types of a Python-created Objective-C method are
determined based on the Python method’s type annotations. The annotations may
contain any ctypes
type, as well as any of the Python types accepted by
ctype_for_type()
. If a parameter or the return type
is not specified, it defaults to ObjCInstance
. The self
parameter
is special-cased — its type is always ObjCInstance
, even if
annotated otherwise. To annotate a method as returning void
, set its return
type to None
.
Before being passed to the Python method, any object parameters
(objc_id
) are automatically converted to
ObjCInstance
. If the method returns an Objective-C object, it is
converted using ns_from_py()
before being returned to Objective-C. These
automatic conversions can be disabled by using objc_rawmethod()
instead
of objc_method()
.
The implicit _cmd
parameter is not passed to the Python method, as it is
normally redundant and not needed. If needed, the _cmd
parameter can be
accessed by using objc_rawmethod()
instead of objc_method()
.
- rubicon.objc.api.objc_rawmethod(py_method)¶
Exposes the decorated method as an Objective-C instance method in a custom class, with fewer convenience features than
objc_method()
.This decorator behaves similarly to
@objc_method
. However, unlike withobjc_method()
, no automatic conversions are performed (aside from those byctypes
). This means that all parameter and return types must be provided asctypes
types (noctype_for_type()
conversion is performed), all arguments are passed in their raw form as received fromctypes
, and the return value must be understood byctypes
.In addition, the implicit
_cmd
parameter is exposed to the Python method, which is not the case when usingobjc_method()
. This means that the decorated Python method must always have an additional_cmd
parameter afterself
; if it is missing, there will be errors at runtime due to mismatched argument counts. Likeself
,_cmd
never needs to be annotated, and any annotations on it are ignored.
Defining properties and ivars
¶
- rubicon.objc.api.objc_property(vartype=<class 'rubicon.objc.runtime.objc_id'>, weak=False)¶
Defines a property in a custom Objective-C class or protocol.
This class should be called in the body of an Objective-C subclass or protocol, for example:
class MySubclass(NSObject): counter = objc_property(NSInteger)
The property type may be any
ctypes
type, as well as any of the Python types accepted byctype_for_type()
.Defining a property automatically defines a corresponding getter and setter. Following standard Objective-C naming conventions, for a property
name
the getter is calledname
and the setter is calledsetName:
.In a custom Objective-C class, implementations for the getter and setter are also generated, which store the property’s value in an
ivar
called_name
. If the property has an object type, the generated setter keeps the stored object retained, and releases it when it is replaced.In a custom Objective-C protocol, only the metadata for the property is generated.
If
weak
isTrue
, the property will be created as a weak property. When assigning an object to it, the reference count of the object will not be increased. When the object is deallocated, the property value is set to None. Weak properties are only supported for Objective-C or Python object types.
- rubicon.objc.api.objc_ivar(vartype)¶
Defines an
ivar
in a custom Objective-C class.If you want to store additional data on a custom Objective-C class, it is recommended to use properties (
objc_property()
) instead ofivars
. Properties are a more modern and high-level Objective-C feature, which automatically deal with reference counting for objects, and creation of getters and setters.The
ivar
type may be anyctypes
type.Unlike properties, the contents of an
ivar
cannot be accessed or modified using Python attribute syntax. Instead, theget_ivar()
andset_ivar()
functions need to be used.
- rubicon.objc.api.get_ivar(obj, varname, weak=False)¶
Get the value of obj’s
ivar
namedvarname
.The returned object is a
ctypes
data object.For non-object types (everything except
objc_id
and subclasses), the returned data object is backed by theivar
’s actual memory. This means that the data object is only usable as long as the “owner” object is alive, and writes to it will directly change theivar
’s value.For object types, the returned data object is independent of the
ivar
’s memory. This is because objectivars
may be weak, and thus cannot always be accessed directly by their address.
Objective-C blocks¶
Blocks are the Objective-C equivalent of function objects, so Rubicon provides ways to call Objective-C blocks from Python and to pass Python callables to Objective-C as blocks.
Automatic conversion¶
If an Objective-C method returns a block (according to its type encoding),
Rubicon will convert the return value to a special ObjCInstance
that
can be called in Python:
block = an_objc_instance.methodReturningABlock()
res = block(arg, ...)
Similarly, if an Objective-C method has a parameter that expects a block, you can pass in a Python callable object, and it will be converted to an Objective-C block. In this case, the callable object needs to have parameter and return type annotations, so that Rubicon can expose this type information to the Objective-C runtime:
def result_handler(res: objc_id) -> None:
print(ObjCInstance(res))
an_objc_instance.doSomethingWithResultHandler(result_handler)
If you are writing a custom Objective-C method (see
Creating custom Objective-C classes and protocols), you can annotate parameter or return
types using objc_block
so that Rubicon converts
them appropriately:
class AnObjCClass(NSObject):
@objc_method
def methodReturningABlock() -> objc_block:
def the_block(arg: NSInteger) -> NSUInteger:
return abs(arg)
return the_block
@objc_method
def doSomethingWithResultHandler_(result_handler: objc_block) -> None:
res = SomeClass.someMethod()
result_handler(res)
Note
These automatic conversions are mostly equivalent to the manual conversions described in the next section. There are internal technical differences between automatic and manual conversions, but they are not noticeable to most users.
The internals of automatic conversion and
objc_block
handling may change in the
future, so if you need more control over the block conversion process, you
should use the manual conversions described in the next section.
Manual conversion¶
These classes are used to manually convert blocks to Python callables and vice versa. You may need to use them to perform these conversions outside of Objective-C method calls, or if you need more control over the block’s type signature.
- class rubicon.objc.api.ObjCBlock(pointer[, return_type, *arg_types])¶
Python wrapper for an Objective-C block object.
This class is used to manually wrap an Objective-C block so that it can be called from Python. Usually Rubicon will do this automatically, if the block object was returned from an Objective-C method whose return type is declared to be a block type. If this automatic detection fails, for example if the method’s return type is generic
id
, Rubicon has no way to tell that the object in question is a block rather than a regular Objective-C object. In that case, the object needs to be manually wrapped usingObjCBlock
.The constructor takes a block object, which can be either an
ObjCInstance
, or a rawobjc_id
pointer.Note
objc_block
is also accepted, because it is a subclass ofobjc_id
). Normally you do not need to make use of this, because in most cases Rubicon will automatically convertobjc_block
s to a callable object.In most cases, Rubicon can automatically determine the block’s return type and parameter types. If a block object doesn’t have return/parameter type information at runtime, Rubicon will raise an error when attempting to convert it. In that case, you need to explicitly pass the correct return type and parameter types to
ObjCBlock
using therestype
andargtypes
parameters.- __call__(*args)¶
Invoke the block object with the given arguments.
The arguments and return value are converted from/to Python objects according to the default
ctypes
rules, based on the block’s return and parameter types.
- class rubicon.objc.api.Block(func[, restype, *argtypes])¶
A wrapper that exposes a Python callable object to Objective-C as a block.
The constructor accepts any Python callable object.
If the callable has parameter and return type annotations, they are used as the block’s parameter and return types. This allows using
Block
as a decorator:@Block def the_block(arg: NSInteger) -> NSUInteger: return abs(arg)
For callables without type annotations, the parameter and return types need to be passed to the
Block
constructor in therestype
andargtypes
arguments:the_block = Block(abs, NSUInteger, NSInteger)
Defining custom subclasses of ObjCInstance
¶
The following functions can be used to register custom subclasses of
ObjCInstance
to be used when wrapping instances of a certain
Objective-C class. This mechanism is for example used by Rubicon to provide
Python-style operators and methods on standard Foundation classes, such as
NSString
and NSDictionary
.
- rubicon.objc.api.register_type_for_objcclass(pytype, objcclass)¶
Register a conversion from an Objective-C class to an
ObjCInstance
subclass.After a call of this function, when Rubicon wraps an Objective-C object that is an instance of
objcclass
(or a subclass), the Python object will have the classpytype
rather thanObjCInstance
. Seetype_for_objcclass()
for a full description of the lookup process.Warning
This function should only be called if no instances of
objcclass
(or a subclass) have been wrapped by Rubicon yet. If the function is called later, it will not fully take effect: the types of existing instances do not change, and mappings for subclasses ofobjcclass
are not updated.
- rubicon.objc.api.for_objcclass(objcclass)¶
Decorator for registering a conversion from an Objective-C class to an
ObjCInstance
subclass.This is equivalent to calling
register_type_for_objcclass()
on the decorated class.
- rubicon.objc.api.type_for_objcclass(objcclass)¶
Look up the
ObjCInstance
subclass used to represent instances of the given Objective-C class in Python.If the exact Objective-C class is not registered, each superclass is also checked, defaulting to
ObjCInstance
if none of the classes in the superclass chain is registered. Afterwards, all searched superclasses are registered for theObjCInstance
subclass that was found. (This speeds up future lookups, and ensures that previously computed mappings are not changed by unrelated registrations.)This method is mainly intended for internal use by Rubicon, but is exposed in the public API for completeness.
- rubicon.objc.api.unregister_type_for_objcclass(objcclass)¶
Unregister a conversion from an Objective-C class to an
ObjCInstance
subclass.Warning
This function should only be called if no instances of
objcclass
(or a subclass) have been wrapped by Rubicon yet. If the function is called later, it will not fully take effect: the types of existing instances do not change, and mappings for subclasses ofobjcclass
are not removed.
- rubicon.objc.api.get_type_for_objcclass_map()¶
Get a copy of all currently registered
ObjCInstance
subclasses as a mapping.Keys are Objective-C class addresses as
int
s.