AttributeError When Setting givenName on CNMutableContact in Python with PyObjC

17 Views Asked by At

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!

1

There are 1 best solutions below

3
Ronald Oussoren On BEST ANSWER

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.