> For clean Markdown of any page, append .md to the page URL. > For a complete documentation index, see https://contentful.com/developers/docs/llms.txt. > For AI client integration (Claude Code, Cursor, etc.), connect to the MCP server at https://contentful.com/_mcp/server. # Offline persistence with the Swift client library > This tutorial shows you how to get offline access to your Contentful content in iOS apps | Authentication | Setup | Syncing data This guide will show you how to use [contentful-persistence.swift][1] to synchronize Contentful data to your local datastore. Contentful's Content Delivery API (CDA) is a read-only API for retrieving content from Contentful. All content, both JSON and binary, is fetched from the server closest to a user's location by using our global CDN. We publish client libraries for various languages to make developing applications easier. ## Requirements This tutorial assumes that you understand the [Contentful data model](/concepts/data-model). ## Authentication For every request, clients [need to provide an API key](/references/authentication), which is created per space and used to delimit applications and content classes. You can create an access token using the [Contentful web app](https://be.contentful.com/login) or the [Content Management API](/references/content-management-api/overview#/reference/api-keys/create-an-api-key). ## Setup Persisting data to disk for use when offline is important for providing a pleasant user experience. The [Contentful persistence library][1] provides a `SynchronizationManager` type which interfaces with the [`/sync` endpoint][2] of Contentful's Content Delivery API to synchronize all data within a Contentful space to a local data store. It relyies on property mappings provided by you to return types of your own definition. In order to create a `SynchronizationManager` instance, you will first need to define your data model by passing in your `NSManagedObject` subclass types, which must conform to the `Persistable` protocols, into your `PersistenceModel`. ```swift let model = PersistenceModel(spaceType: SyncInfo.self, assetType: Asset.self, entryTypes: [Image.self, Gallery.self, Author.self]) ``` You will also need to tell the `SynchronizationManager` where to store the data by passing in a storage class which conforms to the `PersistenceStore` protocol. There is a default implementation of the `PersistenceStore` using [Core Data][3], but if you want to use a different local store, you can create your own. ```swift let store = CoreDataStore(context: self.managedObjectContext) let syncManager = ContentfulSynchronizer(client: client, // Store separate entities for every locale in the space. localizationScheme: .all, persistenceStore: store, persistenceModel: model) ``` In this case, it's your responsibility to provide the right `NSManagedObjectContext` for your application. If you are working with Core Data in a multi-threaded environment, make sure to read [Apple's Core Data Programming Guide][4]. Depending on your setup, you might need to create different managed object contexts for writing and reading data. While you can use the `CoreDataStore` class for querying, you don't have to. The mapping between your persistence model classes and Contentful data is done by conforming to the `Persistable` protocols mentioned above. In the example below, we define the mapping between a Contentful content type with the identifier "author" to our `NSManagedObject` subclass `Author`. Note that the system properties required by Contentful, such as `id` and `updatedAt` should not have a mapping defined in the `fieldMapping()` method. ```swift class Author: NSManagedObject, EntryPersistable { static let contentTypeId = "author" // Properties required by Contentful. @NSManaged var id: String @NSManaged var localeCode: String @NSManaged var createdAt: Date? @NSManaged var updatedAt: Date? // The fields defined on your Author content type. @NSManaged var biography: String? @NSManaged var name: String @NSManaged var twitterHandle: String? @NSManaged var authorInverse: NSSet @NSManaged var createdEntries: NSOrderedSet @NSManaged var awesomeProfilePhoto: Asset? // Map field names on your Contentful entry to property names on your class. static func fieldMapping() -> [FieldName: String] { return [ "name": "name", "biography": "biography", "twitterHandle": "twitterHandle", "createdEntries": "createdEntries", // We can name our class property however we like. "profilePhoto": "awesomeProfilePhoto" ] } } ``` Keep in mind that for `AssetPersistable` types, the URL of the underlying media file will be stored along with other metadata, but no caching of the actual documents will be performed. ### Special notes on the data model editor in Xcode * You will want to make sure that code generation is turned *off* in Xcode's "Data Model Editor". The editor can be found in the right side-bar when viewing your `.xcdatamodel` file. * You will also want to ensure that the `id` and `localeCode` properties on each of your `EntryPersistable` and `AssetPersistable` types are set to *non-optional*. This can also be achieved through the data model editor. * Lastly, in the "Configuration" section in the `.xcdatamodel` file, ensure that the module name is prefixed before your class name if you are using Swift classes. In the app (and Xcode target) we are working on is called "AwesomeApp", the class name for our `Author.swift` file would be set to `AwesomeApp.Author`. ## Syncing the data If you've made it this far, you've done all the setup necessary to sync data to your CoreData store, congrats! All that's left to do is make the appropriate calls via the `SynchronizationManager`, then update your UI using the fetch results from your local store. Note that if you don't have a `localeCode` predicate specified, you will get data for all the locales you have synced with. The `LocalizationScheme` used by the library is specified during initialization of the `SynchronizationManager`. ```swift syncManager.sync { result in do { // In this case, we'll only fetch the entries that are localized to Mexican Spanish as our app is currently being used by somebody in Mexico. let authors: [Author] = try self.store.fetchAll(type: Author.self, predicate: NSPredicate(format: "localeCode == 'es-MX'")) } catch { // Handle failure to fetch from CoreData. } } ``` [1]: https://contentful.github.io/contentful-persistence.swift [2]: /concepts/sync [3]: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreData/index.html [4]: https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreData/Concurrency.html ## Next steps * [Further details on our Synchronization endpoint](/references/content-delivery-api/overview) > This tutorial shows you how to get offline access to your Contentful content in iOS apps | Authentication | Setup | Syncing data