Search code examples
iosswiftabaddressbook

iOS ABAddressBook How to change existing phone number?


I want to check if contact exists and change its phone number if it does.

I tried to do so and got exc_bad_access in the last line of code:

if doesPersonExistWithFirstName(firstName: "Call Recorder", inAddressBook: adbk)
        {
            let existingContact: ABRecord! = ABAddressBookGetPersonWithRecordID(adbk, recordID).takeRetainedValue()
            var success: Bool = false
            let phoneNumbers: ABMutableMultiValue = ABMultiValueCreateMutable(ABPropertyType(kABMultiStringPropertyType)).takeRetainedValue()
            var error: Unmanaged<CFError>? = nil

            success = ABMultiValueAddValueAndLabel(phoneNumbers, recordServicePhoneNumber!, kABPersonPhoneMainLabel, nil)
            print("setting phone number successful? \(success)")
            success = ABRecordSetValue(existingContact, kABPersonPhoneProperty, phoneNumbers, &error)
            print("adding phone numbers successful? \(success)")

            success = ABAddressBookAddRecord(adbk, existingContact, &error)
            print("Adbk addRecord successful? \(success)")
            success = ABAddressBookSave(adbk, &error)
            print("Adbk Save successful? \(success)")
        }
        else
        {
            let newContact: ABRecord! = ABPersonCreate().takeRetainedValue()
            var success: Bool = false
            let newFirstName: NSString = "Call Recorder"
            let image: UIImage = UIImage(named: "record")!
            let imageData = UIImagePNGRepresentation(image)
            let phoneNumbers: ABMutableMultiValue = ABMultiValueCreateMutable(ABPropertyType(kABMultiStringPropertyType)).takeRetainedValue()
            var error: Unmanaged<CFError>? = nil

            success = ABRecordSetValue(newContact, kABPersonFirstNameProperty, newFirstName as CFTypeRef, &error)
            print("setting first name was successful? \(success)")
            success = ABMultiValueAddValueAndLabel(phoneNumbers, recordServicePhoneNumber!, kABPersonPhoneMainLabel, nil)
            print("setting phone number successful? \(success)")
            success = ABPersonSetImageData(newContact, imageData as CFData!, &error)
            print("setting image successful? \(success)")
            success = ABRecordSetValue(newContact, kABPersonPhoneProperty, phoneNumbers, &error)
            print("adding phone numbers successful? \(success)")
            success = ABAddressBookAddRecord(adbk, newContact, &error)
            print("Adbk addRecord successful? \(success)")
            success = ABAddressBookSave(adbk, &error)
            print("Adbk Save successful? \(success)")
        }
    } **// I get exception here**

Code works, phone number changes correctly, but app crashes every time I run it.

UPD: I've ran zombies instrument and it gave me this:

An Objective-C message was sent to a deallocated 'CPRecord' object (zombie) at address: 0x7c6a3200.  

UPD2: I found out this line crashes app:

let existingContact: ABRecord! = ABAddressBookGetPersonWithRecordID(adbk, recordID).takeRetainedValue()

What can be the problem?


Solution

  • The problem was in this line as I thought:

    let existingContact: ABRecord! = ABAddressBookGetPersonWithRecordID(adbk, recordID).takeRetainedValue()
    

    I changed my function so now it uses ABRecord instead of ABRecordID to access required record.

    Fixed code:

    if doesPersonExistWithFirstName(firstName: "Call Recorder", inAddressBook: adbk)
            {
                var success: Bool = false
                let phoneNumbers: ABMutableMultiValue = ABMultiValueCreateMutable(ABPropertyType(kABMultiStringPropertyType)).takeRetainedValue()
                var error: Unmanaged<CFError>? = nil
    
                success = ABMultiValueAddValueAndLabel(phoneNumbers, recordServicePhoneNumber!, kABPersonPhoneMainLabel, nil)
                print("setting phone number successful? \(success)")
                success = ABRecordSetValue(existingContact, kABPersonPhoneProperty, phoneNumbers, &error)
                print("adding phone numbers successful? \(success)")
                success = ABAddressBookAddRecord(adbk, existingContact, &error)
                print("Adbk addRecord successful? \(success)")
                success = ABAddressBookSave(adbk, &error)
                print("Adbk Save successful? \(success)")
            }
            else
            {
                let newContact: ABRecord! = ABPersonCreate().takeRetainedValue()
                var success: Bool = false
                let newFirstName: NSString = "Call Recorder"
                let image: UIImage = UIImage(named: "record")!
                let imageData = UIImagePNGRepresentation(image)
                let phoneNumbers: ABMutableMultiValue = ABMultiValueCreateMutable(ABPropertyType(kABMultiStringPropertyType)).takeRetainedValue()
                var error: Unmanaged<CFError>? = nil
    
                success = ABRecordSetValue(newContact, kABPersonFirstNameProperty, newFirstName as CFTypeRef, &error)
                print("setting first name was successful? \(success)")
                success = ABMultiValueAddValueAndLabel(phoneNumbers, recordServicePhoneNumber!, kABPersonPhoneMainLabel, nil)
                print("setting phone number successful? \(success)")
                success = ABPersonSetImageData(newContact, imageData as CFData!, &error)
                print("setting image successful? \(success)")
                success = ABRecordSetValue(newContact, kABPersonPhoneProperty, phoneNumbers, &error)
                print("adding phone numbers successful? \(success)")
                success = ABAddressBookAddRecord(adbk, newContact, &error)
                print("Adbk addRecord successful? \(success)")
                success = ABAddressBookSave(adbk, &error)
                print("Adbk Save successful? \(success)")
            }
    

    existingContacts implementation:

    var existingContact: ABRecord! = ABPersonCreate().takeRetainedValue()
    func doesPersonExistWithFirstName(firstName paramFirstName: String,
                                          inAddressBook addressBook: ABAddressBook) -> Bool
        {
            let people = ABAddressBookCopyArrayOfAllPeople(addressBook).takeRetainedValue() as NSArray as [ABRecord]
            for person: ABRecord in people
            {
                let firstName = ABRecordCopyValue(person, kABPersonFirstNameProperty).takeRetainedValue() as! String
                if firstName == paramFirstName
                {
                    existingContact = person
                    return true
                }
            }
            return false
        }