{"id":15022403,"url":"https://github.com/contentful/contentful-persistence.swift","last_synced_at":"2025-04-07T12:06:46.142Z","repository":{"id":7827085,"uuid":"56580230","full_name":"contentful/contentful-persistence.swift","owner":"contentful","description":"Simplified persistence for the Contentful Swift Library.","archived":false,"fork":false,"pushed_at":"2025-02-04T23:29:37.000Z","size":11366,"stargazers_count":23,"open_issues_count":10,"forks_count":35,"subscribers_count":7,"default_branch":"master","last_synced_at":"2025-03-31T09:07:06.949Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Swift","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/contentful.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":null,"support":"Supporting Files/ContentfulPersistence.h","governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null}},"created_at":"2016-04-19T08:44:29.000Z","updated_at":"2025-01-22T16:51:18.000Z","dependencies_parsed_at":"2024-01-20T13:27:18.930Z","dependency_job_id":"9437547e-fedd-40c2-a5e4-d6946890519c","html_url":"https://github.com/contentful/contentful-persistence.swift","commit_stats":{"total_commits":238,"total_committers":25,"mean_commits":9.52,"dds":0.5420168067226891,"last_synced_commit":"234795c1f438418989b4eb8f46e4dda1a25b8dba"},"previous_names":[],"tags_count":38,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/contentful%2Fcontentful-persistence.swift","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/contentful%2Fcontentful-persistence.swift/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/contentful%2Fcontentful-persistence.swift/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/contentful%2Fcontentful-persistence.swift/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/contentful","download_url":"https://codeload.github.com/contentful/contentful-persistence.swift/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":247648977,"owners_count":20972945,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-09-24T19:57:54.441Z","updated_at":"2025-04-07T12:06:46.113Z","avatar_url":"https://github.com/contentful.png","language":"Swift","funding_links":[],"categories":[],"sub_categories":[],"readme":"# contentful-persistence.swift\n\n[![Version](https://img.shields.io/cocoapods/v/ContentfulPersistenceSwift.svg?style=flat)](http://cocoadocs.org/docsets/ContentfulPersistenceSwift)\n[![Carthage compatible](https://img.shields.io/badge/Carthage-compatible-4BC51D.svg?style=flat)](https://github.com/Carthage/Carthage)\n[![License](https://img.shields.io/cocoapods/l/ContentfulPersistenceSwift.svg?style=flat)](http://cocoadocs.org/docsets/ContentfulPersistenceSwift)\n[![Platform](https://img.shields.io/cocoapods/p/ContentfulPersistenceSwift.svg?style=flat)](http://cocoadocs.org/docsets/ContentfulPersistenceSwift)\n[![Build Status](https://img.shields.io/travis/contentful/contentful-persistence.swift/master.svg?style=flat)](https://travis-ci.org/contentful/contentful-persistence.swift)\n[![Coverage Status](https://img.shields.io/coveralls/contentful/contentful-persistence.swift.svg)](https://coveralls.io/github/contentful/contentful-persistence.swift)\n\n\u003e An integration to simplify persisting data from [Contentful][1] to a local CoreData database; built on top of the official Contentful [Swift Library][2]. This library specifically uses the [`/sync` endpoint][7] of the Content Delivery API to synchronize all content in a Contentful space to a device.\n\n**What is Contentful?**\n\n[Contentful][1] provides a content infrastructure for digital teams to power content in websites, apps, and devices. Unlike a CMS, Contentful was built to integrate with the modern software stack. It offers a central hub for structured content, powerful management and delivery APIs, and a customizable web app that enables developers and content creators to ship digital products faster.\n\n## Getting Started\n\n### Prerequisites\n\nBefore getting started, it is highly recommended that you familiarize yourself with Apple's CoreData framework as many issues encountered during development may be CoreData specific. Read the [CoreData Programming Guide](https://developer.apple.com/library/content/documentation/Cocoa/Conceptual/CoreData/index.html) and check out other (non-Contentful) examples.\n\n### Usage\n\nThe `SynchronizationManager` manages the state of your CoreData database and keeps it in sync with the data from your Contentful Space:\n\n```swift\n// Tell the library which of your `NSManagedObject` subclasses that conform to `EntryPersistable` should be used when mapping API responses to CoreData entities.\nlet entryTypes = [Author.self, Category.self, Post.self]\n\n// Initialize the data store and it's schema.\nlet store = CoreDataStore(context: self.managedObjectContext)\nlet persistenceModel = PersistenceModel(spaceType: SyncInfo.self, assetType: Asset.self, entryTypes: entryTypes)\n\n// Initialize the Contentful.Client with a persistenceIntegration which will receive messages about changes when calling `sync methods`\nself.client = Client(spaceId: \"\u003cYOUR_SPACE_ID\u003e\", accessToken: \"\u003cYOUR_ACCESS_TOKEN\u003e\")\n\n// Create the manager.\nself.syncManager = SynchronizationManager(\n    client: self.client,\n    localizationScheme: LocalizationScheme.all, // Save data for all locales your space supports.\n    persistenceStore: self.store,\n    persistenceModel: persistenceModel\n)\n\n// Sync with the API.\nself.syncManager.sync { _ in\n  do {\n    // Fetch all `Posts` from CoreData\n    let post: Post? = try self.store.fetchAll(type: Post.self, predicate: NSPredicate(value: true))\n  } catch {\n    // Handle error thrown by CoreData fetches.\n  }\n}\n```\n\n### SpaceType and AssetType\n\nThe `PersistenceModel` requires both a `spaceType` and an `assetType` to be specified. These correspond to Core Data entities used for storing `SyncInfo` and `Asset` objects, respectively.\n\nTo function correctly, these objects must:\n\n- Be of type `NSManagedObject`.\n- Conform to the `SyncSpacePersistable` and `AssetPersistable` protocols, as appropriate.\n- Be defined within the Core Data `xcdatamodel` file.\n\n#### Example Implementation\n\n```swift\nclass SyncInfo: NSManagedObject, SyncSpacePersistable {\n    @NSManaged var syncToken: String?\n    @NSManaged var dbVersion: NSNumber?\n}\n\nclass Asset: NSManagedObject, AssetPersistable {\n    @NSManaged var id: String\n    @NSManaged var localeCode: String?\n    @NSManaged var title: String?\n    @NSManaged var assetDescription: String?\n    @NSManaged var urlString: String?\n    @NSManaged var createdAt: Date?\n    @NSManaged var updatedAt: Date?\n\n    @NSManaged var size: NSNumber?\n    @NSManaged var width: NSNumber?\n    @NSManaged var height: NSNumber?\n    @NSManaged var fileType: String?\n    @NSManaged var fileName: String?\n}\n```\n\nAnd then in the `xcdatamodeld` file:\n![](Screenshots/Asset.png)\n![](Screenshots/SyncInfo.png)\n\n## Define Your `CoreData` Model\n\nTo integrate your model classes with `contentful-persistence.swift`, you must conform to either the `AssetPersistable` protocol for Contentful Assets or the `EntryPersistable` protocol for Contentful entry types.\n\nNext, you'll need to create the corresponding model in your project's `xcdatamodel` file. Both `EntryPersistable` and `AssetPersistable` types require a _non-optional_ `id` property, as well as optional `localeCode`, `createdAt`, and `updatedAt` properties.\n\n**NOTE:** Optionality in Core Data entities differs from Swift optionality. For Core Data entities, optionality means that a property may be absent during a save-to-database operation. To configure a property's optionality, open the \"Data Model Inspector\" in Xcode's \"Utilities\" right sidebar and toggle the \"Optional\" checkbox.\n![](Screenshots/CoreDataOptionality.png)\n\nThe mapping of Contentful fields to your data model entities will be derived automatically, but you can also customize it by implementing the `static func fieldMapping() -\u003e [FieldName: String]?` on your class.\n\nBelow is an example of a model class.\n\n```swift\nimport Foundation\nimport CoreData\nimport ContentfulPersistence\nimport Contentful\n\n// The following @objc attribute is only necessary if your xcdatamodel Default configuration doesn't have your module\n// name prepended to the Swift class. To enable removing the @objc attribute, change the Class for your entity to `ModuleName.Post`\n@objc(Post)\nclass Post: NSManagedObject, EntryPersistable {\n\n    // The identifier of the corresponding Content Type in Contentful.\n    static let contentTypeId = \"post\"\n\n    // Properties of the `sys` object of Contentful resources.\n    @NSManaged var id: String\n    @NSManaged var localeCode: String?\n    @NSManaged var createdAt: Date?\n    @NSManaged var updatedAt: Date?\n\n    // Custom fields on the content type.\n    @NSManaged var body: String?\n    @NSManaged var comments: NSNumber?\n    // NOTE: Unlike date fields in sys properties, this library can't store `Date` for custom fields.\n    // Use `String` and map to date after fetching from CoreData\n    @NSManaged var customDateField: String?\n    @NSManaged var date: Date?\n    @NSManaged var slug: String?\n    @NSManaged var tags: Data?\n    @NSManaged var title: String?\n    @NSManaged var authors: NSOrderedSet?\n    @NSManaged var category: NSOrderedSet?\n    @NSManaged var theFeaturedImage: Asset?\n\n    // Define the mapping from the fields on your Contentful.Entry to your model class.\n    // In the below example, only the `title`, `date` and `author` fields and `featuredImage` link will be populated.\n    // IMPORTANT: This should not include metadata from the `sys` object (e.g. id, createdAt, etc.)\n    static func fieldMapping() -\u003e [FieldName: String] {\n        return [\n            \"title\": \"title\",\n            \"featuredImage\": \"theFeaturedImage\",\n            \"author\": \"authors\"\n            \"date\": \"date\"\n        ]\n    }\n}\n```\n\n## Relationships\n\nLet's say we have the following content model in our Contentful space:\n\n```\nProduct\n- name: String\n- relatedProducts: [Product]\n```\n\nIt represents a product with a name and related products.\nThis would be translated into our Swift model as follows:\n\n```swift\nclass Product: NSManagedObject {\n    // Contentful metadata\n    @NSManaged var id: String\n    @NSManaged var localeCode: String?\n    @NSManaged var createdAt: Date?\n    @NSManaged var updatedAt: Date?\n\n    // Defined properties in Contentful\n    @NSManaged public var name: String?\n    @NSManaged public var relatedProducts: NSOrderedSet?\n}\n\nextension Product: EntryPersistable {\n    public static var contentTypeId = \"product\"\n    public static func fieldMapping() -\u003e [FieldName: String] {\n        return [\n            \"name\": \"name\",\n            \"relatedProducts\": \"relatedProducts\",\n        ]\n    }\n}\n```\n\nThe corresponding CoreData entity would look like this:\n![](Screenshots/Product.png)\n\nNote the type and the arrangement set to `ordered`.\n\nAfter fetching products from the DB, related products could be accessed like this:\n\n```swift\nfor product in products {\n  if let relatedProductsSet = product.relatedProducts,\n      let productsArray = relatedProductsSet.array as? [Product]\n  {\n      // productsArray is now [Product]\n      for product in productsArray {\n          // Access product properties here, e.g., product.name\n          print(\"Related product:\", product.id, product.name)\n      }\n  } else {\n      print(\"No related products or unable to cast\")\n  }\n}\n```\n\n## Installation\n\n### SPM installation\n\nYou can also use the Swift Package Manager which is bundled with your XCode (Swift 3.0+) to add Contentful Persistence as a dependency to your project. In order to do so, select your project, change tabs to 'Package Dependencies' and add the following URL:\n\n```\n\nhttps://github.com/contentful/contentful-persistence.swift\n\n```\n\nYou need to use \"master\" branch.\n\n### CocoaPods installation\n\n[CocoaPods][5] is the dependency manager for Objective-C and Swift, which automates and simplifies the process of using 3rd-party libraries like the ContentfulPersistence in your projects.\n\n```ruby\nplatform :ios, '9.3'\nuse_frameworks!\n\ntarget :MyApp do\n  pod 'ContentfulPersistenceSwift', '~\u003e 0.13.0'\nend\n```\n\n### Carthage installation\n\nYou can also use [Carthage][6] for integration by adding the following to your `Cartfile`:\n\n```\ngithub \"contentful/contentful.swift\" ~\u003e 0.13.0\n```\n\n## Documentation\n\nFor further information, check out the [Developer Documentation][4] or browse the [API documentation][3]. The latter can also be loaded into Xcode as a Docset.\n\n## Contributing and development\n\nTo get started contributing, clone the project, `cd` into the root directory and run the following: `make setup_env`.\n\n```bash\nmake setup_env\ncarthage bootstrap --platform all\n```\n\nThis command will install all the development dependencies necessary to build the project, and execute the tests. To run the tests from the command line, execute `make test`. Tests should also all run directly from the Xcode app.\n\n## License\n\nCopyright (c) 2018 Contentful GmbH. See [LICENSE](LICENSE) for further details.\n\n[1]: https://www.contentful.com\n[2]: https://github.com/contentful/contentful.swift\n[3]: http://cocoadocs.org/docsets/ContentfulPersistenceSwift/\n[4]: https://www.contentful.com/developers/docs/references/content-delivery-api/\n[5]: https://cocoapods.org/\n[6]: https://github.com/Carthage/Carthage\n[7]: https://www.contentful.com/developers/docs/references/content-delivery-api/#/reference/synchronization\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcontentful%2Fcontentful-persistence.swift","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fcontentful%2Fcontentful-persistence.swift","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fcontentful%2Fcontentful-persistence.swift/lists"}