<!--
{
  "availability" : [
    "iOS: 18.0.0 -",
    "iPadOS: 18.0.0 -"
  ],
  "documentType" : "symbol",
  "framework" : "ContactProvider",
  "identifier" : "/documentation/ContactProvider/ContactItemEnumerator",
  "metadataVersion" : "0.1.0",
  "role" : "Protocol",
  "symbol" : {
    "kind" : "Protocol",
    "modules" : [
      "ContactProvider"
    ],
    "preciseIdentifier" : "s:15ContactProvider0A14ItemEnumeratorP"
  },
  "title" : "ContactItemEnumerator"
}
-->

# ContactItemEnumerator

A protocol to provide enumerations of all contact items and changed contact items.

```
protocol ContactItemEnumerator
```

## Overview

Implement this protocol to fetch contact items from your data store in a consistent, predictable order when the system requests contact item enumeration. The [`ContactItemEnumerating`](/documentation/ContactProvider/ContactItemEnumerating) protocol, typically implemented by the app extension, returns an instance of this type when the system requests contacts.

The enumerator has two main methods to implement:

- [`enumerateContent(in:for:)`](/documentation/ContactProvider/ContactItemEnumerator/enumerateContent(in:for:)) – Enumerates all of your contacts that you want to provide to the system-wide Contacts ecosystem, sending arrays of [`ContactItem`](/documentation/ContactProvider/ContactItem) instances to a [`ContactItemContentObserver`](/documentation/ContactProvider/ContactItemContentObserver).
- [`enumerateChanges(startingAt:for:)`](/documentation/ContactProvider/ContactItemEnumerator/enumerateChanges(startingAt:for:)) – Enumerates changed contacts, sending updates and deletions to a [`ContactItemChangeObserver`](/documentation/ContactProvider/ContactItemChangeObserver), as arrays of [`ContactItem`](/documentation/ContactProvider/ContactItem) and [`ContactItem.Identifier`](/documentation/ContactProvider/ContactItem/Identifier) instances, respectively.

The following `RootContainerEnumerator` outlines how to implement a `ContactItemEnumerator`. The listing shows `TODO` in places where the implementation depends on the specifics of your app’s data store.

```swift
import ContactProvider

class RootContainerEnumerator: ContactItemEnumerator {

    func configure(for domain: ContactProviderDomain) {
        // Configure your enumerator here.
    }
    
    func enumerateContent(in page: ContactItemPage, 
                          for observer: any ContactItemContentObserver) {

        // TODO: Get the database generation for the content enumeration, which is used for the first call to `enumerateChanges`.
        let generationMarker = (page == .initialPage) ? currentGenerationMarker() : page.generationMarker

        do {
            // TODO: Get your batch of items to enumerate.
            let content = try fetchContent(startingAt: generationMarker,
                                           offset: page.offset,
                                           count: observer.suggestedPageSize)
            
            // Send the items to the observer.
            observer.didEnumerate(content.items)

            if content.moreComing {
                // Need to enumerate more items.
            
                // TODO: Create your next `ItemPage` that starts the next batch of items.
                //
                // Note: If `page.generationMarker` is empty, you must provide a non-empty `nextPage.generationMarker`.
                //       If `page.generationMarker` is non-empty, `nextPage.generationMarker` must be the same value.
                let nextPage = ContactItemPage(generationMarker: generationMarker,
                                               offset: page.offset + content.items.count)

                // Send the observer to the next page.
                observer.didFinishEnumeratingPage(upTo: nextPage)
            } else {
                // All items have been enumerated.
                
                // Tell the observer the next sync should be a change enumeration.
                observer.didFinishEnumeratingContent(upTo: generationMarker)
            }

        } catch {
            // Tell the observer an error occurred.
            //
            // Note: The system resumes the enumeration from the last `ContactItemPage` that was enumerated successfully, if the error is resumable.
            observer.didFinishEnumeratingContentWithError(error)
        }
        
        func currentGenerationMarker() -> Data {
            // TODO: Get the value specific to your data source identifying the current database generation.
            //
            // Note: Don't call `didFinishEnumeratingPage(upTo:)` with an empty `ContactItemPage.generationMarker`; this results in an error.
            let generationMarker: Data = "<currentDatabaseGenerationMarker>".data(using: .utf8)!
            return generationMarker
        }

        func fetchContent(startingAt generationMarker: Data, offset: Int, count: Int) throws -> ContentFetchResult {
            // TODO: Fetch up to `count` items from the database, starting at `offset` items for the database generation specified by `generationMarker`.
            let items: [ContactItem] = []
            let moreComing: Bool = false

            return ContentFetchResult(items: items, moreComing: moreComing)
        }

        struct ContentFetchResult {
            let items: [ContactItem]
            let moreComing: Bool
        }

    }
    
    func enumerateChanges(startingAt syncAnchor: ContactItemSyncAnchor, 
                          for observer: any ContactItemChangeObserver) {
        do {
            // TODO: Get your batch of changed items to enumerate.
            let changes = try fetchChanges(startingAt: syncAnchor.generationMarker,
                                           offset: syncAnchor.offset,
                                           count: observer.suggestedBatchSize)

            // Send the changes to the observer.
            observer.didUpdate(changes.updatedItems)
            observer.didDelete(changes.deletedItemIdentifiers)
            observer.didFinishEnumeratingChanges(upTo: changes.nextSyncAnchor,
                                                 moreComing: changes.moreComing)
        } catch {
            // Tell the observer an error occurred.
            //
            // Note: The system resumes the enumeration from the last `ItemChangeAnchor` that was enumerated successfully, if the error is resumable.
            observer.didFinishEnumeratingChangesWithError(error)
        }

        func fetchChanges(startingAt generationMarker: Data, offset: Int, count: Int) throws -> ChangeFetchResult {
            // TODO: Fetch up to `count` changes from the database, starting at `offset` changes after the database generation specified by `generationMarker`.
            let updatedItems: [ContactItem] = []
            let deletedItemIdentifiers: [ContactItem.Identifier] = []
            let moreComing: Bool = false

            // TODO: Generate the next `ContactItemSyncAnchor`, using the `generationMarker` and offset of the last change that was fetched.
            let lastChangeGenerationMarker: Data = "<lastChangeGenerationMarker>".data(using: .utf8)!
            let lastChangeOffset: Int = 0
            let nextSyncAnchor = ContactItemSyncAnchor(generationMarker: lastChangeGenerationMarker, offset: lastChangeOffset)

            return ChangeFetchResult(updatedItems: updatedItems,
                                     deletedItemIdentifiers: deletedItemIdentifiers,
                                     moreComing: moreComing,
                                     nextSyncAnchor: nextSyncAnchor)
        }

        struct ChangeFetchResult {
            let updatedItems: [ContactItem]
            let deletedItemIdentifiers: [ContactItem.Identifier]
            let moreComing: Bool
            let nextSyncAnchor: ContactItemSyncAnchor
        }
    }
    
    func invalidate() async {
        // Invalidate here.
    }
}
```

## Topics

### Enumerating contact items

[`enumerateContent(in:for:)`](/documentation/ContactProvider/ContactItemEnumerator/enumerateContent(in:for:))

Enumerates all items, batched in pages.

[`ContactItemPage`](/documentation/ContactProvider/ContactItemPage)

A fixed offset into enumerating all contact items.

[`ContactItemContentObserver`](/documentation/ContactProvider/ContactItemContentObserver)

A protocol that defines a system observer that receives a resumable enumeration of all items.

### Enumerating item changes

[`enumerateChanges(startingAt:for:)`](/documentation/ContactProvider/ContactItemEnumerator/enumerateChanges(startingAt:for:))

Enumerates items changed since the last sync.

[`ContactItemSyncAnchor`](/documentation/ContactProvider/ContactItemSyncAnchor)

A snapshot point into enumerating changed contact items.

[`ContactItemChangeObserver`](/documentation/ContactProvider/ContactItemChangeObserver)

A protocol that defines a system observer that receives a resumable enumeration of changed contact items.

### Managing enumerator life cycle

[`invalidate()`](/documentation/ContactProvider/ContactItemEnumerator/invalidate())

Invalidates the enumerator.



---

Copyright &copy; 2026 Apple Inc. All rights reserved. | [Terms of Use](https://www.apple.com/legal/internet-services/terms/site.html) | [Privacy Policy](https://www.apple.com/privacy/privacy-policy)