You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.25 18:12:37