shiny-contactstore
Generate code using Shiny.Maui.ContactStore for cross-platform device contact access with CRUD, LINQ queries, and MAUI permissions
来源信息
- 仓库
- shinyorg/contactstore
- 最近来源活动
- 2026年3月25日 00:04
- 检测到的 SKILL.md 语言
- 英语
- 星标
- 0
- 分支
- 0
安装方式
默认使用会先检查来源的 Prompt;你也可以切换为直接命令,或下载本地副本。
检查来源文件
决定是否安装前,请先阅读 SKILL.md,以及 SkillsMP 当前展示的配套文件。
正在显示 SKILL.md
SKILL.md
来源说明 · 只读预览- name
- shiny-contactstore
- description
- Generate code using Shiny.Maui.ContactStore for cross-platform device contact access with CRUD, LINQ queries, and MAUI permissions
- auto_invoke
- true
- triggers
- ["contact store","contacts","IContactStore","ContactStore","AddContactStore","ContactPermission","ContactReadPermission","ContactWritePermission","Shiny.Maui.ContactStore","device contacts","read contacts","write contacts","contact query","contact LINQ"]
# Shiny.Maui.ContactStore Skill
You are an expert in Shiny.Maui.ContactStore, a cross-platform .NET MAUI library for accessing device contacts on Android and iOS.
## When to Use This Skill
Invoke this skill when the user wants to:
- Access device contacts (read, create, update, delete)
- Query contacts with LINQ
- Set up contact permissions using MAUI permissions
- Register the contact store in DI
- Work with contact models (phones, emails, addresses, etc.)
## Library Overview
**GitHub**: https://github.com/shinyorg/contactstore
**NuGet**: `Shiny.Maui.ContactStore`
**Namespace**: `Shiny.Maui.ContactStore`
Shiny.Maui.ContactStore provides:
- Full CRUD operations on device contacts
- LINQ query support with native translation (Android content provider queries, iOS CNContact predicates)
- MAUI permission classes for requesting contact access
- Dependency injection integration
- AOT and trimmer compatible
## Setup
### 1. Install NuGet Package
```bash
dotnet add package Shiny.Maui.ContactStore
```
### 2. Register in MauiProgram.cs
```csharp
using Shiny.Maui.ContactStore;
builder.Services.AddContactStore();
```
### 3. Platform Permissions
**Android** — Add to `AndroidManifest.xml`:
```xml
<uses-permission android:name="android.permission.READ_CONTACTS" />
<uses-permission android:name="android.permission.WRITE_CONTACTS" />
```
**iOS** — Add to `Info.plist`:
```xml
<key>NSContactsUsageDescription</key>
<string>This app needs access to your contacts.</string>
```
## Permissions
The library provides a MAUI permission class `ContactPermission` that wraps both read and write contact permissions.
Use the extension methods on `IContactStore`:
```csharp
// Request permissions (triggers OS prompt if needed)
var status = await contactStore.RequestPermssionsAsync();
if (status != PermissionStatus.Granted)
{
// Handle denied
return;
}
// Check current status without prompting
var status = await contactStore.CheckPermissionStatusAsync();
```
### Android Permission Results
- `PermissionStatus.Granted` — both read and write access granted
- `PermissionStatus.Limited` — only read or only write granted (not both)
- `PermissionStatus.Denied` — neither read nor write granted
### iOS Permission Results
- `PermissionStatus.Granted` — contacts access authorized
- `PermissionStatus.Denied` — contacts access denied
- `PermissionStatus.Restricted` — contacts access restricted
## API Reference
### IContactStore Interface
```csharp
public interface IContactStore
{
Task<IReadOnlyList<Contact>> GetAll(CancellationToken ct = default);
Task<Contact?> GetById(string contactId, CancellationToken ct = default);
IQueryable<Contact> Query();
Task<string> Create(Contact contact, CancellationToken ct = default);
Task Update(Contact contact, CancellationToken ct = default);
Task Delete(string contactId, CancellationToken ct = default);
}
```
### Extension Methods
```csharp
// Permission extensions
Task<PermissionStatus> contactStore.RequestPermssionsAsync();
Task<PermissionStatus> contactStore.CheckPermissionStatusAsync();
// Query extensions
Task<IReadOnlyList<char>> contactStore.GetFamilyNameFirstLetters(CancellationToken ct = default);
```
### Query with LINQ
The library translates LINQ predicates to native queries where possible, with in-memory fallback.
```csharp
// Filter by name
var results = contactStore.Query()
.Where(c => c.GivenName.Contains("John"))
.ToList();
// Filter by phone number
var results = contactStore.Query()
.Where(c => c.Phones.Any(p => p.Number.Contains("555")))
.ToList();
// Filter by email
var results = contactStore.Query()
.Where(c => c.Emails.Any(e => e.Address.Contains("@example.com")))
.ToList();
// Combine filters
var results = contactStore.Query()
.Where(c => c.GivenName.StartsWith("J") && c.FamilyName.Contains("Smith"))
.ToList();
// Paging
var page = contactStore.Query()
.Where(c => c.FamilyName.StartsWith("A"))
.Skip(10)
.Take(20)
.ToList();
```
**Supported operations:** `Contains`, `StartsWith`, `EndsWith`, `Equals`
**Filterable properties:** `GivenName`, `FamilyName`, `MiddleName`, `NamePrefix`, `NameSuffix`, `Nickname`, `DisplayName`, `Note`
**Filterable collections:** `Phones` (by `Number`), `Emails` (by `Address`)
### Create a Contact
```csharp
var contact = new Contact
{
GivenName = "John",
FamilyName = "Doe",
Note = "Met at conference"
};
contact.Phones.Add(new ContactPhone("555-1234", PhoneType.Mobile));
contact.Emails.Add(new ContactEmail("john@example.com", EmailType.Work));
string id = await contactStore.Create(contact);
```
### Update a Contact
```csharp
var contact = await contactStore.GetById(id);
contact.GivenName = "Jane";
await contactStore.Update(contact);
```
### Delete a Contact
```csharp
await contactStore.Delete(contactId);
```
## Models
### Contact
| Property | Type |
|----------------|-----------------------------|
| Id | `string?` |
| NamePrefix | `string?` |
| GivenName | `string?` |
| MiddleName | `string?` |
| FamilyName | `string?` |
| NameSuffix | `string?` |
| Nickname | `string?` |
| DisplayName | `string` |
| Note | `string?` |
| Organization | `ContactOrganization?` |
| Photo | `byte[]?` |
| Thumbnail | `byte[]?` |
| Phones | `List<ContactPhone>` |
| Emails | `List<ContactEmail>` |
| Addresses | `List<ContactAddress>` |
| Dates | `List<ContactDate>` |
| Relationships | `List<ContactRelationship>` |
| Websites | `List<ContactWebsite>` |
### Enums
**PhoneType:** Home, Mobile, Work, FaxWork, FaxHome, Pager, Other, Custom
**EmailType:** Home, Work, Other, Custom
**AddressType:** Home, Work, Other, Custom
**ContactDateType:** Birthday, Anniversary, Other, Custom
**RelationshipType:** Father, Mother, Parent, Brother, Sister, Child, Friend, Spouse, Partner, Assistant, Manager, Other, Custom
## iOS Notes & Relations Entitlement
Reading `Note` and `Relationships` on iOS requires the `com.apple.developer.contacts.notes` entitlement. The library auto-detects this at runtime. If absent, `Note` returns `null` and `Relationships` is empty.
## Best Practices
1. **Always request permissions first** — use `contactStore.RequestPermssionsAsync()` before any CRUD operation
2. **Use LINQ queries for filtering** — prefer `Query().Where(...)` over `GetAll()` when filtering, as it uses native queries
3. **Check for Limited on Android** — `PermissionStatus.Limited` means partial access (read-only or write-only)
4. **Handle iOS entitlements gracefully** — Notes and Relations silently return empty without the entitlement
5. **Use primary constructors** — inject `IContactStore` via primary constructor
在 GitHub 查看