CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/pypi-pyobjc

PyObjC is a bridge between Python and Objective-C that allows full featured Cocoa applications to be written in pure Python.

Pending
Overview
Eval results
Files

categories.mddocs/

Categories and Method Addition

Category support for extending existing Objective-C classes with new methods. Categories allow adding functionality to existing classes without subclassing or modifying the original class definition, providing a powerful mechanism for extending system frameworks and third-party classes.

Capabilities

Class Method Addition

Functions for dynamically adding methods to existing Objective-C classes.

def classAddMethod(targetClass, method, targetMethod):
    """
    Add a method to an existing Objective-C class.
    
    Args:
        targetClass: The Objective-C class to modify
        method: The method selector to add
        targetMethod: The implementation function to add
        
    Usage:
        def new_method_impl(self, _cmd):
            return "Hello from new method!"
        
        objc.classAddMethod(
            NSString, 
            "newMethod", 
            new_method_impl
        )
    """

Category Decorator

Decorator for creating Objective-C categories from Python classes.

def category(baseClass):
    """
    Decorator to create an Objective-C category from a Python class.
    
    Args:
        baseClass: The Objective-C class to extend with category methods
        
    Returns:
        Decorator function that creates the category
        
    Usage:
        @objc.category(NSString)
        class NSStringExtensions:
            def isPythonic(self):
                return self.hasPrefix_("py") or self.hasSuffix_(".py")
            
            @objc.signature(b'@@:@')
            def stringByAppendingPythonSuffix_(self, suffix):
                return self.stringByAppendingFormat_("_py_%@", suffix)
    """

Category Implementation

Core category implementation class for managing method injection.

class Category:
    """
    Core implementation class for Objective-C categories.
    
    Manages the process of injecting methods from Python classes
    into existing Objective-C classes, handling method signatures,
    type conversion, and proper integration with the Objective-C runtime.
    """
    
    def __init__(self, baseClass):
        """
        Initialize a category for the specified base class.
        
        Args:
            baseClass: The Objective-C class to extend
        """
        
    def addMethod(self, method_name: str, implementation):
        """
        Add a method to the category.
        
        Args:
            method_name (str): Name of the method to add
            implementation: Python function implementing the method
        """
        
    def inject(self):
        """
        Inject all category methods into the target class.
        
        This method performs the actual modification of the Objective-C
        class, adding all methods defined in the category.
        """

Usage Examples

Adding Methods to NSString

import objc
from Foundation import NSString

@objc.category(NSString)
class NSStringPythonExtensions:
    
    def isPythonic(self):
        """Check if string contains Python-related keywords."""
        return (self.hasPrefix_("py") or 
                self.hasSuffix_(".py") or 
                self.containsString_("python"))
    
    @objc.signature(b'@@:@')
    def stringByAppendingPythonSuffix_(self, suffix):
        """Append a Python-style suffix to the string."""
        return self.stringByAppendingFormat_("_py_%@", suffix)
    
    @objc.signature(b'@@:')
    def pythonize(self):
        """Convert the string to a Python-friendly format."""
        return self.lowercaseString().stringByReplacingOccurrencesOfString_withString_(
            " ", "_"
        )

# Usage after category definition
test_string = NSString.stringWithString_("Hello World")
print(test_string.isPythonic())  # False
print(test_string.pythonize())   # "hello_world"

py_string = NSString.stringWithString_("python_script.py")
print(py_string.isPythonic())    # True
print(py_string.stringByAppendingPythonSuffix_("backup"))  # "python_script.py_py_backup"

Adding Methods to NSArray

import objc
from Foundation import NSArray, NSMutableArray

@objc.category(NSArray)
class NSArrayPythonExtensions:
    
    def pythonList(self):
        """Convert NSArray to Python list."""
        result = []
        for i in range(self.count()):
            result.append(self.objectAtIndex_(i))
        return result
    
    @objc.signature(b'@@:@')
    def arrayByApplyingBlock_(self, block):
        """Apply a block to each element and return new array."""
        result = NSMutableArray.alloc().init()
        for i in range(self.count()):
            item = self.objectAtIndex_(i)
            transformed = block(item)
            result.addObject_(transformed)
        return result.copy()
    
    def isEmpty(self):
        """Check if array is empty."""
        return self.count() == 0

# Usage
array = NSArray.arrayWithObjects_("apple", "banana", "cherry", None)
print(array.pythonList())  # ['apple', 'banana', 'cherry']
print(array.isEmpty())     # False

# Transform array elements
upper_array = array.arrayByApplyingBlock_(lambda x: x.uppercaseString())
print(upper_array.pythonList())  # ['APPLE', 'BANANA', 'CHERRY']

Adding Methods to Custom Classes

import objc
from Foundation import NSObject

# Define a simple custom class
class MyCustomClass(NSObject):
    def init(self):
        self = objc.super(MyCustomClass, self).init()
        if self is None:
            return None
        self._data = []
        return self

# Create category to extend it
@objc.category(MyCustomClass)
class MyCustomClassExtensions:
    
    def addItem_(self, item):
        """Add an item to the internal data storage."""
        self._data.append(item)
    
    def getItemCount(self):
        """Get the number of items stored."""
        return len(self._data)
    
    @objc.signature(b'@@:i')
    def getItemAtIndex_(self, index):
        """Get item at specific index."""
        if 0 <= index < len(self._data):
            return self._data[index]
        return None
    
    def clearAllItems(self):
        """Remove all items from storage."""
        self._data.clear()

# Usage
obj = MyCustomClass.alloc().init()
obj.addItem_("First item")
obj.addItem_("Second item")
print(obj.getItemCount())  # 2
print(obj.getItemAtIndex_(0))  # "First item"
obj.clearAllItems()
print(obj.getItemCount())  # 0

Low-level Method Addition

import objc
from Foundation import NSString

def custom_reverse_method(self, _cmd):
    """Custom method implementation that reverses the string."""
    # Get the string content
    original = str(self)
    # Reverse it
    reversed_str = original[::-1]
    # Return as NSString
    return NSString.stringWithString_(reversed_str)

# Add method directly to NSString class
objc.classAddMethod(
    NSString,
    "reverseString",
    custom_reverse_method
)

# Usage
test_string = NSString.stringWithString_("Hello World")
reversed_string = test_string.reverseString()
print(reversed_string)  # "dlroW olleH"

Category with Complex Method Signatures

import objc
from Foundation import NSString, NSArray

@objc.category(NSString)
class NSStringAdvancedExtensions:
    
    @objc.signature(b'@@:@@i')
    def stringByReplacingRange_withString_options_(self, range_dict, replacement, options):
        """
        Replace a range of characters with new string and options.
        
        Args:
            range_dict: Dictionary with 'location' and 'length' keys
            replacement: String to insert
            options: Replacement options
        """
        location = range_dict.get('location', 0)
        length = range_dict.get('length', 0)
        
        # Create NSRange equivalent
        before = self.substringToIndex_(location)
        after = self.substringFromIndex_(location + length)
        
        return before.stringByAppendingString_(
            replacement.stringByAppendingString_(after)
        )
    
    @objc.signature(b'@@:@')
    def componentsSeparatedByMultipleDelimiters_(self, delimiters_array):
        """
        Split string by multiple delimiters.
        
        Args:
            delimiters_array: NSArray of delimiter strings
        """
        result = NSArray.arrayWithObject_(self)
        
        for i in range(delimiters_array.count()):
            delimiter = delimiters_array.objectAtIndex_(i)
            new_result = []
            
            for j in range(result.count()):
                string_part = result.objectAtIndex_(j) 
                components = string_part.componentsSeparatedByString_(delimiter)
                for k in range(components.count()):
                    component = components.objectAtIndex_(k)
                    if component.length() > 0:
                        new_result.append(component)
            
            result = NSArray.arrayWithArray_(new_result)
        
        return result

# Usage
text = NSString.stringWithString_("apple,banana;cherry:date")
delimiters = NSArray.arrayWithObjects_(",", ";", ":", None)
components = text.componentsSeparatedByMultipleDelimiters_(delimiters)

print("Components:")
for i in range(components.count()):
    print(f"  {components.objectAtIndex_(i)}")
# Output:
#   apple
#   banana  
#   cherry
#   date

Best Practices

Method Naming

  • Follow Objective-C naming conventions (camelCase)
  • Use descriptive names that indicate the method's purpose
  • For methods that take parameters, end with an underscore

Type Signatures

  • Always provide type signatures for methods with complex parameters
  • Use @objc.signature() decorator for explicit type information
  • Test method signatures thoroughly to ensure proper bridge behavior

Memory Management

  • Categories inherit the memory management rules of their target class
  • Be careful with object retention and autorelease in category methods
  • Use objc.autorelease_pool() for methods that create many temporary objects

Error Handling

  • Categories should handle errors gracefully
  • Don't assume the target class's internal state
  • Validate parameters before using them in category methods

Install with Tessl CLI

npx tessl i tessl/pypi-pyobjc

docs

categories.md

core-bridge.md

decorators.md

framework-loading.md

index.md

protocols.md

type-system.md

utilities.md

tile.json