Search code examples
swiftgenericscompletionhandler

Swift generics in completion handler


Im trying to refactor dat fetching func to enable it for several Decodable struct types.

    func fetchData<T: Decodable>(_ fetchRequest: FetchRequestType, completion: @escaping ((Result<T, Error>) -> Void)) {
    guard !isFetching else { return }
    isFetching = true
    
    guard let url = getURL(fetchRequest) else { assertionFailure("Could not compose URL"); return }
    let urlRequest = URLRequest(url: url)
    
    self.session.dataTask(with: urlRequest) { [unowned self] (data, response, error) in
        guard let response = response as? HTTPURLResponse,
            response.statusCode == 200 else {
                self.isFetching = false
                completion(.failure(NSError()))
                return
        }

        guard let data = data else { assertionFailure("No data"); return }
        
        if let jsonData = try? JSONDecoder().decode(T.self, from: data) {
            self.isFetching = false
            completion(.success(jsonData))
        } else {
            assertionFailure("Could not decode JSON data"); return
        }

        
    }.resume()
    
}

But when Im calling the func from controller with one of Decodable types I get a compile error

Generic parameter 'T' could not be inferred

        networkClient.fetchData(.accountsSearch(searchLogin: text, pageNumber: 1)) { [unowned self] result in
        switch result {
            case .success(let dataJSON):
                let accountsListJSON = dataJSON as! AccountsListJSON
                let fetchedAccounts = accountsListJSON.items
                    .map({ AccountGeneral(login: $0.login, id: $0.id, avatarURLString: $0.avatarURL, type: $0.type) })
                self.accounts = fetchedAccounts
            
            case .failure(_):
                assertionFailure("Fetching error!")
            
        }
    }

Please help me to find out what happened and solve a problem.


Solution

  • You can generally help the compiler to infer the T type by providing the result type, when you call fetchData(_:completion:) function like this:

    networkClient.fetchData(
        .accountsSearch(searchLogin: text, pageNumber: 1)
    ) { [unowned self] (result: Result<AccountsListJSON, Error>) in
        ...
    }