ContactsManager is a comprehensive, cross-platform library designed to simplify access and management of contacts on Android and iOS devices. It provides a unified API for retrieving, normalizing, and displaying contact information including phone numbers and avatars, making it ideal for mobile applications built with Kotlin Multiplatform or native platforms.
- Retrieve contacts with names, phone numbers, email, Address and avatars.
- Fetch phone numbers with or without Country code.
- Support for Android and iOS platforms.
- Contact Avatar view with fallback placeholder with Initials of the contact, customizable with backgroundColor,textColor and cornerRadius
- Handles runtime permissions on Android and iOS.
- Observe Live changes to contacts even when app is in the background
- Save contacts option with native sheet
- Cross-platform API for Kotlin Multiplatform projects.
Add the following dependency to your build.gradle or build.gradle.kts:
dependencies {
implementation("network.chaintech:cmp-contact-manager:1.0.0")
}Make sure to include Maven Central in your repositories:
repositories {
mavenCentral()
}To get permission we are using CMPEasyPermission library which makes it really convenient and easy to get permission and it can also be used for many other permissions.
Check it out here 👉 https://github.com/Chaintech-Network/CMPEasyPermission
commonMain.dependencies {
implementation("network.chaintech:cmp-easy-permission:1.0.3")
}Add the following key to your Info.plist file to request access to contacts:
<key>NSContactsUsageDescription</key>
<string>This app requires access to contacts to display your contacts list.</string>##Start by creating a ContactRepository object
val repository = createContactsRepository(componentActivity = componentActivity) RequestPermission(
permission = PermissionState.READ_CONTACTS,
openSetting = true,
isGranted = { isGranted ->
permissionGranted = isGranted
scope.launch {
if (isGranted) {
withContext(Dispatchers.Default) {
contacts = repository.getContacts()
}
}
}
},
deniedDialogTitle = "App requires access to your contacts",
deniedDialogDesc = "You can enable this permission in the settings",
)LaunchedEffect(repository, permissionGranted) {
if (permissionGranted == true) {
repository.observeContactsChanges {
scope.launch(Dispatchers.Main) {
val newContacts = try { repository.getContacts() } catch (e: Exception) { emptyList() }
contacts = newContacts
}
}
}
}//Opens the native save contact sheet
scope.launch {
repository.saveContact(context = componentActivity)
}Native contacts sheet for saving contact with all native features
The Contact data class represents a contact with various properties and computed attributes for convenience.
id: String— Unique identifier of the contact.displayName: String— Full name of the contact.firstName: String— First name or Given Name of the contact.lastName: String— Last name or Family Name of the contact.phoneNumbers: List<String>— List of phone numbers in E.164 normalized format.imageByteArray: ByteArray?— Raw avatar image data if available.imageBitmap: Bitmap?— Platform-specific bitmap representation of the avatar (Android).email: String?— Email address of the contact.address: String?— Postal address of the contact.birthday: String?— Birthday information.company: String?— Company or organization the contact belongs to.
initials: String— The uppercase initials derived from thedisplayName(up to 2 characters).phoneNumbersWithoutCountryCode: List<String>— Phone numbers normalized by removing country codes, spaces, and dashes for easier comparison.
The ContactAvatarView composable is designed to display a contact's avatar image if available, or fallback to showing the contact's initials with a styled background.
contact: TheContactobject whose avatar or initials will be displayed.size: The size (width and height) of the avatar view, typically specified usingdp.cornerRadius: The corner radius to apply to the avatar's background or image, allowing for rounded corners.isRandomizedColor(optional): ABooleanflag indicating whether to use a randomly colored background with white initials when no avatar image is available. Defaults tofalse.
ContactAvatarView(
contact = contact,
size = 48.dp,
cornerRadius = 8.dp,
isRandomizedColor = true
)In this example, the avatar will display at 48 dp size with rounded corners of 8 dp. If the contact has an imageBitmap, it will be shown as the avatar. If imageBitmap is null, the view will display the contact's initials. Since isRandomizedColor is set to true, the background behind the initials will be a random color, and the initials will be rendered in white for better contrast.
If isRandomizedColor is not specified or set to false, the fallback background will use a default color scheme.
This approach ensures a visually consistent and appealing avatar display regardless of whether the contact has an image or not.
The library retrieves contact avatars when available. If no avatar is present, it provides a fallback option by displaying the initials of the contact's name. This ensures a consistent and visually appealing UI.
ContactAvatarView(contact = contact, size = 48.dp, cornerRadius = 8.dp)Use if phone number is needed without country code and leading 0 is also to be stripped
contact.phoneNumbersWithoutCountryCodeotherwise use
contact.phoneNumbers- The API is designed to be consistent across Android and iOS for Kotlin Multiplatform projects.
- Platform-specific implementations handle permissions and access nuances.
- Use the shared API for business logic and UI layer integration.
- The library supports asynchronous calls on iOS and synchronous or coroutine-based calls on Android.
For more information and updates, visit the GitHub repository.




