Getting Started with Contentful and 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 a user's location by using our global CDN.

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

Requirements

This tutorial assumes that you understand 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.

Install the SDK

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 a Podfile for your project which adds Contentful as a dependency for you project. You can specify a particular version(s) of the SDK using the various operators that Cocoapods offers

use_frameworks!

target 'Your Xcode targets name' do
  pod 'Contentful'
end

Initialize 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.

let spaceId = "<space_id>"
let token = "<access_token>"

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

Accessing data

The Client class manages all requests to the API, and now that you have initialized a Client instance, you can fetch entries. Note that you must retain your Client instance as a property (instance variable) in your application so that asynchronous callbacks are not released by the system before execution.

Fetch one entry:

client.fetchEntry(id: "nyancat") { (result: Result<Entry>) in
    switch result {
    case .success(let nyanCat):
        print(nyanCat.id) // Prints "nyanCat" to console.

    case .error(let error):
        print("Oh no something went wrong: \(error)")
    }
}

Fetch all entries with content_type = "cat"

let query = Query.where(contentTypeId: "cat")

client.fetchEntries(matching: query) { (result: Result<ArrayResponse<Entry>>) in
    switch result {
    case .success(let entriesArrayResponse):
        let cats = entriesArrayResponse.items

        // Do stuff with cat entries.

    case .error(let error):
        print("Oh no something went wrong: \(error)")
    }
}

A more refined approach

While the above approach is great for quickly fetching data from the API, it can also be useful to retrieve already serialized instances of your own model classes. The EntryDecodable protocol enables said mapping. EntryDecodable actually extends the Decodable protocol which is standard library starting in Swift 4, so all the methods for deserailizing your fields should look familiar, and the Contentful Swift SDK extends Swift's native protocols to make things easier.

Let's get started by implementing a model class, Cat, which will conform to our EntryDecodable as well as the EntryQueryable protocols:

final class Cat: EntryDecodable, EntryQueryable {

    static let contentTypeId: String = "cat"

    let sys: Sys
    let color: String?
    let name: String?
    let lives: Int?
    let likes: [String]?

    // Relationship fields.
    var bestFriend: Cat?
    var image: Asset?

    public required init(from decoder: Decoder) throws {
        sys             = try decoder.sys()
        let fields      = try decoder.contentfulFieldsContainer(keyedBy: Cat.Fields.self)

        self.name       = try fields.decodeIfPresent(String.self, forKey: .name)
        self.color      = try fields.decodeIfPresent(String.self, forKey: .color)
        self.likes      = try fields.decodeIfPresent(Array<String>.self, forKey: .likes)
        self.lives      = try fields.decodeIfPresent(Int.self, forKey: .lives)

        try fields.resolveLink(forKey: .bestFriend, decoder: decoder) { [weak self] linkedCat in
            self?.bestFriend = linkedCat as? Cat
        }
        try fields.resolveLink(forKey: .image, decoder: decoder) { [weak self] image in
            self?.image = image as? Asset
        }
    }

    // If your field names and your properties names differ, you can define the mapping in your `Fields` enum.
    enum Fields: String, CodingKey {
        case bestFriend, image
        case name, color, likes, lives
    }
}

Now take a look at what a query on our Cat class would look like. In particular, we'll create a query where the color field of our cat is set to "gray".

let query = QueryOn<Cat>.where(field: .color, .equals("gray"))

// Note the type in the asynchronously returned result: An `MappedArrayResponse` with `Cat` as the item type.
client.fetchMappedEntries(matching: query) { (result: Result<MappedArrayResponse<Cat>>) in
    switch result {
    case .success(let catsResponse):
        guard let cat = catsResponse.items.first else { return }

        print(cat.color!) // Prints "gray" to console.

    case .error(let error):
        print("Oh no something went wrong: \(error)")
    }
}

Next steps

Not what you’re looking for? Try our FAQ.