Using the Delivery API on Swift

This guide will show you how to get started using our Swift SDK to consume content.

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 an user's location by using our global CDN.

We publish SDKs for various languages to make developing applications easier.

Pre-requisites

This tutorial assumes you have read and understood the guide that covers the Contentful data model.

Authentication

For every request, clients need to provide an API key, which is created per space and used to delimit applications and content classes.

You can create an access token using the Contentful web app or the Content Management API.

Setup the client

There are different ways to integrate the SDK into your own apps, described in detail in the README for the SDK. This guide will use CocoaPods, the dependency manager for Cocoa projects, which helps you keep the SDK up-to-date:

Create the following Podfile for your project:

1
2
3
4
5
6
use_frameworks!

target 'Product Catalogue' do
  pod 'Contentful', '~> 0.2.0'
  pod 'ContentfulPersistenceSwift', '~> 0.2.0'
end

As you are developing a mobile app, you should provide offline data persistence for users and the app integrates the Contentful persistence library. If you prefer to not use a dependency manager, we also provide dynamic frameworks as part of our GitHub releases, but these might not be compatible depending on the Swift version you use, because there is no stable Swift ABI yet.

Configuring the API client

The Client class manages all requests to the API.

Initializing the client

You need an API key and a space ID to initialize a client

You can use the API key and space ID pre-filled below from our example space or replace them with your own values.

1
2
3
4
let spaceId = "71rop70dkqaj"
let token = "297e67b247c1a77c1a23bb33bf4c32b81500519edd767a8384a4b8f8803fb971"

let client = Client(spaceIdentifier: spaceId, accessToken: token)

Accessing data

Now that you have initialized a client, you can fetch entries:

1
2
3
client.fetchEntries(["content_type": "2PqfXUJwE8qSYKuM0U6w8M"]).1.next {
    print($0)
}

Each content type has its own unique ID, and you can find it by looking at the last part of the URL in the web app:

The fetchEntries(_:) method returns a tuple of NSURLSessionDataTask, for cancellation purposes, and a signal, called on completion of the request. The error is available via .error and if you prefer, there is a variant, which takes a completion closure instead.

The CDA supports a variety of parameters to search, filter and sort your content. The SDK passes these parameters as a dictionary, which in this case will retrieve entries of a certain content type. You can learn more about search parameters in this guide, and explore more of the API using this Playground.

Offline persistence

The Contentful persistence library provides a ContentfulSynchronizer type which utilizes the sync API to synchronize a Contentful space with a local data store, relying on a mapping provided by you. There is a default implementation of the PersistenceStore protocol using Core Data, but if you want to use a different local store, you can create your own.

1
2
let store = CoreDataStore(context: self.managedObjectContext)
let synchronizer = ContentfulSynchronizer(client: client, persistenceStore: store)

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. 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 best way to replicate your content model from Contentful in your own app is using our Xcode plugin. This requires management access to the space, so you can only use the plugin for your own spaces. For this tutorial, you can use the generated model from the example GitHub repository. The generated model contains a class per content type, with fields matching those from Contentful, and there are dedicated classes for assets and spaces. From the data model, you can generate classes using Xcode, but each class must implement a protocol, which requires a manual step (Resource for entries, Asset for assets and Space for spaces).

Using this, you can define a mapping between your content model and the local entities:

1
2
3
4
5
6
synchronizer.mapAssets(to: Asset.self)
synchronizer.mapSpaces(to: SyncInfo.self)

synchronizer.map(contentTypeId: "brand", to: Brand.self)
synchronizer.map(contentTypeId: "category", to: ProductCategory.self)
synchronizer.map(contentTypeId: "product", to: Product.self)

By default, Contentful fields are automatically mapped to properties of the same name, but you can optionally specify a custom mapping, if needed. Keep in mind that for assets, the URL will be stored, but no caching of the actual documents is performed, for caching images look into FastImageCache.

Displaying the data

To display the list of products, use a UITableView based on a NSFetchedResultsController:

1
2
3
4
5
func fetchedResultsController(forContentType type: Any.Type, predicate: NSPredicate, sortDescriptors: [NSSortDescriptor]) throws -> NSFetchedResultsController {
    let fetchRequest = try store.fetchRequest(type, predicate: predicate)
    fetchRequest.sortDescriptors = sortDescriptors
    return NSFetchedResultsController(fetchRequest: fetchRequest, managedObjectContext: managedObjectContext, sectionNameKeyPath: nil, cacheName: nil)
}

Using the fetched results controller as a table view data source happens through glue code in a CoreDataFetchDataSource class, which is out of scope to discuss here, but you can read more in the example on GitHub.

All the Contentful synchronization specific code is in a single class, ContentfulDataManager (read more on GitHub):

1
2
3
4
class ProductList: UITableViewController {
    lazy var dataManager: ContentfulDataManager = {
        return ContentfulDataManager()
    }()

You can instantiate a specific data source for the table view:

1
2
3
4
5
6
7
8
9
10
11
12
13
lazy var dataSource: CoreDataFetchDataSource<ProductCell> = {
        let resultsController = try! self.dataManager.fetchedResultsController(forContentType: Product.self, predicate: self.predicate, sortDescriptors: [NSSortDescriptor(key: "productName", ascending: true)])
        return CoreDataFetchDataSource<ProductCell>(fetchedResultsController: resultsController, tableView: self.tableView)
    }()

    var predicate: NSPredicate = NSPredicate(value: true)

    override func viewDidLoad() {
      super.viewDidLoad()

      tableView.dataSource = dataSource
    }
}