【问题标题】:Xcode - API Failing When Search Size is Greater Than 14Xcode - 当搜索大小大于 14 时 API 失败
【发布时间】:2021-10-29 12:10:35
【问题描述】:

从 iTunes API https://itunes.apple.com/search?term=\(search)&entity=software&limit=14 获取数据时,如果限制为较大的数字(例如 30、40、50 等),则获取失败。该限制由 URL 末尾的limit=14 表示。 14 是返回的结果数。这可以更改为任何数字。

在 Postman 中拨打电话时,我可以输入任何数字的限制,它可以正常工作。此外,在 XCtest 中运行大量 api 时,测试通过。似乎只有在应用中进行通话时才会失败。

失败发生在guard let 语句中。在下面的代码中,如果数字太大(例如 50),它会打印“failed to fetch data”——表明存在 URL 问题。当使用较小的数字(例如 10)时,获取成功并且数据在我的表视图中返回。您还可以更改搜索词。目前我将其设置为“Apple”。

以下是 API 的代码:

 import Foundation
import UIKit

struct Response: Codable {
  var resultCount: Int
  var results: [Result]
}

struct Result: Codable {
  var screenshotUrls, ipadScreenshotUrls, appletvScreenshotUrls: [String]
  var artworkUrl60, artworkUrl512, artworkUrl100, artistViewUrl: String
  var supportedDevices, advisories: [String]
  var isGameCenterEnabled: Bool
  var features: [String]
  var kind, minimumOsVersion, trackCensoredName, fileSizeBytes: String
  var contentAdvisoryRating: String
  var genreIds: [String]
  var primaryGenreName, artistName, trackContentRating, trackName, releaseDate, sellerName, currentVersionReleaseDate, releaseNotes, version: String
  var primaryGenreId: Int
  var currency, description: String
  var price: Double
  var averageUserRating: Double
}

    class API {
  var storedData = Response(resultCount: Int.init(), results: [])
  func loadData(search: String, completionHandler: @escaping (Response) -> Void) {
    guard let url = URL(string:"https://itunes.apple.com/search?term=\(search)&entity=software&limit=40") else {
      print("failed to fetch data")
      return
    }
    let request = URLRequest(url: url)
    URLSession.shared.dataTask(with: request) { data, response, error in
      if let data = data {
        if let response = try? JSONDecoder().decode(Response.self, from: data) {
          DispatchQueue.main.async {
            self.storedData.resultCount = response.resultCount
            self.storedData.results = response.results
            completionHandler(self.storedData)
          }
          return
        }
      }
      print("failed \(error?.localizedDescription ?? "unknown error")")
    }
    .resume()
  }
  func reloadTableData() {
    DataManager.shared.viewController.tableView.reloadData()
  }
}

对于为什么较大的数字会导致在运行应用程序时让守卫失败,但在我的测试或邮递员中没有失败的任何想法?

编辑

以下是我如何调用该函数。我在 viewdidload 中调用它。它使用完成处理程序,因此如下所示:

api.loadData(search: "ibm") { Results in
  self.filteredResults = self.api.storedData.results //stores value in filtered results array
  
  self.tableView.reloadData() //refreshes table view - table view is referencing the filteredResults array
}

【问题讨论】:

  • 上面的代码是否应该重现该问题?我试过guard let url = URL(string:"https://itunes.apple.com/search?term=Apple&entity=software&limit=14"),然后guard let url = URL(string:"https://itunes.apple.com/search?term=Apple&entity=software&limit=500"),它们没有失败。
  • 感谢您查看!你是直接在 A 视图控制器中测试的吗?仅在调用 viewdidload 中的函数时才会发生故障。您发布的第一个链接不会引发错误,因为它是 =14,但在我端运行第二个链接确实会引发错误 (=500)
  • guard let url = URL(string:"https://itunes.apple.com/search?term=Apple&entity=software&limit=14") 在这个问题中提供的方式在有或没有 UIViewController 的情况下都不会做任何不同的事情(因为它有硬编码的 URL)。您正在以某种方式参数化此 URL,或者此处缺少某些其他信息。此外,“引发错误 500”(这是服务器错误)与 URL 防护失败非常不同。如果你得到错误 500,这意味着服务器不接受你的参数,但这也意味着它没有因为数量大而使守卫失败。
  • 我添加了一段额外的代码来展示我是如何调用该函数的。不确定这是否有帮助。
  • 我找到了问题的根源。我在不正确的分支上,这导致我复制了不正确的代码。我已经添加了更新的代码。该问题与我传递给 URL 的参数有关。当我传入一个值时,当数字 >15 时我收到一个错误。没有参数,它可以正常工作。任何想法为什么在传递参数时更大的数字会失败?

标签: ios swift xcode api get


【解决方案1】:

当您设置的限制超过 20 个时,JSON 中缺少许多键:

  • 解码 JSON 时始终使用 doCatch 并打印 error,它会告诉您出了什么问题,由于以下错误,您的源解码失败:refer
    keyNotFound(CodingKeys(stringValue: "releaseNotes", intValue: nil), Swift.DecodingError.Context(codingPath: [CodingKeys(stringValue: "results", intValue: nil), _JSONKey(stringValue: "Index 15", intValue: 15)], debugDescription: "No value associated with key CodingKeys(stringValue: \"releaseNotes\", intValue: nil) (\"releaseNotes\").", underlyingError: nil))
  • 现在尝试在结构体Optional 中设置所有变量来修复解码问题。

使属性可选

struct Response: Codable {
  var resultCount: Int?
  var results: [Result]?
}

struct Result: Codable {
  var screenshotUrls, ipadScreenshotUrls, appletvScreenshotUrls: [String]?
  var artworkUrl60, artworkUrl512, artworkUrl100, artistViewUrl: String?
  var supportedDevices, advisories: [String]?
  var isGameCenterEnabled: Bool?
  var features: [String]?
  var kind, minimumOsVersion, trackCensoredName, fileSizeBytes: String?
  var contentAdvisoryRating: String?
  var genreIds: [String]?
  var primaryGenreName, artistName, trackContentRating, trackName, releaseDate, sellerName, currentVersionReleaseDate, releaseNotes, version: String?
  var primaryGenreId: Int?
  var currency, description: String?
  var price: Double?
  var averageUserRating: Double?
}

把这个放在if-let数据块里面:

        do {
            let response =  try JSONDecoder().decode(Response.self, from: data)
            DispatchQueue.main.async {
                self.storedData.resultCount = response.resultCount
                self.storedData.results = response.results
                completionHandler(self.storedData)
            }
            
        } catch let error {
            print(error)
        }

【讨论】:

  • 很棒的信息。谢谢!当我对字符串进行硬编码时,无论添加的大小如何,它都可以正常工作。当我传入搜索参数时,它似乎只会失败(请参阅更新的代码)。关于为什么它以一种方式而不是另一种方式工作的任何想法?
  • @nickreps 也许“apple”关键字有效,但它不适用于“ibm”。如果这就是你的意思 - itunes.apple.com/…
  • 很奇怪!它似乎对某些参数失败,而不是其他参数。例如,IBM 失败了,“游戏”失败了,但苹果成功了。关于为什么会这样的任何想法?当结果计数 == 14 时搜索“IBM”可以正常工作,但如果结果数量很大,则会失败。
  • @nickreps 这就是我在回答中所说的,重构您的代码并捕获error。 + 使这些变量可选,它不会失败。
  • 谢谢!从我的结构中删除 releaseNotes 键并实现 do let 看起来像是要走的路。似乎 do let 是所有 api 调用中最好的方法,因此可以打印错误。听起来对吗?
猜你喜欢
  • 2015-05-29
  • 1970-01-01
  • 2017-06-01
  • 1970-01-01
  • 2018-11-03
  • 2014-12-23
  • 2018-05-03
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多