I'm working on a Python script on macOS using PyObjC to interact with the Contacts framework. I've created a simple class to encapsulate contact creation, setting the givenName property, and saving it to the Contacts app.
When I set the givenName on a CNMutableContact object, Python throws an AttributeError, claiming that the givenName attribute is read-only.
SimpleContact Python Class simplecontact.py:
#!/usr/bin/env python3
import objc
from Contacts import CNMutableContact, CNContactStore, CNSaveRequest
class SimpleContact:
def __init__(self, first_name):
self.contact = CNMutableContact.new()
self.contact.givenName = first_name
def save(self):
store = CNContactStore.alloc().init()
request = CNSaveRequest.alloc().init()
request.addContact_toContainerWithIdentifier_(self.contact, None)
error = objc.nil
store.executeSaveRequest_error_(request, error)
if error is not objc.nil:
print(f"Failed to save contact: \n{error}")
else:
print("Contact saved successfully.")
if __name__ == "__main__":
simple_contact = SimpleContact("John99999")
simple_contact.save()
Error Message:
➜ python simplecontact.py
Traceback (most recent call last):
File "~/Desktop/simple-contact/simplecontact.py", line 25, in <module>
simple_contact = SimpleContact("John99999")
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "~/Desktop/simple-contact/simplecontact.py", line 9, in __init__
self.contact.givenName = first_name
^^^^^^^^^^^^^^^^^^^^^^
AttributeError: 'CNMutableContact' object attribute 'givenName' is read-only
Environment Info:
- Python version: 3.11.8
- macOS version: 14.3.1 (Build 23D60)
Relevant package versions:
- pyobjc==10.2
- pyobjc-core==10.2
- pyobjc-framework-Contacts==10.2
I expected to be able to set the givenName property on a CNMutableContact object without issues, as per the Contacts framework documentation.
Is this an issue with PyObjC? Or am I missing something in how CNMutableContact properties should be handled in Python?
Any insights or workarounds would be greatly appreciated!
You should use a setter method to set the field, like so:
Some background: PyObjC doesn't expose ObjC properties as such, but only exposes the individual accessor methods (getter and setter). The reason for that is simple: In ObjC properties and methods are in different namespaces, whereas in Python classes have a single namespace for all kinds of attributes.
I'm thinking about changing this in a future version of PyObjC, but it will take years before the default is changed because this would be a breaking change for code using PyObjC.