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.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()
ā 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
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:
class SimpleContact:
def __init__(self, first_name):
self.contact = CNMutableContact.new()
self.contact.setGivenName_(first_name)
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.