Swift - API调用/ CompletionHandler / URLSessions

时间:2017-01-20 21:36:32

标签: ios swift xcode

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

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

在我的代码中的某处,并在控制台中接收一个读取ArrayCount = 123的单个print语句。但是在我放置print语句的任何地方,我得到的数组计数为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()
}

1 个答案:

答案 0 :(得分:0)

我不确定你到底想要做什么,但是一旦我将你那令人难以置信的冗长代码弄清楚并试图理解它,我认为结论是你只想要记录内循环最后一次迭代后的数组计数。例如:

而不是这样做:for layerThree in layerTwo

执行此操作:for (index, layerThree) in layerTwo.enumerated()

然后,在追加lat / long值后,添加以下检查:

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

这适用于您的情况。但是,由于缺乏可移植性和代码的可重用性,我完全不同意执行此操作。此外,还有很多优秀的语言结构被忽略了。使用一些好的if let语句可以显着减少无情的嵌套guard块。此外,考虑到您无法在任何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)")
})

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