PyObjC
PyObjC is a bridge between Python and Objective-C, allowing Python scripts to use and extend existing Objective-C class libraries, most importantly Apple's Cocoa. It enables the development of full-featured macOS applications in pure Python and seamless integration with Objective-C, C, and C++ code. PyObjC (version 12.1) is actively maintained and typically releases new versions aligning with macOS SDK updates and Python version support.
Warnings
- breaking PyObjC has dropped support for older Python versions in recent major releases. Version 12.0 dropped Python 3.9, and Version 11.0 dropped Python 3.8. Ensure your Python environment meets the minimum requirement (Python 3.10+ for PyObjC 12.x).
- breaking PyObjC 11.1 introduced significant changes to align with `clang`'s documentation for Automatic Reference Counting (ARC) for initializer methods. Methods in the 'init' family now correctly model stealing a reference to `self` and returning a new one. This can cause reference counting bugs to surface or lead to crashes if existing code relied on previous, incorrect behavior.
- gotcha Objective-C method names containing colons (e.g., `doSomething:withSomethingElse:`) are translated to Python by replacing colons with underscores (e.g., `doSomething_withSomethingElse_`). Each underscore signifies an argument. Missing an underscore or an argument will result in `AttributeError` or unexpected behavior.
- gotcha Using `__init__` in Python subclasses of Objective-C classes can have subtle interactions with `__new__`. While PyObjC 10.3 initially removed support for calling `__init__` when a user-defined `__new__` was present, this was partially reintroduced in 10.3.1. Code relying on PyObjC's provided `__new__` still cannot use `__init__`.
- breaking The `AVFAudio` framework, previously merged into `AVFoundation` bindings, was split into its own top-level package (`pyobjc-framework-AVFAudio`) starting with PyObjC 12.0.
- gotcha PyObjC is specifically designed for CPython on macOS. It does not support alternative Python runtimes like PyPy, Jython, or IronPython, and this is unlikely to change.
Install
-
pip install pyobjc -
pip install pyobjc-core pyobjc-framework-Cocoa pyobjc-framework-AVFoundation
Imports
- objc
import objc
- Foundation
from Foundation import NSObject, NSString
- AppKit
from AppKit import NSApplication, NSWindow
Quickstart
import objc
from Foundation import NSString, NSArray
# Working with Objective-C classes
hello = NSString.stringWithString_("Hello, World!")
print(f"Hello string length: {hello.length()}") # Expected: 13
# Creating and using Objective-C objects
# Note the 'None' to signify the end of the variadic arguments for arrayWithObjects_
my_array = NSArray.arrayWithObjects_("foo", "bar", "baz", None)
print(f"Array count: {my_array.count()}") # Expected: 3
print(f"Object at index 1: {my_array.objectAtIndex_(1)}") # Expected: bar
# Using the bridge constants
print(f"objc.YES: {objc.YES}") # Expected: True
print(f"objc.NO: {objc.NO}") # Expected: False
print(f"objc.nil: {objc.nil}") # Expected: None
# Proper memory management with autorelease pools (good practice for intensive ops)
with objc.autorelease_pool():
temp_strings = []
for i in range(5):
temp_string = NSString.stringWithFormat_("Item %d", i)
temp_strings.append(temp_string)
print(f"Temporary strings created: {len(temp_strings)}")
# Objects are automatically released when the pool exits