Search code examples
swiftalamofire

Making multiple requests with Alamofire depending on array of chunks


I'm struggling with making badge requests with Alamofire and I need help.

I have some ids and with them I need to struct parameters (Dictionary String) and send a GET request with Alamofire. Everything is fine, but I need to cover the case when ids are above 200, because when they are more than 200, API returns 414 code status (too long URL). So when ids are more than 200 they are separated in chunks. With each chunk I'm making a new request to API. The problem is that I return only the first 200 ids when I call my method. Here is an example:

func request (_ idsDict: [String: [String]], _ idSchema: String, _ completion: @escaping Result<SomeModel, Error>) -> Void {
        let chunks = transformEntitiesIdsToChunks(idsDict)
        let decoder = JSONDecoder()
        
        chunks.forEach {chunk in
            let parameters = constructQueryParams(idsDict, chunk, idSchema, apiKey, clientId)
            AF.request(baseURL, parameters: parameters).response { response in
                switch response.result {
                case .success(let data):
                    // some error handling for decoding and no data

                    completion(.success(data.data))
                case .failure(let error):
                    return completion(.failure(error.localizedDescription))
                }
            }
        }
    }

// Method wraps AF request in a continuation block and makes sure that the closure from request method returned data or throwed error.

// That way fetching from API becomes async/await and can be used in do/try/catch block.

func getIdsEntities (_ idsDict: [String: [String]], _ idSchema: String) async throws -> [SomeModel] {
        return try await withUnsafeThrowingContinuation { continuation in
            request(idsDict, idSchema) { result in
                switch result {
                case .success(let data):
                    continuation.resume(returning: data)
                    return
                case .failure(let error):
                    continuation.resume(throwing: error)
                    return
                }
            }
        }
    }

I have tried with recursive functions and with DispatchGroup but none of them worked. Any help will be appriciated. Thank you in advance.


Solution

  • Thanks to Larme's comment I was able to find my mistake. When making request to API I was passing the decoded response to the completion closure. To fix this I had to declare an array of model let responses:[SomeModel] = [] and append the decoded result to it. I used let group = DispatchGroup() so I can wait the requests to execute and have my final array of results and then I used group.notify(queue: .main, execute: {completion(.success(responses))}) to return to the main queue and have my array of completed fetched data. This is now how my code looks like:

     private func request (_ idsDict: [String: [String]], _ idSchema: String, _ completion: @escaping APIListResponseClosure<SomeModel>) -> Void {
            var responses: [SomeModel] = []
            let group = DispatchGroup()
            let chunks = transformEntitiesIdsToChunks(idsDict)
            let decoder = JSONDecoder()
            
            chunks.forEach {chunk in
                group.enter()
                let parameters = constructQueryParams(idsDict, chunk, idSchema, apiKey, clientId)
                AF.request(baseURL, parameters: parameters).response { response in
                    switch response.result {
                    case .success(let data):
                        // some error handling for decoding and no data
    
                        responses.append(data.data)
                        group.leave()
                    case .failure(let error):
                        return completion(.failure(.APIError(error.localizedDescription)))
                    }
                }
            }
            group.notify(queue: .main, execute:  {
                print("Ids are fetched")
                completion(.success(responses))
            })
        }
    

    Thanks again to Larme and I hope I helped someone else with this case.