Swift-API调用/CompletionHandler/URLSessions的问题



我这里有一个长函数,它进行一系列API调用,解析数据,并返回两个数组,表示一系列观测位置(一个数组表示纬度,一个数组代表纬度)。我遇到的问题是确定何时完成两个阵列的填充。理想情况下,我希望能够放置

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

在我的代码中的某个地方,并在控制台中接收到一条打印语句,读取ArrayCount=123。然而,无论我在哪里放置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()
}

我不确定你到底想做什么,但一旦我精简了你极其冗长的代码并试图理解它,我认为结论是你只想在内部循环的最后一次迭代后记录数组计数。例如:

而不是这样做: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)")
}

这应该适用于您的情况。然而,我完全不同意执行此操作,因为您的代码缺乏可移植性和可重用性。此外,还有相当多的好的语言结构被忽视了。使用几个好的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)")
})

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

最新更新