Safe Rust bindings for Apple's Contacts framework on macOS.
Status: v0.2.2 reaches 100% top-level
Contacts.frameworkdeclaration coverage, adding exhaustive constant wrappers plusCNMutablePostalAddress,CNEntityType,CNChangeHistoryEventVisitor, and rawNSStringkey-descriptor support.
use contacts::prelude::*;
fn main() -> Result<(), Box<dyn std::error::Error>> {
let status = CNContactStore::authorization_status();
println!("contacts authorization: {status:?}");
if !status.is_authorized() {
return Ok(());
}
let store = CNContactStore::new()?;
let request = CNContactFetchRequest::new([
CNContactKey::GivenName,
CNContactKey::FamilyName,
CNContactKey::OrganizationName,
CNContactKey::EmailAddresses,
])
.with_sort_order(CNContactSortOrder::GivenName)
.with_descriptor(CNContactFormatter::descriptor_for_required_keys_for_style(
CNContactFormatterStyle::FullName,
));
for contact in store.enumerate_contacts_limited(&request, 5)? {
let display_name = CNContactFormatter::string_from_contact(
&contact,
CNContactFormatterStyle::FullName,
)?
.unwrap_or_else(|| contact.display_name());
println!("{display_name}");
}
Ok(())
}Enable the async feature for executor-agnostic Future wrappers over
CNContactStore completion-handler APIs:
[dependencies]
contacts = { version = "0.3", features = ["async"] }use contacts::async_api::AsyncCNContactStore;
use contacts::store::CNEntityType;
async fn run() -> Result<(), Box<dyn std::error::Error>> {
// Request access asynchronously
let granted = AsyncCNContactStore::request_access(CNEntityType::Contacts).await?;
println!("granted: {granted}");
// Enumerate contacts asynchronously (collects all into Vec)
use contacts::store::CNContactStore;
use contacts::fetch_request::CNContactFetchRequest;
use contacts::contact::CNContactKey;
let store = CNContactStore::new()?;
let request = CNContactFetchRequest::new([CNContactKey::GivenName, CNContactKey::FamilyName]);
let contacts = AsyncCNContactStore::enumerate_contacts(&store, &request).await?;
println!("{} contacts", contacts.len());
Ok(())
}| Apple API | Rust Future | Notes |
|---|---|---|
requestAccess(for:completionHandler:) |
RequestAccessFuture |
Future<bool> |
enumerateContacts(with:usingBlock:) |
EnumerateContactsFuture |
Collects all → Future<Vec<CNContact>> |
A Stream-based Tier-2 wrapper for incremental enumeration will follow in a
future release.
CNContactStoreauthorization, fetch, save, container, group, history-token,CNEntityType, andCNContactsUserDefaultshelpers- Expanded
CNContact/CNMutableContactcoverage for names, phonetics, notes, image data, dates, relations, social profiles, instant-message addresses, raw field constants, andNSItemProviderround-tripping CNMutableGroup,CNMutablePostalAddress, subgroup/member save operations, and group/container predicate factoriesCNContactFetchRequest,CNChangeHistoryFetchRequest,CNFetchRequest, andCNKeyDescriptorbuilders for comparator, formatter, vCard, change-history, and rawNSStringkey descriptorsCNContactFormatterandCNPostalAddressFormatterstring / attributed-string helpersCNChangeHistoryFetchRequest,CNChangeHistoryEventVisitor,CNFetchResult, andCNContactStoreDidChangeNotificationsupport- Exhaustive typed wrappers for Contacts keys, labels, services, relation labels,
CNErrorCode,CNErrorDomain, andCNErrorUserInfo* CNContactVCardSerializationround-tripping betweenCNContactvalues and vCard bytes
The crate ships thirteen numbered examples, one per logical area, including:
01_contacts_store_smoke07_format_and_print08_change_notifications11_vcard_serialization13_user_defaults
Run them all with:
for ex in examples/*.rs; do
cargo run --example "$(basename "$ex" .rs)"
donecargo clippy --all-targets -- -D warnings
cargo testContacts.framework access is gated by the user's privacy settings. Store, group, and container examples never force a permission prompt; they report current availability and keep running in restricted environments.
See COVERAGE.md for the per-area implementation matrix and COVERAGE_AUDIT.md for the exhaustive 100% top-level declaration audit.
Licensed under either of Apache-2.0 or MIT at your option.