PyObjC DeviceCheck Framework
PyObjC-framework-DeviceCheck provides Python wrappers for Apple's DeviceCheck framework on macOS, allowing Python applications to interact with DeviceCheck APIs to manage device state and assert app integrity. It is part of the broader PyObjC project, a bridge that enables Python scripts to fully utilize Objective-C class libraries like Cocoa. The library is actively maintained, with version 12.1 being the current release, and follows a frequent release cadence tied to macOS SDK and Python version updates.
Warnings
- breaking PyObjC has dropped support for older Python versions. Version 12.0 removed support for Python 3.9, and version 11.0 removed support for Python 3.8. Users must ensure their Python environment meets the `>=3.10` requirement for PyObjC 12.1.
- breaking PyObjC 11.1 changed how it models Objective-C's Automatic Reference Counting (ARC) for 'init' methods. Methods in the 'init' family now correctly 'steal a reference to self and return a new reference' in alignment with `clang`'s documentation. This change can affect applications relying on the previous, less accurate reference counting behavior, especially for singletons or in multithreaded contexts.
- gotcha The `DeviceCheck` framework, and thus `pyobjc-framework-devicecheck`, only functions on physical macOS/iOS devices and will not work when run on a simulator. Calls to `DCDevice.currentDevice().isSupported()` will return `False` on simulators.
- gotcha Subclassing `NSString` or `NSMutableString` in Python using PyObjC is not supported and will lead to application crashes. These classes are treated as 'final' within the PyObjC bridge due to special internal handling.
- gotcha Specific `NSData` and `NSMutableData` initialization methods, such as `dataWithBytesNoCopy:length:` or `initWithBytesNoCopy:length:`, should not be used with Python buffers. Cocoa might attempt to `free()` the buffer when the `NSData` object is released, which is incompatible with Python's memory management and can cause crashes. For methods like `dataWithBytesNoCopy:length:freeWhenDone:`, the `freeWhenDone` argument *must* be `False`.
Install
-
pip install pyobjc-framework-devicecheck
Imports
- DCDevice
from DeviceCheck import DCDevice
- AppHelper
from PyObjCTools import AppHelper
Quickstart
import objc
from DeviceCheck import DCDevice
from PyObjCTools import AppHelper
import Foundation # For NSData, NSError types
def device_check_completion_handler(token_data, error):
"""Objective-C completion handler for generateTokenWithCompletionHandler_."""
if error:
print(f"Error generating token: {error.localizedDescription()}")
elif token_data:
# NSData object from Objective-C can be converted to Python bytes
python_token = token_data.bytes().tobytes()
print(f"Successfully generated DeviceCheck token (first 10 bytes): {python_token[:10]}...")
else:
print("No token data and no error received.")
AppHelper.stopEventLoop()
def main():
device = DCDevice.currentDevice()
if not device.isSupported():
print("DeviceCheck is not supported on this device (e.g., simulator).")
return
print("DeviceCheck is supported. Generating token...")
# Call the asynchronous Objective-C method with a Python callable as handler
device.generateTokenWithCompletionHandler_(device_check_completion_handler)
# Start the PyObjC event loop to process the asynchronous call
print("Starting event loop (waiting for token or error)...")
AppHelper.runEventLoop()
print("Event loop stopped.")
if __name__ == "__main__":
main()