Skip to content
Shiny.NET
Commercial SupportGuaranteed response times on Shiny bug fixes, straight from the maintainers. Plans from US$1,500 a year.See the plans

Contact Store | Getting Started

A cross-platform library for accessing device contacts on iOS, Mac Catalyst, macOS, and Android. 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
Feature Description
Full CRUD create, read, update, and delete device contacts
Fluent async queries Query() returns a ContactQuery builder (Where/Search/OrderBy/Skip/Take → ToListAsync(ct)) with native translation on every platform
Shiny permissions GetCurrentAccess() 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
Mac Catalyst 15.0
macOS 26.0

iOS, Mac Catalyst, and macOS share one implementation over the Contacts framework (CNContactStore), so queries, CRUD, and the notes/relations handling behave identically across all three.

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@shiny

One plugin installs all 48 Shiny skills. Your agent loads only the skill relevant to what you're building, so there's no cost to having them all available.

copilot plugin marketplace add https://github.com/shinyorg/skills
copilot plugin install shiny@shiny

One plugin installs all 48 Shiny skills. Your agent loads only the skill relevant to what you're building, so there's no cost to having them all available.

View shiny-contactstore Skill