Skip to content

Commit a20f602

Browse files
feat: allow deleting contacts by identifier (#12)
This PR makes it possible to delete contacts by identifier which is less error prone than by predicate when you want to delete a single contact.
1 parent 902406f commit a20f602

File tree

4 files changed

+55
-10
lines changed

4 files changed

+55
-10
lines changed

README.md

100644100755
Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -180,13 +180,14 @@ const success = contacts.addNewContact({
180180
console.log(`New contact was ${success ? 'saved' : 'not saved'}.`)
181181
```
182182

183-
### `contacts.deleteContact(name)`
183+
### `contacts.deleteContact({ identifier, name })`
184184

185-
* `name` String (required) - The first, middle, last, or full name of a contact.
185+
* `identifier` String (optional) - The contact's unique identifier.
186+
* `name` String (optional) - The first, middle, last, or full name of a contact.
186187

187188
Returns `Boolean` - whether the contact was deleted successfully.
188189

189-
Deletes a contact to the user's contacts database.
190+
Deletes a contact from the user's contacts database.
190191

191192
If a contact's full name is 'Shelley Vohr', I could pass 'Shelley', 'Vohr', or 'Shelley Vohr' as `name`.
192193
However, you should take care to specify `name` to such a degree that you can be confident the first contact to be returned from a predicate search is the contact you intend to delete.

contacts.mm

100644100755
Lines changed: 30 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -324,6 +324,22 @@ CNAuthorizationStatus AuthStatus() {
324324
error:nil];
325325
}
326326

327+
// Returns all contacts in the CNContactStore matching an identifier
328+
NSArray *FindContactsWithIdentifier(const std::string &identifier_string, Napi::Array extra_keys) {
329+
CNContactStore *addressBook = [[CNContactStore alloc] init];
330+
331+
NSString *identifier = [NSString stringWithUTF8String:identifier_string.c_str()];
332+
333+
NSArray *identifiers = @[identifier];
334+
335+
NSPredicate *predicate = [CNContact predicateForContactsWithIdentifiers:identifiers];
336+
337+
return
338+
[addressBook unifiedContactsMatchingPredicate:predicate
339+
keysToFetch:GetContactKeys(extra_keys)
340+
error:nil];
341+
}
342+
327343
// Creates a new CNContact in order to update, delete, or add it to the
328344
// CNContactStore.
329345
CNMutableContact *CreateCNMutableContact(Napi::Object contact_data) {
@@ -472,8 +488,20 @@ CNAuthorizationStatus AuthStatus() {
472488
if (AuthStatus() != CNAuthorizationStatusAuthorized)
473489
return Napi::Boolean::New(env, false);
474490

475-
const std::string name_string = info[0].As<Napi::String>().Utf8Value();
476-
NSArray *cncontacts = FindContacts(name_string, Napi::Array::New(env));
491+
Napi::Object contact_data = info[0].As<Napi::Object>();
492+
493+
NSArray *cncontacts;
494+
if (contact_data.Has("identifier")) {
495+
const std::string identifier =
496+
contact_data.Get("identifier").As<Napi::String>().Utf8Value();
497+
cncontacts = FindContactsWithIdentifier(identifier, Napi::Array::New(env));
498+
} else if (contact_data.Has("name")) {
499+
const std::string name_string =
500+
contact_data.Get("name").As<Napi::String>().Utf8Value();
501+
cncontacts = FindContacts(name_string, Napi::Array::New(env));
502+
} else {
503+
return Napi::Boolean::New(env, false);
504+
}
477505

478506
CNContact *contact = (CNContact *)[cncontacts objectAtIndex:0];
479507
CNSaveRequest *request = [[CNSaveRequest alloc] init];

index.js

100644100755
Lines changed: 14 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,23 @@ function updateContact(contact) {
150150
return contacts.updateContact.call(this, contact)
151151
}
152152

153-
function deleteContact(name) {
154-
if (typeof name !== 'string') {
153+
function deleteContact(contact) {
154+
if (!contact || Object.keys(contact).length === 0) {
155+
throw new TypeError('contact must be a non-empty object')
156+
}
157+
158+
const hasIdentifier = contact.hasOwnProperty('identifier')
159+
const hasName = contact.hasOwnProperty('name')
160+
161+
if (hasIdentifier && typeof contact.identifier !== 'string') {
162+
throw new TypeError('identifier must be a string')
163+
}
164+
165+
if (hasName && typeof contact.name !== 'string') {
155166
throw new TypeError('name must be a string')
156167
}
157168

158-
return contacts.deleteContact.call(this, name)
169+
return contacts.deleteContact.call(this, contact)
159170
}
160171

161172
module.exports = {

test/module.spec.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -150,12 +150,17 @@ describe('node-mac-contacts', () => {
150150
})
151151
})
152152

153-
describe('deleteContact(name)', () => {
153+
describe('deleteContact({ name, identifier })', () => {
154154
it('should throw if name is not a string', () => {
155155
expect(() => {
156-
deleteContact(12345)
156+
deleteContact({ name: 12345 })
157157
}).to.throw(/name must be a string/)
158158
})
159+
it('should throw if identifier is not a string', () => {
160+
expect(() => {
161+
deleteContact({ identifier: 12345 })
162+
}).to.throw(/identifier must be a string/)
163+
})
159164
})
160165

161166
describe('updateContact(contact)', () => {

0 commit comments

Comments
 (0)