Search code examples
contactspyobjccncontactstorecnmutablecontactpyobjc-framework-contacts

AttributeError When Setting givenName on CNMutableContact in Python with PyObjC


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! 🙏


Solution

  • 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.