File: Contacts\Contacts.ios.macos.cs
Web Access
Project: src\src\Essentials\src\Essentials.csproj (Microsoft.Maui.Essentials)
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Threading.Tasks;
using Contacts;
#if __IOS__
using ContactsUI;
#endif
 
namespace Microsoft.Maui.ApplicationModel.Communication
{
	class ContactsImplementation : IContacts
	{
#if __MACOS__
		public Task<Contact> PickContactAsync() =>
			throw ExceptionUtils.NotSupportedOrImplementedException;
#elif __IOS__
		public Task<Contact> PickContactAsync()
		{
			var vc = WindowStateManager.Default.GetCurrentUIViewController(true);
 
			var source = new TaskCompletionSource<Contact>();
 
			var picker = new CNContactPickerViewController
			{
				Delegate = new ContactPickerDelegate(phoneContact =>
				{
					try
					{
						source?.TrySetResult(ConvertContact(phoneContact));
					}
					catch (Exception ex)
					{
						source?.TrySetException(ex);
					}
				})
			};
 
			if (picker.PresentationController != null)
			{
				picker.PresentationController.Delegate =
					new UIPresentationControllerDelegate(() => source?.TrySetResult(null));
			}
 
			vc.PresentViewController(picker, true, null);
 
			return source.Task;
		}
#endif
 
		public Task<IEnumerable<Contact>> GetAllAsync(CancellationToken cancellationToken)
		{
			var keys = new[]
			{
				CNContactKey.Identifier,
				CNContactKey.NamePrefix,
				CNContactKey.GivenName,
				CNContactKey.MiddleName,
				CNContactKey.FamilyName,
				CNContactKey.NameSuffix,
				CNContactKey.EmailAddresses,
				CNContactKey.PhoneNumbers,
				CNContactKey.Type
			};
 
			var store = new CNContactStore();
			var containers = store.GetContainers(null, out _);
			if (containers == null)
				return Task.FromResult<IEnumerable<Contact>>(Array.Empty<Contact>());
 
			return Task.FromResult(GetEnumerable());
 
			IEnumerable<Contact> GetEnumerable()
			{
				foreach (var container in containers)
				{
					using var pred = CNContact.GetPredicateForContactsInContainer(container.Identifier);
					var contacts = store.GetUnifiedContacts(pred, keys, out var error);
					if (contacts == null)
						continue;
 
					foreach (var contact in contacts)
					{
						yield return ConvertContact(contact);
					}
				}
			}
		}
 
		internal static Contact ConvertContact(CNContact contact)
		{
			if (contact == null)
				return default;
 
			var phones = contact.PhoneNumbers?.Select(
				item => new ContactPhone(item?.Value?.StringValue));
			var emails = contact.EmailAddresses?.Select(
				item => new ContactEmail(item?.Value?.ToString()));
 
			return new Contact(
				contact.Identifier,
				contact.NamePrefix,
				contact.GivenName,
				contact.MiddleName,
				contact.FamilyName,
				contact.NameSuffix,
				phones,
				emails);
		}
 
#if __IOS__
		class ContactPickerDelegate : CNContactPickerDelegate
		{
			public ContactPickerDelegate(Action<CNContact> didSelectContactHandler) =>
				DidSelectContactHandler = didSelectContactHandler;
 
			public ContactPickerDelegate(IntPtr handle)
				: base(handle)
			{
			}
 
			public Action<CNContact> DidSelectContactHandler { get; }
#pragma warning disable CA1416 // picker.DismissModalViewController(bool) has UnsupportedOSPlatform("ios6.0")]. (Deprecated but still works)
#pragma warning disable CA1422 // Validate platform compatibility
			public override void ContactPickerDidCancel(CNContactPickerViewController picker)
			{
				DidSelectContactHandler?.Invoke(default);
				picker.DismissModalViewController(true);
			}
 
			public override void DidSelectContact(CNContactPickerViewController picker, CNContact contact)
			{
				DidSelectContactHandler?.Invoke(contact);
				picker.DismissModalViewController(true);
			}
 
			public override void DidSelectContactProperty(CNContactPickerViewController picker, CNContactProperty contactProperty) =>
				picker.DismissModalViewController(true);
 
#pragma warning restore CA1422 // Validate platform compatibility
#pragma warning restore CA1416
		}
#endif
	}
}