【问题标题】:Swift - Troubles with API calls / CompletionHandler / URLSessionsSwift - API 调用 / CompletionHandler / URLSessions 的问题
【发布时间】:2017-01-20 21:36:31
【问题描述】:

我在这里有一个很长的函数,它会进行一堆 API 调用,解析数据并返回两个数组,代表一堆观光地点(一个数组保存纬度,一个保存纬度)。我遇到的问题是确定两个数组何时完成填充。理想情况下,我希望能够放置

print("ArrayCount = \(self.latArray.count)")

在我的代码中的某处,并在控制台中接收到一条打印语句,读取 ArrayCount = 123。但是,在我放置打印语句的任何地方,我都会得到一个数组计数 0,或者在添加它们时打印出一个循环值 (1 ..2..3.. ... ..123)。提前致谢!

func someFunction()
{
    let url:URL = URL(string: "...")

    let task = URLSession.shared.dataTask(with: URLRequest(url: url))
    {
        data, response, error in

        if error != nil
        {
            print("ERROR IN API REQUEST: \(error!.localizedDescription)")
        }

        else
        {
            do
            {
                if let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any]
                {
                    if let layerOne = parsedData["one"] as? [String: Any]
                    {
                        if let layerTwo = layerOne["two"] as? [[String: Any]]
                        {
                            for layerThree in layerTwo
                            {
                                if let variableName = layerThree["value"] as? String
                                {
                                    let innerUrl:URL = URL(string: "...")!

                                    let innerTask = URLSession.shared.dataTask(with: URLRequest(url: innerUrl))
                                    {
                                        data, response, error in

                                        if error != nil
                                        {
                                            print("ERROR IN API REQUEST: \(error!.localizedDescription)")
                                        }

                                        else
                                        {
                                            do
                                            {
                                                if let parsedData = try JSONSerialization.jsonObject(with: data!, options: .allowFragments) as? [String: Any]
                                                {
                                                    if let layerA = parsedData["A"] as? [String: Any]
                                                    {
                                                        if let lat = layerA["Latitude"] as? String, let long = layerA["Longitude"] as? String
                                                        {
                                                            self.latArray.append(lat)
                                                            self.longArray.append(lon)
                                                        }
                                                    }
                                                }
                                            }

                                            catch
                                            {
                                                print("ERROR IN JSON SERIALIZATION")
                                            }
                                        }
                                    }
                                    innerTask.resume()
                                }
                            }
                        }
                    }
                }
            }

            catch
            {
                print("ERROR IN JSON SERIALIZATION")
            }
        }
    }

    task.resume()
}

【问题讨论】:

  • 你能提供一个你收到的 JSON 样本吗?你解析这个 JSON 的方式看起来很奇怪。
  • 您是否通过并替换所有变量和下标来使用随机的任意名称?这看起来有点像您正在尝试解析来自 Google Places API 的 API 响应。为什么不直接发布您的原始代码?这样,其他人就可以更轻松地帮助您解决问题。
  • 您可以为每个if 语句执行多个条件绑定。你不需要所有这些疯狂的嵌套。

标签: ios swift xcode


【解决方案1】:

我不确定您到底要做什么,但是一旦我精简了您令人难以置信的冗长代码并尝试理解它,我认为结论是您只想在内部循环的最后一次迭代。例如:

不要这样做:for layerThree in layerTwo

这样做:for (index, layerThree) in layerTwo.enumerated()

然后,在附加纬度/经度值后,添加此检查:

if index == layerTwo.count - 1 //if this is our last inner loop
{
    //print the array counts
    print("Lat count: \(latArray.count)")
    print("Long count: \(longArray.count)")
}

这应该适用于您的情况。但是,我完全不同意执行此操作,因为您的代码缺乏可移植性和可重用性。此外,还有很多好的语言结构被忽略了。使用一些好的guard 语句可以显着减少无情的嵌套if let 块。此外,考虑到您无论如何都没有处理任何 catch 块中的错误,不妨删除它们并选择 try? 代替。一种更快捷的处理方式是在函数本身中包含一个完成处理程序,以便可以在其他地方处理数组计数打印逻辑。我将包含一些关于如何清理的示例代码:

func someFunction(completion: (([String], [String]) -> Void)?)
{
    let url = URL(string: "...")!
    var latArray: [String] = []
    var longArray: [String] = []

    let task = URLSession.shared.dataTask(with: URLRequest(url: url),
        completionHandler: { (data, response, error) -> Void in
            guard error == nil else
            {
                print("ERROR IN API REQUEST: \(error?.localizedDescription)")
                return
            }

            guard let parsedData = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments),
                let parsedDict = parsedData as? [String: Any],
                let layerOne = parsedDict["one"] as? [String: Any],
                let layerTwo = layerOne["two"] as? [[String: Any]] else
            {
                print("JSON OBJECT DOES NOT MATCH EXPECTED PATTERN")
                return
            }

            for (index, layerThree) in layerTwo.enumerated()
            {
                guard let _ = layerThree["value"] as? String else
                {
                    print("Skip this iteration (I guess?)")
                    continue
                }

                let innerUrl = URL(string: "...")!
                let innerTask = URLSession.shared.dataTask(with: URLRequest(url: innerUrl),
                    completionHandler: { (data, response, error) -> Void in
                        guard error == nil else
                        {
                            print("ERROR IN API REQUEST: \(error?.localizedDescription)")
                            return
                        }

                        guard let parsedData = try? JSONSerialization.jsonObject(with: data!, options: .allowFragments),
                            let parsedDict = parsedData as? [String: Any],
                            let layerA = parsedDict["A"] as? [String: Any],
                            let lat = layerA["Latitude"] as? String,
                            let long = layerA["Longitude"] as? String else
                        {
                            print("Something went wrong")
                            return
                        }

                        latArray.append(lat)
                        longArray.append(long)

                        if index == layerTwo.count - 1 //if this is our last inner loop
                        {
                            completion?(latArray, longArray)
                        }
                    }
                )
                innerTask.resume()
            }
        }
    )

    task.resume()
}

someFunction(completion: { (latArray, longArray) -> Void in
    //print the array counts
    print("Lat count: \(latArray.count)")
    print("Long count: \(longArray.count)")
})

这仍然可以进一步改进,但根据您的问题,我认为我个人不应该花更多时间为您构建它。如果您想出一些自己的方法来构建具有更可读和可重用代码的应用程序,那将是最好的。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2021-01-08
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-11-29
    • 1970-01-01
    相关资源
    最近更新 更多