SwiftUI结构体中调用Google Places API获取健身房信息的方法
解决SwiftUI中调用Google Places API填充地图标注的问题
问题概述
使用SwiftUI开发MapView时,测试数据vancouverPoints能正常展示地图标注,但通过getPlaces()调用Google Places API填充gymPoints的逻辑未完善,虽API密钥有效,却不清楚如何完成数据获取与解析、视图更新的流程。
解决方案步骤
1. 修正API请求方式
Google Places Nearby Search接口仅支持GET请求,原代码中设置的POST方式会导致请求失败,需调整请求配置:
- 将
request.httpMethod改为"GET" - 移除
Content-Type请求头(GET请求无需该设置)
2. 改用异步网络请求
同步网络请求会阻塞主线程,需使用Swift的async/await实现异步请求,将getPlaces()改为异步函数。
3. 动态管理标注数据
将gymPoints改为@State变量,这样数据更新时能自动触发视图刷新,替换原有的静态变量声明。
4. 解析API响应数据
创建对应的数据模型结构体,解析Google Places返回的JSON格式数据,将结果转换为AnnotatedItem数组。
修改后的完整代码
1. 新增Google Places响应模型
import Foundation import MapKit // 解析Google Places Nearby Search的响应模型 struct PlacesResponse: Codable { let results: [PlaceResult] } struct PlaceResult: Codable { let name: String let geometry: Geometry } struct Geometry: Codable { let location: Location } struct Location: Codable { let lat: Double let lng: Double } // 原有的AnnotatedItem保持不变 struct AnnotatedItem: Identifiable { let id = UUID() var name: String var coordinate: CLLocationCoordinate2D }
2. 更新MapView与异步请求逻辑
import SwiftUI import MapKit struct MapView: View { @EnvironmentObject var viewModel: LocationViewModel @State private var region = MKCoordinateRegion(center: CLLocationCoordinate2D(latitude: 49.250944, longitude: -122.759012), span: MKCoordinateSpan(latitudeDelta: 0.05, longitudeDelta: 0.05)) // 改为@State变量,支持动态更新 @State private var gymPoints: [AnnotatedItem] = [] private var vancouverPoints = [ AnnotatedItem(name: "Science park", coordinate: .init(latitude: 49.27477428248723, longitude: -123.1036925314458)), AnnotatedItem(name: "Stanely park", coordinate: .init(latitude: 49.30405290703585, longitude: -123.14429044749619)) ] var body: some View { // 切换为使用gymPoints作为标注数据源,加载完成前显示测试数据 Map(coordinateRegion: $region, annotationItems: gymPoints.isEmpty ? vancouverPoints : gymPoints) { item in MapMarker(coordinate: item.coordinate, tint: .red) } .edgesIgnoringSafeArea(.all) // 视图出现时异步加载数据 .task { gymPoints = await getPlaces() } } } // 改为异步函数,返回AnnotatedItem数组 func getPlaces() async -> [AnnotatedItem] { let longitudeX = "-122.735960" let latitudeY = "49.252400" let apiKey = "APP_KEY" // 替换为你的实际API密钥 guard let url = URL(string: "https://maps.googleapis.com/maps/api/place/nearbysearch/json?location=\(latitudeY),\(longitudeX)&radius=15000&types=gym&key=\(apiKey)") else { print("无效的API URL") return [] } do { let (data, _) = try await URLSession.shared.data(from: url) let response = try JSONDecoder().decode(PlacesResponse.self, from: data) // 将API结果转换为AnnotatedItem数组 return response.results.map { place in AnnotatedItem( name: place.name, coordinate: CLLocationCoordinate2D(latitude: place.geometry.location.lat, longitude: place.geometry.location.lng) ) } } catch { print("请求或解析失败:\(error.localizedDescription)") return [] } }
关键说明
- 异步请求:使用
async/await避免主线程阻塞,通过.task修饰符在视图初始化时触发数据加载 - 数据解析:通过Codable模型结构体自动解析JSON,确保类型安全
- 视图更新:
@State变量gymPoints更新时,Map视图会自动刷新显示新的标注 - API请求:严格遵循Google Places API的GET请求要求,避免请求方式错误导致的失败
内容的提问来源于stack exchange,提问作者Coding_Noob
相关产品推荐
相关产品推荐

