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

如何在Swift版Xcode项目中接入Google Fit API并实现用户认证

在Swift(Xcode)项目中接入Google Fit API的自动认证与Token获取步骤

一、前置配置(Google Cloud与项目)

  • 打开Google Cloud控制台,选中目标项目,搜索并启用Google Fit API
  • 进入「API和服务」→「凭据」,创建「OAuth客户端ID」,选择iOS类型,填入项目的Bundle ID,完成后下载GoogleService-Info.plist文件
  • 将GoogleService-Info.plist拖入Xcode项目,确保勾选对应的项目目标(Target)

二、集成Google Sign-In SDK

方式1:CocoaPods

在Podfile中添加依赖:

pod 'GoogleSignIn'

执行pod install后,打开项目的.xcworkspace文件

方式2:Swift Package Manager

在Xcode中选择「File」→「Add Packages」,搜索官方GoogleSignIn包并添加到项目依赖

配置Info.plist

右键Info.plist→「Open As」→「Source Code」,添加以下配置($(REVERSED_CLIENT_ID)会自动从GoogleService-Info.plist读取对应值):

<key>CFBundleURLTypes</key>
<array>
  <dict>
    <key>CFBundleURLSchemes</key>
    <array>
      <string>$(REVERSED_CLIENT_ID)</string>
    </array>
  </dict>
</array>
<key>LSApplicationQueriesSchemes</key>
<array>
  <string>googlechrome</string>
  <string>googlechromes</string>
</array>

三、实现登录与AccessToken自动获取

1. 初始化Google Sign-In

在AppDelegate的启动方法中完成初始化:

import GoogleSignIn

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
    guard let clientID = Bundle.main.object(forInfoDictionaryKey: "CLIENT_ID") as? String else {
        fatalError("GoogleService-Info.plist中缺少CLIENT_ID配置")
    }
    GIDConfiguration.shared.clientID = clientID
    return true
}

如果使用SceneDelegate,需添加URL回调处理:

func scene(_ scene: UIScene, openURLContexts URLContexts: Set<UIOpenURLContext>) {
    guard let url = URLContexts.first?.url else { return }
    GIDSignIn.sharedInstance.handle(url)
}

2. 发起带Fit权限的登录请求

在登录按钮的点击事件中触发登录,请求对应的数据权限:

// 根据需求调整权限范围,比如心率读取、活动数据读取
let fitScopes = [
    "https://www.googleapis.com/auth/fitness.heart_rate.read",
    "https://www.googleapis.com/auth/fitness.activity.read"
]

GIDSignIn.sharedInstance.signIn(with: GIDConfiguration.shared, presenting: self) { user, error in
    if let error = error {
        print("登录失败:\(error.localizedDescription)")
        return
    }
    guard let user = user, let accessToken = user.accessToken?.tokenString else {
        print("未获取到用户或AccessToken")
        return
    }
    
    // 存储Token,用于后续API请求
    UserDefaults.standard.set(accessToken, forKey: "GoogleFitAccessToken")
    // 调用你的Google Fit API请求逻辑
    self.callFitAPI(with: accessToken)
}

3. 处理Token过期自动刷新

Google Sign-In SDK支持自动刷新Token,可在API请求返回401(未授权)时触发:

// 在API请求错误回调中处理401
if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 401 {
    GIDSignIn.sharedInstance.currentUser?.refreshTokensIfNeeded { refreshedUser, refreshError in
        if let newToken = refreshedUser?.accessToken?.tokenString {
            UserDefaults.standard.set(newToken, forKey: "GoogleFitAccessToken")
            self.callFitAPI(with: newToken)
        }
    }
}

四、替换原有API请求的Token配置

将你已有的API请求代码中的硬编码Token替换为自动获取的Token:

func callFitAPI(with token: String) {
    let url = URL(string: "https://fitness.googleapis.com/fitness/v1/users/me/dataSources/derived:com.google.heart_rate.bpm:com.google.android.gms:merge_heart_rate_bpm/datasets/1681711200000000000-1681887600000000000")
    guard let requestUrl = url else { fatalError() }

    var request = URLRequest(url: requestUrl)
    request.httpMethod = "GET"
    request.setValue("application/json", forHTTPHeaderField: "Content-Type")
    // 替换为自动获取的AccessToken
    request.setValue("Bearer \(token)", forHTTPHeaderField: "Authorization")

    let task = URLSession.shared.dataTask(with: request) { (data, response, error) in
        if let error = error {
            print("请求错误:\(error)")
            // 处理Token过期逻辑
            if let httpResponse = response as? HTTPURLResponse, httpResponse.statusCode == 401 {
                GIDSignIn.sharedInstance.currentUser?.refreshTokensIfNeeded { user, refreshError in
                    if let newToken = user?.accessToken?.tokenString {
                        self.callFitAPI(with: newToken)
                    }
                }
            }
            return
        }
        
        if let response = response as? HTTPURLResponse {
            print("响应状态码:\(response.statusCode)")
        }
        
        if let data = data, let dataString = String(data: data, encoding: .utf8) {
            print("响应数据:\n \(dataString)")
        }
    }
    task.resume()
}

关键注意事项

  • 权限范围需与业务需求匹配:不同的Fit数据类型对应不同的scope,需确保请求的权限已在Google Cloud控制台的OAuth consent screen中声明
  • 测试阶段需使用已添加到Google Cloud测试账号列表的邮箱,避免OAuth授权被拦截
  • 确保GoogleService-Info.plist中的CLIENT_ID、REVERSED_CLIENT_ID与控制台配置完全一致

内容的提问来源于stack exchange,提问作者DanLand

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.24 10:28:18