Skip to content
Document DB v12 - Improved Interceptors with Soft Delete Integration, AI protections, & Admin UI with Aspire Integration!How!?

Contact Store | Getting Started

A cross-platform library for accessing device contacts on Android and iOS. Provides full CRUD operations, a fluent async query builder, Shiny’s AccessState permission model, and dependency injection integration. Built on Shiny.Core, so it runs in any Shiny host — with or without .NET MAUI.

GitHub GitHub stars for shinyorg/contactstore
Downloads NuGet downloads for Shiny.Contacts
Frameworks
.NET MAUI
.NET
  • Full CRUD — create, read, update, and delete device contacts
  • Fluent async queriesQuery() returns a ContactQuery builder (Where/Search/OrderBy/Skip/TakeToListAsync(ct)) with native translation on both platforms
  • Shiny permissionsGetCurrentAccess() and RequestAccess() using Shiny.Core’s AccessState model
  • No MAUI required — built on Shiny.Core, so it works in any Shiny host
  • AOT compatible — trimmer and AOT safe
  • Dependency injection — register with a single AddContactStore() call
Platform Minimum Version
Android API 24
iOS 15.0
Changed files
  • MyApp/
Platforms/Android/AndroidManifest.xml
AndroidManifest.xml
1<?xml version="1.0" encoding="utf-8"?>2<manifest xmlns:android="http://schemas.android.com/apk/res/android">3	<application android:allowBackup="true" android:icon="@mipmap/appicon" android:roundIcon="@mipmap/appicon_round" android:supportsRtl="true">4	</application>5	<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>6	<uses-permission android:name="android.permission.BATTERY_STATS" />7	<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />8	<uses-permission android:name="android.permission.INTERNET" />9	<uses-permission android:name="android.permission.READ_CONTACTS" />10	<uses-permission android:name="android.permission.WRITE_CONTACTS" />11</manifest>
// Request access (triggers OS prompt if needed)
var access = await contactStore.RequestAccess();
if (access != AccessState.Available)
return;
// Get all contacts
var contacts = await contactStore.GetAll();
// Query with the fluent builder
var results = await contactStore.Query()
.Where(ContactField.GivenName, "John")
.OrderBy(ContactSortField.FamilyName)
.ToListAsync();
// Create a contact
var contact = new Contact { GivenName = "John", FamilyName = "Doe" };
contact.Phones.Add(new ContactPhone("555-1234", PhoneType.Mobile));
string id = await contactStore.Create(contact);
// Update
contact.GivenName = "Jane";
await contactStore.Update(contact);
// Delete
await contactStore.Delete(id);

Bulk reads — GetAll() and Query() — populate only the lightweight Thumbnail. The full-resolution Photo is null on these results: decoding every contact’s full image into memory at once spikes memory and can get the app OOM/jetsam-killed on a real device with many photo contacts. Fetch a single contact with GetById(id) to get the full Photo (it also includes the Thumbnail).

Bind list rows to Thumbnail, and load Photo on a per-contact detail screen.

claude plugin marketplace add shinyorg/skills
claude plugin install shiny-maui@shiny
copilot plugin marketplace add https://github.com/shinyorg/skills
copilot plugin install shiny-maui@shiny
View shiny-maui Plugin