Contacts: remove member from group not working on macOS

Hi, In my app, I have an option to remove a contact from a contact group (using the Contacts framework), and it's been working fine till recently users of the macOS version reported that it's not working. I have been using the CNSaveRequest removeMember(contact, from: group) API. The same API works fine on iOS. I'm not sure when it started but it seems to be affecting macOS14.6 as well as 15.1.

I was able to reproduce it in a small test project as well, and have the same experience (the API works on iOS but not on macOS), so it definitely seems like a problem with the framework. Can someone confirm this, and/or suggest a workaround?

Here's the code I run to test it out ...a simple SwiftUI view that has 4 buttons:

  1. Create contact and group
  2. Add contact to group
  3. Remove contact from group
  4. (optional) cleanup by deleting contact and group

It's the 3rd step that seems to fail on macOS, but works fine on iOS.

Here's the code to test it out:

struct ContentView: View {
    
    let contactsModel = ContactsStoreModel()
    
    var body: some View {
        VStack (alignment: .center, spacing: 15){
            Button ("1. Add Contact And Group") {
                print("add contact button pressed")
                contactsModel.addTestContact()
                if let _ = contactsModel.createdContact {
                    print("created contact success")
                }
            }
            
            Button ("2. Add Contact To Group") {
                print("add to group button pressed")
                contactsModel.addContactToGroup()
            }
            
            Button ("3. Remove Contact From Group") {
                print("remove from group button pressed")
                contactsModel.removeContactFromGroup()
            }
            
            Button ("4. Delete Contact and Group") {
                print("remove from group button pressed")
                contactsModel.deleteContactAndGroup()
            }
        }
        .padding()
    }
}

#Preview {
    ContentView()
}


@available(iOS 13.0, *)
@objc final class ContactsStoreModel: NSObject, ObservableObject {
    let contactStore = CNContactStore()
    var createdContact : CNContact?
    var createdGroup : CNGroup?
    
    public func addTestContact() {
        let storeContainer =   contactStore.defaultContainerIdentifier()
        let contact = CNMutableContact()
        contact.givenName = "Testing"
        contact.familyName = "User"
        contact.phoneNumbers = [CNLabeledValue(label: "Cell", value: CNPhoneNumber(stringValue: "1234567890"))]
        
        let group = CNMutableGroup()
        group.name = "Testing Group"
        print("create contact id = \(contact.identifier)")
        print("create group id = \(group.identifier)")
        do {
            let saveRequest = CNSaveRequest()
            saveRequest.transactionAuthor = "TestApp"
            
            saveRequest.add(contact, toContainerWithIdentifier: storeContainer)
            saveRequest.add(group, toContainerWithIdentifier: storeContainer)

            try contactStore.execute(saveRequest)
            
            createdContact = contact
            createdGroup = group
            
        } catch {
            print("error in store execute = \(error)")
        }
    }
    
    public func addContactToGroup() {
        
        if let contact = createdContact, let group = createdGroup {
            do {
                let saveRequest = CNSaveRequest()
                saveRequest.transactionAuthor = "TestApp"
                
                saveRequest.addMember(contact, to: group)
                
                try contactStore.execute(saveRequest)
            }
            catch {
                print("error in store execute = \(error)")
            }
        }
    }
    
    public func removeContactFromGroup() {
        if let contact = createdContact, let group = createdGroup {
            do {
                let saveRequest = CNSaveRequest()
                saveRequest.transactionAuthor = "TestApp"
                
                saveRequest.removeMember(contact, from: group)
                
                try contactStore.execute(saveRequest)
            }
            catch {
                print("error in store execute = \(error)")
            }
        }
    }
    
    
    public func addGroupAndContact() {
        let storeContainer =   contactStore.defaultContainerIdentifier()
        let group = CNMutableGroup()
        group.name = "Test Group"

        print("create group id = \(group.identifier)")
        
        if let contact = createdContact {
            
            do {
                let saveRequest = CNSaveRequest()
                saveRequest.transactionAuthor = "TestApp"
                
                saveRequest.add(group, toContainerWithIdentifier: storeContainer)
                saveRequest.addMember(contact, to: group)
                
                try contactStore.execute(saveRequest)
                
                createdGroup = group
            } catch {
                print("error in store execute = \(error)")
            }
        }
    }
    
    public func deleteContactAndGroup() {
        if let contact = createdContact, let group = createdGroup {
            do {
                let mutableGroup = group.mutableCopy() as! CNMutableGroup
                let mutableContact = contact.mutableCopy() as! CNMutableContact
                
                let saveRequest = CNSaveRequest()
                saveRequest.transactionAuthor = "TestApp"
                
                saveRequest.delete(mutableContact)
                saveRequest.delete(mutableGroup)
                try contactStore.execute(saveRequest)
            }
            catch {
                print("error in deleting store execute = \(error)")
            }
        }
    }
}

Please file a bug report using Feedback Assistant then post your Feedback number here.

Contacts: remove member from group not working on macOS
 
 
Q