如何让我的 iOS 应用程序连接到 api?

How can I get my iOS app connected with api?

我是 iOS 开发的初学者。我试图在求职 iOS 应用程序中使用 api URl: https://www.arbeitnow.com/api/job-board-api。但我的应用程序上没有显示任何内容。我在 POSTMAN 中测试了 URL,它 returns json(但在描述部分是 HTML?)。我写的代码:

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()
}

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"
    }
}

我使用了 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)
        }
    }
}

888

我不知道我的代码有什么问题。谁能帮忙?谢谢!

您正在丢弃所有有意义的错误信息,这将使诊断变得困难。如果你得到一个 Error 对象,你应该 return 那:

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()
}

所以,那会,

  • 如果有 URLSession 错误,请告诉您错误是什么;
  • 如果有非 2xx 状态代码,请告诉您代码是什么(以及 return 响应的正文,以防您想查看);和
  • 如果有解析错误,告诉你解析错误是什么。

如果没有这样的东西来捕捉显着错误信息,你就是瞎了眼。


在这种情况下,错误是您正在解析 [Results],但结构是一个字典,其键为 data,其值为 [Results]。您缺少此字典的对象,该对象包含 [Results].

struct ResponseObject: Decodable {
    let data: [Posting]
    let links: Links
    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 Links: 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()
}

您的型号与您从您提供的 link 收到的 JSON 不匹配:

使用:

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"
    }
}

应该可以解决问题

用法:

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

编辑评论: 这应该有效!

let results = try decoder.decode([Results].self, from: data)

是你的代码吗?

改为使用:

let root = JsonDecoder().decode(Root.self, from: data)

这里怎么会缺少数据?

之后,您应该将根对象映射到您的 Result 类型,以保持您的 Viewmodel 和完成处理程序保持现在的状态。或者改为更改 Viewmodel 和完成处理程序。