Home > Net >  How can I get my iOS app connected with api?
How can I get my iOS app connected with api?

Time:04-10

I am a beginner in iOS development. I was trying to use an api URl: https://www.arbeitnow.com/api/job-board-api in my job search iOS app. But nothing shows on my app. I tested the URL in POSTMAN and it returns json(but HTML in description part?). I wrote the code:

func getResults(completed: @escaping (Result<[Results], ErrorMessage>) -> Void) {
        
        let urlString = "https://www.arbeitnow.com/api/job-board-api"
        guard let url = URL(string: urlString) else {return}
        
        let task = URLSession.shared.dataTask(with: url) { (data, response, error) in
            
            if let _ = error {
                completed(.failure(.invalidData))
                return
            }
            
            guard let response = response as? HTTPURLResponse, response.statusCode == 200 else {
                completed(.failure(.invalidResponse))
                return
            }
            
            guard let data = data else {
                completed(.failure(.invalidData))
                return
            }
            
            do {
                let deconder = JSONDecoder()
                deconder.keyDecodingStrategy = .convertFromSnakeCase
                let results = try deconder.decode([Results].self, from: data)
                completed(.success(results))
                
                
            } catch {
                completed(.failure(.invalidData))
            }
            
            
        }
        task.resume()
    }

Results is:

struct Results: Codable {
    let slug, companyName, title, resultsDescription: String
    let remote: Bool
    let url: String
    let tags, jobTypes: [String]
    let location: String
    let createdAt: Int

    enum CodingKeys: String, CodingKey {
        case slug
        case companyName = "company_name"
        case title
        case resultsDescription = "description"
        case remote, url, tags
        case jobTypes = "job_types"
        case location
        case createdAt = "created_at"
    }
}

I used the code in HomeViewController:

override func viewDidLoad() {
        super.viewDidLoad()
        title = "Home"
        collectionView.backgroundColor = UIColor(named: "backgroundMain")
        collectionView.register(SearchViewCell.self, forCellWithReuseIdentifier: cellId)
        setupSearchBar()
        
        Service.shared.getResults() {[weak self] result in
            switch result {
            case .success(let results):
                print(results)
                self?.jobResults = results
                
                DispatchQueue.main.async {
                    self?.collectionView.reloadData()
                }
                
                
            case .failure(let error):
                print(error)
            }
        }
    }

I don't know what is wrong with my code. Can anyone help? Thanks!

CodePudding user response:

Your model does not match the JSON you receive from the link you provided:

Using:

struct Root: Codable{
   let data: [WorkData]
   let links: Links
   let meta: Meta
}

// MARK: - Links
struct Links: Codable {
    let first: String
    let last, prev: String?
    let next: String
}

// MARK: - Meta
struct Meta: Codable {
    let currentPage, from: Int
    let path: String
    let perPage, to: Int
    let terms, info: String

    enum CodingKeys: String, CodingKey {
        case currentPage = "current_page"
        case from, path
        case perPage = "per_page"
        case to, terms, info
    }
}

struct WorkData: Codable {
    let slug, companyName, title, payloadDescription: String
    let remote: Bool
    let url: String
    let tags, jobTypes: [String]
    let location: String
    let createdAt: Int

    enum CodingKeys: String, CodingKey {
        case slug
        case companyName = "company_name"
        case title
        case payloadDescription = "description"
        case remote, url, tags
        case jobTypes = "job_types"
        case location
        case createdAt = "created_at"
    }
}

should solve the problem

Usage:

let root = JsonDecoder().decode(Root.self, from: data)
let firstCompany = root.data[0]

CodePudding user response:

You are discarding all meaningful error information, which will make this hard to diagnose. If you get an Error object, you should return that:

enum WebServiceError: Error {
    case httpError(Data, Int)
}

func getResults(completion: @escaping (Result<[Results], Error>) -> Void) {
    let urlString = "https://www.arbeitnow.com/api/job-board-api"
    guard let url = URL(string: urlString) else {
        completion(.failure(URLError(.badURL)))
        return
    }

    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        guard
            let data = data,
            let response = response as? HTTPURLResponse,
            error == nil
        else {
            completion(.failure(error ?? URLError(.badServerResponse)))
            return
        }

        guard 200 ..< 300 ~= response.statusCode else {
            completion(.failure(WebServiceError.httpError(data, response.statusCode)))
            return
        }

        do {
            let decoder = JSONDecoder()
            decoder.keyDecodingStrategy = .convertFromSnakeCase
            let results = try decoder.decode([Results].self, from: data)
            completion(.success(results.data))
        } catch {
            completion(.failure(error))
        }
    }
    task.resume()
}

So, that will,

  • if there was a URLSession error, tell you what the error was;
  • if there was a non-2xx status code, tell you what the code was (and return the body of the response, too, in case you want to look at that); and
  • if there was a parsing error, tell you what the parsing error was.

Without something like this, that captures the salient error information, you are flying blind.


In this case, the error is that you are parsing for [Results], but the structure is a dictionary, whose key is data and whose value is a [Results]. You are missing an object for this dictionary that wraps the [Results].

struct ResponseObject: Decodable {
    let data: [Posting]
    let link: Link
    let meta: Meta
}

struct Posting: Decodable {
    let slug, companyName, title, description: String
    let remote: Bool
    let url: String
    let tags, jobTypes: [String]
    let location: String
    let createdAt: Int
}

struct Link: Decodable {
    let first: URL?
    let last: URL?
    let prev: URL?
    let next: URL?
}

struct Meta: Decodable {
    let currentPage: Int
    let path: URL
    let perPage: Int
    let from: Int
    let to: Int
    let terms: String
    let info: String
}

func getResults(completion: @escaping (Result<[Posting], Error>) -> Void) {
    let urlString = "https://www.arbeitnow.com/api/job-board-api"
    guard let url = URL(string: urlString) else {
        completion(.failure(URLError(.badURL)))
        return
    }

    let task = URLSession.shared.dataTask(with: url) { data, response, error in
        guard
            let data = data,
            let response = response as? HTTPURLResponse,
            error == nil
        else {
            completion(.failure(error ?? URLError(.badServerResponse)))
            return
        }

        guard 200 ..< 300 ~= response.statusCode else {
            completion(.failure(WebServiceError.httpError(data, response.statusCode)))
            return
        }

        do {
            let decoder = JSONDecoder()
            decoder.keyDecodingStrategy = .convertFromSnakeCase
            let results = try decoder.decode(ResponseObject.self, from: data)
            completion(.success(results.data))
        } catch {
            completion(.failure(error))
        }
    }
    task.resume()
}
  • Related