swift withTaskGroup不适用于CLGeocoder

niwlg2el  于 2023-11-16  发布在  Swift
关注(0)|答案(1)|浏览(123)

我需要实现基于用户输入的地址搜索功能。对于地址建议,我使用MKLocalSearchCompleter。在检索到建议后,我需要根据地址的标题获取地标。为了获取地标,我使用CLGeocoder函数geocodeAddressString。由于这是一个cnrc函数,我需要执行多个请求,用每个地址一个接一个地单独调用它太慢了,因此我需要合并并同时执行它们。withTaskGroup似乎是一个完美的方法,但问题是,由于某种原因,任务组只是停止,没有任何错误或异常。2只有第一个任务通过,任务永远挂起。
我尝试用多种不同的方式重写同一个东西,但没有任何帮助。为了使这尽可能简单,我创建了一个单独的Playgrounds项目,只是为了尽可能隔离它。令我惊讶的是,这个问题仍然存在!下面是Playgrounds的代码:

import CoreLocation
import MapKit

class MapManager: NSObject, MKLocalSearchCompleterDelegate {
    
    let geocoder = CLGeocoder()
    
    private lazy var localSearchCompleter: MKLocalSearchCompleter = {
        let completer = MKLocalSearchCompleter()
        completer.delegate = self
        return completer
    }()
    
    func searchAddress(_ query: String) {
        localSearchCompleter.queryFragment = query
    }
    
    func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
        Task {
            let placemarks = try! await withThrowingTaskGroup(of: [CLPlacemark].self) { group in
                
                var placemarks: [CLPlacemark] = []
                
                print(completer.results.count) // 15
                
                for result in completer.results {
                    print("Task was added to the group")
                    group.addTask { try! await self.geocoder.geocodeAddressString(result.title) }
                }
                
                
                for try await placemark in group {
                    print("Task appended to a list")
                    placemarks.append(contentsOf: placemark)
                }
                
                // This is never called
                return placemarks
            }
            
            print(placemarks)
        }
    }
}

let manager = MapManager()
manager.searchAddress("wall street")

字符串
当基于查询“华尔街”搜索地址时,completer返回15个完成。我需要将15个任务添加到任务组,每个地址一个。然后对每个地址进行地理编码以获取其地标。问题是所有任务都添加到组中,但只有第一个任务被追加到列表中。下面是控制台输出:

15
Task was added to the group
Task was added to the group
Task was added to the group
Task was added to the group
Task was added to the group
Task was added to the group
Task was added to the group
Task was added to the group
Task was added to the group
Task was added to the group
Task was added to the group
Task was added to the group
Task was added to the group
Task was added to the group
Task was added to the group
Task appended to a list


我想知道这是CLGeocoder,MKLocalSearchCompleter,TaskGroup的问题还是我做错了什么。这看起来是一件非常简单的事情,它可以完美地与其他模拟类型和值一起工作,但由于某种原因,位置服务和任务组的组合完全破坏了项目。
多谢帮忙!

pxy2qtax

pxy2qtax1#

您正在并行执行地理编码请求。Apple的地理编码API不适用于并行查询。如果您连续运行它们,它不会挂起:

func completerDidUpdateResults(_ completer: MKLocalSearchCompleter) {
    Task {
        var placemarks: [CLPlacemark] = []

        for result in completer.results {
            let searchString = [result.title, result.subtitle]
                .filter { !$0.isEmpty }
                .joined(separator: ", ")

            print("searchString =", searchString)

            do {
                let placemark = try await geocoder.geocodeAddressString(searchString)
                print(placemark)
                placemarks.append(contentsOf: placemark)
            } catch let error as CLError {
                print(error)
            } catch {
                print(error)
            }
        }
    }
}

字符串
FYI,documentation警告我们:

使用地理编码器对象的提示

应用程序必须意识到它们如何使用地理编码。每个应用程序的地理编码请求都有速率限制,因此在短时间内发出太多请求可能会导致某些请求失败。(当超过最大速率时,地理编码器会向关联的完成处理程序返回一个错误对象,其中包含CLError.Code.network错误。)以下是有效使用此类的一些经验法则:

  • 对于任何一个用户操作,最多发送一个地理编码请求。
  • 如果用户执行多个涉及对同一位置进行地理编码的操作,请重用初始地理编码请求的结果,而不是为每个操作启动单独的请求。
  • 当您希望自动更新用户的当前位置时(例如当用户移动时),请仅在用户移动了相当长的距离并经过合理的时间后才发出新的地理编码请求。例如,在典型情况下,您不应每分钟发送多个地理编码请求。
  • 不要在用户无法立即看到结果的时候启动地理编码请求。例如,如果应用程序处于非活动状态或处于后台,则不要启动请求。

所以,在向您展示了如何解决未完成的地理编码任务之后,您真的不应该对所有单独的MKLocalSearchCompleterresults进行地理编码。当用户输入时,我们将输入反跳,然后调用完成程序来呈现一些文本字段完成选项。但仅此而已。我们不应该对所有这些进行地理编码。
因此,用户正在输入,我们调用完成器(可能在一点去抖动之后)。用户看到完成器UI(没有额外的地理编码请求),要么选择一个,要么继续输入,只有当他们点击回车或选择一个时,应用程序才应该在他们选择的那个上执行实际的MKLocalSearch
当您执行最终的MKLocalSearch时,结果包括详细的mapItems以及所有必要的地理编码数据。
底线是,应该将完成者UX与搜索UX分离。

相关问题