iOS Google Places API 自动完成失败排查与解决
2025-03-19 00:29:45
iOS 应用中 Google Places 自动完成 API 请求失败的排查与解决
最近在 iOS 应用里集成 Google Places 的自动完成 API,遇到了一些问题,请求总是失败,而且错误信息还很笼统。错误日志大概长这样:
Domain=com.google.places.ErrorDomain Code=-1 "Something went wrong with the connection to the Places API server." UserInfo={NSLocalizedFailureReason=Something went wrong with the connection to the Places API server., NSUnderlyingError=0x6000035a8000 {Error Domain=com.google.places.api.server.ErrorDomain Code=-1 "(null)" UserInfo={NSUnderlyingError=0x6000035c5890 {Error Domain=NSURLErrorDomain Code=-999 "cancelled" UserInfo={NSErrorFailingURLStringKey=https://maps.googleapis.com/maps/api/place/autocomplete/json?key={apiKey}&sessiontoken=BC3ED61C-AA05-4F3B-8F5E-B94AB5380CA5&input=texa, NSErrorFailingURLKey=https://maps.googleapis.com/maps/api/place/autocomplete/json?key={apiKey}&sessiontoken=BC3ED61C-AA05-4F3B-8F5E-B94AB5380CA5&input=texa, _NSURLErrorRelatedURLSessionTaskErrorKey=( "LocalDataTask <61FD3877-12B9-4294-94F8-5FFE135978D2>.<4>" ), _NSURLErrorFailingURLSessionTaskErrorKey=LocalDataTask <61FD3877-12B9-4294-94F8-5FFE135978D2>.<4>, NSLocalizedDescription=cancelled}}}}}
看到这个错误,"Something went wrong",很头疼,完全不知道问题出在哪儿。 别急,下面咱们就来一步步排查,找出原因并解决它。
一、问题根源分析
出现这种"笼统"的错误,可能有好几个原因,咱们按可能性从大到小梳理一下:
- API Key 问题 : 最常见的情况,API Key 没配置对,或者被禁用了。
- 网络问题 : 用户的设备没网,或者网络环境不稳定。
- 请求参数错误 : 比如,必填参数没填,或者格式不对。
- API 配额超限 : 免费配额用完了,或者请求太频繁。
- iOS Bundle ID 限制 : API Key 开启了 Bundle ID 限制, 但是输入了错误的ID。
- 代码逻辑问题 : 请求被取消,或代码实现存在Bug。
- Google 服务端问题 : 虽然不常见,但 Google 服务端偶尔也会抽风。
从给出的错误日志看,NSURLErrorDomain Code=-999 "cancelled"
,请求被取消了,这个信息有点价值。这说明问题可能出在网络层面,客户端取消,或代码层面。
二、问题排查与解决方案
知道了可能的原因,我们就逐一排查。
1. 检查 API Key 和启用状态
这是最容易忽视,但最容易解决的问题。
- 原理: Google Maps Platform 的 API 需要有效的 API Key 才能正常使用。
- 步骤:
- 登录 Google Cloud Console (console.cloud.google.com)。
- 找到你的项目。
- 在 "API 和服务" 中找到 "凭据"。
- 检查你使用的 API Key 是否存在,并且状态是 "有效"。
- 检查 API Key 是否启用了 "Places API"。如果没有, 开启。
2. 验证 API Key 限制 (Application Restrictions)
确保你的 iOS 应用的 Bundle Identifier 已正确配置到 API Key 的限制列表中。
- 原理: 为了安全,你可以限制哪些应用可以使用你的 API Key。
- 步骤:
- 在 Google Cloud Console 的 "凭据" 页面,找到你的 API Key,点击编辑。
- 在 "应用限制" (Application restrictions) 部分,选择 "iOS apps"。
- 在列表中检查是否有你的应用的 Bundle Identifier,并且完全匹配。
- 如果没有或者不匹配,添加或修改它。 记住要保存。
3. 网络连接检查
确保设备能够正常访问互联网。
-
原理: Places API 请求需要通过网络发送到 Google 服务器。
-
步骤:
- 打开浏览器,试试能不能访问 google.com。
- 可以在代码里使用
NetworkReachability
(Swift) 或SCNetworkReachability
(Objective-C) 来检测网络状态。 - Swift 示例:
import SystemConfiguration func isNetworkAvailable() -> Bool { var zeroAddress = sockaddr_in() zeroAddress.sin_len = UInt8(MemoryLayout.size(ofValue: zeroAddress)) zeroAddress.sin_family = sa_family_t(AF_INET) let defaultRouteReachability = withUnsafePointer(to: &zeroAddress) { $0.withMemoryRebound(to: sockaddr.self, capacity: 1) {zeroSockAddress in SCNetworkReachabilityCreateWithAddress(nil, zeroSockAddress) } } var flags = SCNetworkReachabilityFlags() if !SCNetworkReachabilityGetFlags(defaultRouteReachability!, &flags) { return false } let isReachable = (flags.rawValue & UInt32(kSCNetworkFlagsReachable)) != 0 let needsConnection = (flags.rawValue & UInt32(kSCNetworkFlagsConnectionRequired)) != 0 return (isReachable && !needsConnection) }
4. 检查请求参数
仔细检查你的请求参数,确保它们符合 API 文档的要求。
- 原理: API 有自己的格式和数据要求。
- 检查内容:
input
: 必填,用户输入的文本。key
: 必填,你的 API Key。sessiontoken
: 建议使用,用于标识一个自动完成会话,节省费用。- 其他可选参数,比如
location
,radius
,language
等,根据需求检查是否正确。
- 示例URL(GET 请求):
https://maps.googleapis.com/maps/api/place/autocomplete/json?input=Paris&key=YOUR_API_KEY&sessiontoken=UNIQUE_SESSION_TOKEN
5. 检查 API 配额和计费
虽然不太可能直接导致 "cancelled" 错误,但还是值得检查一下。
- 原理: Google Maps Platform 有免费配额,超过配额需要付费。
- 步骤:
- 在 Google Cloud Console,进入 "API 和服务" -> "信息中心"。
- 找到 "Places API",查看 "配额" 部分。
- 确保已经设置好了 账单信息(billing)。
6. 仔细检查代码逻辑: 请求取消的排查
结合 NSURLErrorDomain Code=-999 "cancelled"
这个错误信息,着重排查代码逻辑中可能导致请求被取消的地方。
-
原理: 有时候,代码中的某些操作可能会意外地取消网络请求。
-
排查要点:
- 重复请求: 是否在短时间内发起了多个相同的请求? 可能是 UI 上的问题,导致用户多次触发了请求。
URLSessionTask
管理: 如果你手动管理URLSessionTask
,检查是否在不恰当的时候调用了cancel()
方法。比如: 在View 销毁时取消,在 TableView/CollectionView Cell 重用时取消等。- 异步操作 : 检查在进行网络请求的异步操作中,是否有提前返回或者退出导致请求被取消的逻辑。
- 第三方库 : 如果你使用了第三方网络库(比如 Alamofire, Moya),查阅其文档,了解如何处理请求取消,以及是否有相关的配置项。
-
示例(Swift 使用
URLSession
):
//推荐把 task 作为变量存储, 以备手动取消等操作
var placesTask: URLSessionDataTask?
func fetchPlaces(input: String, sessionToken: String) {
//取消之前的请求 (如果有)
placesTask?.cancel()
guard let url = URL(string: "https://maps.googleapis.com/maps/api/place/autocomplete/json?input=\(input)&key=YOUR_API_KEY&sessiontoken=\(sessionToken)") else {
return
}
placesTask = URLSession.shared.dataTask(with: url) { (data, response, error) in
// ... 处理结果 ...
if let error = error as NSError?, error.code == NSURLErrorCancelled {
// 请求被取消, 通常不需要特别处理, 但要排除代码问题.
print("Request cancelled")
return;
}
// 处理成功和失败情况 ...
}
placesTask?.resume()
}
进阶:Debouncer/Throttler 防抖
在自动完成场景下, 用户输入很快,会产生大量请求,使用 Debouncer 或 Throttler 可以有效减少请求次数。
-
原理:
- Debouncer (防抖): 一段时间内,只响应最后一次操作。 比如设定500ms, 用户连续输入,只在停止输入500ms后才发起请求。
- Throttler (节流): 一段时间内,只响应第一次操作。
-
示例(Swift, 简单的Debouncer):
import Foundation
class Debouncer {
private var workItem: DispatchWorkItem?
private let queue: DispatchQueue
private let delay: TimeInterval
init(delay: TimeInterval, queue: DispatchQueue = .main) {
self.delay = delay
self.queue = queue
}
func debounce(action: @escaping () -> Void) {
workItem?.cancel()
let newWorkItem = DispatchWorkItem { action() }
workItem = newWorkItem
queue.asyncAfter(deadline: .now() + delay, execute: newWorkItem)
}
}
//用法:
let debouncer = Debouncer(delay: 0.5) // 0.5 秒
func textFieldDidChange(_ textField: UITextField) {
debouncer.debounce {
// 获取 textField.text, 发起 Place Autocomplete 请求
self.fetchPlaces(input: textField.text ?? "", sessionToken: "...")
}
}
7. Google 服务端问题
如果以上都排除了,那可能是 Google 服务端的问题。
- 原理: 服务端问题我们没办法直接解决。
- 怎么办:
- 查看 Google Maps Platform 的状态页面 (status.cloud.google.com)。
- 稍等片刻,或者几个小时后再试。
- 在 Google Maps Platform 的 Issue Tracker 里搜索或报告问题。
通过以上步骤的排查和处理,基本可以解决 iOS 应用中 Google Places 自动完成 API 请求失败的问题了. 请根据你的情况逐一排查, 早日搞定这个问题!