我需要实现基于用户输入的地址搜索功能.对于地址建议,我使用MKLocalSearchCompleter.在检索到建议后,我需要根据地址的标题获得地标.为了获得地标,我使用了CLGeocoder函数geocodeAddressString.由于这是一个异步函数,我需要做多个请求,每个地址一个接一个地单独调用它太慢了,所以我需要组合这些请求并同时执行它们.withTaskGroup似乎是完成此操作的完美方法,但问题是,由于某些原因,任务组只是停止,没有任何错误或异常.只有第一个任务完成,该任务将永远挂起.

我试着用多种不同的方式重写相同的东西,但都无济于事.为了让这件事尽可能简单,我创建了一个单独的playground 项目,只是为了让它尽可能地孤立.令我惊讶的是,这个问题仍然存在!以下是操场上的代码:

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

当根据查询"Wall Street"搜索地址时,Complete er返回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

我想知道这是CLGeocode、MKLocalSearchCompleter、Taskgroup的问题,还是我只是做错了什么.这似乎是一件非常简单的事情,它与其他被嘲笑的类型和值完美地工作,然而出于某种原因,位置服务和任务组的组合完全 destruct 了这个项目.

多谢帮忙!

推荐答案

您正在并行执行地理编码请求.苹果的地理编码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)
            }
        }
    }
}

仅供参考,documentation警告我们:

Tips for Using a Geocoder Object

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

  • 对于任何一个用户操作,最多发送一个地理编码请求.

  • 如果用户执行涉及对同一位置进行地理编码的多个操作,请重复使用初始地理编码请求的结果,而不是 for each 操作启动单独的请求.

  • 当您想要自动更新用户的当前位置时(例如,当用户移动时),仅当用户移动了一段相当长的距离并且经过了一段合理的时间后,才发出新的地理编码请求.例如,在典型情况下,您不应每分钟发送一个以上的地理编码请求.

  • 不要在用户无法立即看到结果时启动地理编码请求.例如,如果应用程序处于非活动状态或在后台,请不要启动请求.

因此,在向您展示了如何绕过未完成的地理编码任务之后,您真的不应该对所有个体MKLocalSearchCompleter results进行地理编码.当用户输入时,我们go 掉输入,然后调用完成器来显示一些文本字段完成选项.但仅此而已.我们应该对所有这些都进行not地理编码.

所以,用户正在输入,我们调用完成器(可能在一点debounce 之后).用户看到完成器UI(没有额外的地理编码请求), Select 一个或继续输入,只有当他们点击回车或 Select 一个时,应用程序才应该在他们 Select 的那个上执行实际的MKLocalSearch.

当您执行最后的MKLocalSearch时,结果包括详细的mapItems和所有必要的地理编码数据.

归根结底,应该将完成者用户体验与搜索用户体验分离.

Ios相关问答推荐

Xcode15.3上的生成错误:调用的对象类型';facebook::flipper::SocketCertificateProvider';不是函数或函数指针

如何在Android或iPhone上的原生react 中处理其他应用程序通知?

在flatter_riverpod中,如果没有ref对象,我们如何访问提供程序?

分配给指针会通过 class_addMethod 导致 EXC_BAD_ACCESS

拖动手势导致 sim 崩溃 Swift UI

如何在Combine发布者之间强制执行最小延迟

Flutter动画放大图

我正在try 制作一种 Select 器样式,例如设置中的外观 Select 器

视图之间的 SwiftUI 过渡,没有过渡

SwiftUI:通过Sheet将数据添加到Realm DB时视图不更新

DateFormatter 使用 TimeZone 和 Locale 的特定组合返回 nil

按钮在 SwiftUI 中没有采用给定的高度和宽度

在代码生成的 UIView 上绘制 UIBezierPath

Swift枚举继承

在 iOS 7 上更改标签栏色调 colored颜色

每个版本的 iOS 都附带什么版本的移动 Safari?

iPhone 未连接.连接 iPhone 后 Xcode 将继续

IOS - 从 UITextView 中删除所有填充

Objective-C 将 NSDate 设置为当前 UTC

如何在 iphone 屏幕中间显示活动指示器?