Search code examples
iosswifttvoscontentful

Using parameters on async call in swift


I'm having an async call with a completionhandler that fetches data for me through a query. These queries can vary based upon the users action.

My data call looks like this;

class DataManager {
    func requestVideoData(query: QueryOn<VideoModel>, completion: @escaping (([VideoModel]?, UInt?, Error?) -> Void)) {
        client.fetchMappedEntries(matching: query) { (result: Result<MappedArrayResponse<FRVideoModel>>) in
            completion(videos, arrayLenght, nil)
        }
    }
}

My ViewController looks like this;

DataManager().requestVideoData(query: /*One of the queries below*/) { videos, arrayLength, error in
    //Use the data fetched based on the query that has been entered
}

My queries look like this;

let latestVideosQuery = QueryOn<FRVideoModel>().limit(to: 50)
try! latestVideosQuery.order(by: Ordering(sys: .createdAt, inReverse: true))

And this;

let countryQuery = QueryOn<FRVideoModel>()
        .where(valueAtKeyPath: "fields.country.sys.contentType.sys.id", .equals("country"))
        .where(valueAtKeyPath: "fields.country.fields.countryTitle", .includes(["France"]))
        .limit(to: 50)

But I'm not completely sure how I would implement these queries the right way so they correspond with the MVC model.

I was thinking about a switch statement in the DataManager class, and pass a value into the query parameter on my ViewController that would result in the right call on fetchMappedEntries(). The only problem with this is that I still need to execute the correct function according to my query in my VC, so I would need a switch statement over there as well.

Or do I need to include all my queries inside my ViewController? This is something I think is incorrect because it seems something that should be in my model.


Solution

  • In the end I went with a slightly modified networking layer and a router where the queries are stored in a public enum, which I can then use in my functions inside my ViewController. Looks something like this;

    public enum Api {
        case latestVideos(limit: UInt)
        case countryVideos(countries: [String], limit: UInt)
    }
    
    extension Api:Query {
        var query: QueryOn<VideoModel> {
            switch self {
            case .latestVideos(let limit):
                let latestVideosQuery = QueryOn<VideoModel>().limit(to: limit)
                try! latestVideosQuery.order(by: Ordering(sys: .createdAt, inReverse: true))
                return latestVideosQuery
            case .countryVideos(let countries, let limit):
                let countryQuery = QueryOn<VideoModel>()
                    .where(valueAtKeyPath: "fields.country.sys.contentType.sys.id", .equals("country"))
                    .where(valueAtKeyPath: "fields.country.fields.countryTitle", .includes(countries))
                    .limit(to: limit)
                return countryQuery
            }
        }
    }
    

    And the NetworkManager struct to fetch the data;

    struct NetworkManager {
        private let router = Router<Api>()
    
        func fetchLatestVideos(limit: UInt, completion: @escaping(_ videos: [VideoModel]?, _ arrayLength: UInt?,_ error: Error?) -> Void) {
            router.requestVideoData(.latestVideos(limit: limit)) { (videos, arrayLength, error) in
                if error != nil {
                    completion(nil, nil, error)
                } else {
                    completion(videos, arrayLength, nil)
                }
            }
        }
    }