Skip to content

Fetching single entry by ID of custom type implementing EntryDecodable never resolves links #251

Closed
@stephanecopin

Description

@stephanecopin
  • contentful.swift version number: 4.2.2
  • Xcode version number: 10.0 (10A254a)
  • Target operating system(s) and version number(s)
    • iOS: 12.0
    • tvOS:
    • watchOS:
    • macOS:
  • Package manager:
    • Carthage
    • Cocoapods

Hey,

It seems like there's an issue in how the SDK checks for custom types implementing the EntryDecodable protocol, when fetching a single entry using the method Client.fetch(_:,id:,then:).
It checks whether the type passed is different from both EntryDecodable.self and Entry.self:

  • For the latter, it's fine, since it's a class that can't be subclassed outside of the repo (though it should also probably be final for the same reason explained below)
  • For the former, resourceType != EntryDecodable.self never will be false, even if the types implement the EntryDecodable protocol.
    This can be reproduced using the following code:
protocol TestProtocol {
	func test() // To add some metadata to the types, just in case
}

extension TestProtocol {
	func test() {
	}
}

struct Test1: TestProtocol {}
class Test2: TestProtocol {}
final class Test3: TestProtocol {}
enum Test4: TestProtocol {}

var testIndex = 1
func checkProtocol<TestType>(_ type: TestType.Type) {
	print("Test\(testIndex) -> \(type != TestProtocol.self)")
	testIndex += 1
}

checkProtocol(Test1.self)
checkProtocol(Test2.self)
checkProtocol(Test3.self)
checkProtocol(Test4.self)

Which will output:

Test1 -> true
Test2 -> true
Test3 -> true
Test4 -> true

This all happens here: https://github.com/contentful/contentful.swift/blob/master/Sources/Contentful/Client.swift#L458-L463
(likewise, if a subclass of Entry is defined in the SDK, for example named MyEntry, then MyEntry.self != Entry.self is true)

I couldn't find a way to check if a meta-type inherits from/implements another in Swift so as not to change the code too much, so unfortunately I don't have any "proper" fix (probably something using generics constraint would be best).
Maybe adding a parameter resolveLinks (defaults to true) to the method would be enough? I'd expect anyway that most people will be retrieving Entry or EntryDecodable subtypes anyway

For now, until this is fixed, I've added a method that always resolved links like:

public func fetchAndResolveLinks<DecodableEntry: EntryDecodable>(
    _ resourceType: DecodableEntry.Type,
    id: String,
    then completion: @escaping ResultsHandler<DecodableEntry>) -> URLSessionDataTask
{
    let fetchCompletion: (Contentful.Result<ArrayResponse<DecodableEntry>>) -> Void = { result in
        switch result {
        case .success(let response) where response.items.first != nil:
            completion(Result.success(response.items.first!))
        case .error(let error):
            completion(Result.error(error))
        default:
            completion(Result.error(SDKError.noResourceFoundFor(id: id)))
        }
    }

    let query = ResourceQuery.where(sys: .id, .equals(id))
    return fetch(url: url(endpoint: DecodableEntry.endpoint, parameters: query.parameters), then: fetchCompletion)
}

(copy/pasting part of the included fetch method)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions