Skip to content

Instantly share code, notes, and snippets.

@nivbp7
Created January 27, 2022 14:46
Show Gist options
  • Save nivbp7/8ff337d3879a43f3edfe6ef1ee506b40 to your computer and use it in GitHub Desktop.
Save nivbp7/8ff337d3879a43f3edfe6ef1ee506b40 to your computer and use it in GitHub Desktop.
Contentful Controller for testing completion handler is invoked twice on failure case
import Foundation
import Contentful
final class ContentfulController: NSObject {
// --------------------------------------------------------------------------------
// MARK:
// MARK: Singleton
static let shared = ContentfulController()
// --------------------------------------------------------------------------------
// MARK:
// MARK: Constants - Private
private let spaceID = <SpaceID>
private let deliveryAPIAccessToken = <Token>
// --------------------------------------------------------------------------------
// MARK:
// MARK: Interface - Private
private var client: Client!
// --------------------------------------------------------------------------------
// MARK:
// MARK: Initialization/deinitalization
private override init() {
super.init()
let contentTypeClasses: [EntryDecodable.Type] = [
ContentfulPost.self,
ContentfulAuthor.self,
]
client = Client(spaceId: spaceID,
accessToken: deliveryAPIAccessToken,
contentTypeClasses: contentTypeClasses)
}
// --------------------------------------------------------------------------------
// MARK:
// MARK: Interface - Public
func fetchPosts(completion: @escaping (Result<[ContentfulPost],Error>) -> Void) {
client.fetchArray(of: ContentfulPost.self) { (result) in
switch result {
case .success(let response):
print(response.items.count)
completion(.success(response.items))
case .failure(let error):
print("error in fetchPosts")
completion(.failure(error))
}
}
}
}
final class ContentfulPost: EntryDecodable, FieldKeysQueryable, Codable {
static let contentTypeId: String = "post"
let id: String
let localeCode: String?
let updatedAt: Date?
let createdAt: Date?
let title: String?
let type: String?
var author: ContentfulAuthor?
let readingTime: Int?
let body: Int? //This is intentionally set as in Int in order to have a fail case
var heroImage: Asset?
var video: 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: ContentfulPost.FieldKeys.self)
self.title = try fields.decodeIfPresent(String.self, forKey: .title)
self.body = try fields.decodeIfPresent(Int.self, forKey: .body)
self.type = try fields.decodeIfPresent(String.self, forKey: .type)
self.readingTime = try fields.decodeIfPresent(Int.self, forKey: .readingTime)
try fields.resolveLink(forKey: .heroImage, decoder: decoder) { [weak self] heroImage in
self?.heroImage = heroImage as? Asset
}
try fields.resolveLink(forKey: .author, decoder: decoder, callback: {[weak self] (author) in
self?.author = author as? ContentfulAuthor
})
try fields.resolveLink(forKey: .video, decoder: decoder, callback: {[weak self] (video) in
self?.video = video as? Asset
})
}
enum FieldKeys: String, CodingKey {
case title, type, author, readingTime, body, heroImage, video
}
}
final class ContentfulAuthor: EntryDecodable, FieldKeysQueryable, Codable {
static let contentTypeId: String = "author"
let id: String
let localeCode: String?
let updatedAt: Date?
let createdAt: Date?
let name: String?
let url: String?
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: ContentfulAuthor.FieldKeys.self)
self.name = try fields.decodeIfPresent(String.self, forKey: .name)
self.url = try fields.decodeIfPresent(String.self, forKey: .url)
try fields.resolveLink(forKey: .image, decoder: decoder) { [weak self] image in
self?.image = image as? Asset
}
}
enum FieldKeys: String, CodingKey {
case name, url, image
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment