> 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. # Getting Started with Contentful and Swift > This tutorial will walk you through building a simple app from start to finish with our Swift client library. This guide will show you how to get started using our [Swift client library][1] 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 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). ## Install the client library There are different ways to integrate the client library into your own apps, described in detail in the [README for the client library][2]. This guide will use [CocoaPods][3], the dependency manager for Cocoa projects, which helps you keep the client library 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 client library using the [various operators that Cocoapods offers][24] ```ruby 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. ```swift let client = Client(spaceId: "", environmentId: "", // Defaults to "master" if omitted accessToken: "") ``` ## Accessing data The [`Client`][6] 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: ```swift client.fetch(Entry.self, id: "nyancat") { (result: Result) 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" ```swift let query = Query.where(contentTypeId: "cat") client.fetchArray(of: Entry.self, matching: query) { (result: Result>) 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 deserializing your fields should look familiar, and the Contentful Swift client library extends Swift's native protocols to make things easier. Let's get started by implementing a model class, `Cat`, which will conform to both the `EntryDecodable` and `FieldKeysQueryable` protocols: ```swift final class Cat: EntryDecodable, FieldKeysQueryable { static let contentTypeId: String = "cat" // FlatResource members. let id: String let localeCode: String? let updatedAt: Date? let createdAt: Date? 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 { let sys = try decoder.sys() id = sys.id localeCode = sys.locale updatedAt = sys.updatedAt createdAt = sys.createdAt let fields = try decoder.contentfulFieldsContainer(keyedBy: Cat.FieldKeys.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.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 `FieldKeys` enum. enum FieldKeys: String, CodingKey { case bestFriend, image case name, color, likes, lives } ``` **Note:** If you want to simplify the implementation of an `EntryDecodable`, declare conformance to resource, add `let sys: Sys` to the class definition, and assign that property with `sys = try decoder.sys()` during initialization. Then, `id`, `localeCode`, `updatedAt`, and `createdAt` are all provided via the `sys` property and don't need to be declared as class members. Note that this style of implementation may make integration with local database frameworks more cumbersome. 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". ```swift let query = QueryOn.where(field: .color, .equals("gray")) // Note the type in the asynchronously returned result: An `ArrayResponse` with `Cat` as the item type. client.fetchArray(of: Cat.self, matching: query) { (result: Result>) 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)") } } ``` [1]: https://github.com/contentful/contentful.swift [2]: https://github.com/contentful/contentful.swift#cocoapods-installation [3]: https://cocoapods.org [4]: https://github.com/contentful/contentful-persistence.swift [6]: https://github.com/contentful/contentful.swift/blob/c2aa0a311d0da88360e699b726fecbdc5001f844/README.md [7]: /references/authentication [10]: https://github.com/JensRavens/Interstellar [11]: /references/content-delivery-api/overview [12]: https://github.com/contentful/contentful.swift#swift-playground [13]: /concepts/sync/ [20]: https://github.com/contentful/product-catalogue-swift/blob/master/Code/DataSource.swift [21]: https://github.com/contentful/product-catalogue-swift/blob/master/Code/ContentfulDataManager.swift [22]: https://github.com/contentful/contentful.swift [23]: https://github.com/carthage/carthage [24]: https://guides.cocoapods.org/using/the-podfile.html#specifying-pod-versions ## Next steps * [Persist to CoreData with contentful-persistence.swift](/sdks/ios/tutorials/offline-persistence-in-ios-sdk/) > This tutorial will walk you through building a simple app from start to finish with our Swift client library.