我目前正在开发一个需要在Swift中使用Google Calendar API的应用程序。不幸的是,项目的进展处于停滞状态,因为我无法确定我需要如何处理才能超越标题中显示的错误。它出现在执行谷歌登录功能后。绕过firebase客户端ID以支持通过Google Developer Console生成的另一个ID也似乎无法解决问题。在代码中的多个场景中,我根据另一个stackoverflow用户的解决方案添加了范围值以及向范围添加“https://www.googleapis.com/auth/userinfo.profile”,但没有一个解决了我的问题。我有一个Google-Info.plist文件,该文件已经正确配置了反向ID和url方案,并且还尝试使用该plist文件中的客户端ID来执行该功能,但它似乎仍然没有工作。下面我已经包含了我的程序的两个主要部分的代码,其中包括对日历api或授权函数的引用。如果您看到任何可能被错误实施的内容,或者我错过了让它发挥作用的一步,请告诉我。我的大部分代码都基于此处提供的快速入门代码:https://developers.google.com/google-apps/calendar/quickstart/ios?ver=swift
只需稍加改动即可额外实施Firebase。注意:登录确实可以正常工作,并且正在成功地将数据传送到firebase,一旦调用Google日历,它就不会允许进一步的进展。根据我在下面提供的内容,还有一个单独的viewcontroller,其中包含google登录按钮,但我不相信我在该类中存在的问题,因此将其排除在外。
APPDELEGATE
import UIKit
import Firebase
import GoogleSignIn
import FirebaseDatabase
import GoogleAPIClientForREST
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, GIDSignInDelegate {
var scopes = [kGTLRAuthScopeCalendarReadonly, "https://www.googleapis.com/auth/userinfo.profile"]
var window: UIWindow?
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
GIDSignIn.sharedInstance().clientID = FirebaseApp.app()?.options.clientID
GIDSignIn.sharedInstance().delegate = self
GIDSignIn.sharedInstance().scopes = scopes
window = UIWindow(frame: UIScreen.main.bounds)
window?.rootViewController = UINavigationController(rootViewController: ViewController())
window?.makeKeyAndVisible()
return true
}
func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
if let err = error {
print("Failed to log into Google: ", err)
return
}
print("Successfully logged into Google", user)
//Allows information to be stored in Firebase Database
let ref = Database.database().reference(fromURL: "https://tesseractscheduler.firebaseio.com/")
guard let idToken = user.authentication.idToken else { return }
guard let accessToken = user.authentication.accessToken else { return }
LoginController().service.authorizer = user.authentication.fetcherAuthorizer()
let credentials = GoogleAuthProvider.credential(withIDToken: idToken, accessToken: accessToken)
Auth.auth().signIn(with: credentials, completion: { (user, error) in
if let err = error {
print("Failed to create a Firebase User with Google account: ", err)
return
}
//successfully authenticated user
guard let uid = user?.uid else { return }
print("Successfully logged into Firebase with Google", uid)
//Creates database entry with users unique identifier, username, and email. "null" provided to prevent errors.
ref.updateChildValues(["UID": uid, "username": user?.displayName ?? "null", "Email": user?.email ?? "null"])
//Pushes to next screen if login has occurred.
if GIDSignIn.sharedInstance().hasAuthInKeychain(){
//GIDSignIn.sharedInstance().clientID = "197204473417-56pqo0dhn8v2nf5v64aj7o64ui414rv7.apps.googleusercontent.com"
self.window?.rootViewController = UINavigationController(rootViewController: LoginController())
}
LoginController().fetchEvents()
})
}
func application(_ application: UIApplication, open url: URL, options:
[UIApplicationOpenURLOptionsKey : Any] = [:]) -> Bool {
GIDSignIn.sharedInstance().handle(url,
sourceApplication: options[
UIApplicationOpenURLOptionsKey.sourceApplication] as! String!, annotation : options[UIApplicationOpenURLOptionsKey.annotation])
return true
}
LOGINCONTROLLER
import UIKit
import GoogleSignIn
import Firebase
import GoogleAPIClientForREST
class LoginController: UIViewController {
let service = GTLRCalendarService()
let output = UITextView()
let appDelegate = (UIApplication.shared.delegate as? AppDelegate)
override func viewDidLoad() {
super.viewDidLoad()
navigationItem.leftBarButtonItem = UIBarButtonItem(title: "Logout", style: .plain, target: self, action: #selector(didTapSignOut))
view.backgroundColor = UIColor(red: 61/255, green: 91/255, blue: 151/255, alpha: 1)
output.frame = view.bounds
output.isEditable = false
output.contentInset = UIEdgeInsets(top: 20, left: 0, bottom: 20, right: 0)
output.autoresizingMask = [.flexibleHeight, .flexibleWidth]
output.isHidden = true
view.addSubview(output);
}
func fetchEvents() {
let query = GTLRCalendarQuery_EventsList.query(withCalendarId: "primary")
query.maxResults = 10
query.timeMin = GTLRDateTime(date: Date())
query.singleEvents = true
query.orderBy = kGTLRCalendarOrderByStartTime
service.executeQuery(
query,
delegate: self,
didFinish: #selector(displayResultWithTicket(ticket:finishedWithObject:error:)))
}
@objc func displayResultWithTicket(
ticket: GTLRServiceTicket,
finishedWithObject response : GTLRCalendar_Events,
error : NSError?) {
if let error = error {
showAlert(title: "Error", message: error.localizedDescription)
return
}
var outputText = ""
if let events = response.items, !events.isEmpty {
for event in events {
let start = event.start!.dateTime ?? event.start!.date!
let startString = DateFormatter.localizedString(
from: start.date,
dateStyle: .short,
timeStyle: .short)
outputText += "\(startString) - \(event.summary!)\n"
}
} else {
outputText = "No upcoming events found."
}
output.text = outputText
}
private func presentViewController(alert: UIAlertController, animated flag: Bool, completion: (() -> Void)?) -> Void {
UIApplication.shared.keyWindow?.rootViewController?.present(alert, animated: flag, completion: completion)
}
//Shows an error alert with a specified output from the log.
func showAlert(title : String, message: String) {
let alert = UIAlertController(
title: title,
message: message,
preferredStyle: UIAlertControllerStyle.alert
)
let ok = UIAlertAction(
title: "OK",
style: UIAlertActionStyle.default,
handler: nil
)
alert.addAction(ok)
presentViewController(alert: alert, animated: true, completion: nil)
}
//Communicated to navigation button to perform logout function.
@objc func didTapSignOut(_ sender: AnyObject) {
GIDSignIn.sharedInstance().signOut()
print("Successfully logged out of Google.")
showAlert(title: "Log Off", message: "Logout Successful!")
appDelegate?.window?.rootViewController = UINavigationController(rootViewController: ViewController())
}
// Do any additional setup after loading the view.
}
如果您需要提供任何其他信息以帮助确定问题,请告知我们。我进行了相当广泛的搜索,无法为我的特定问题找到解决方案。如果这是一个愚蠢的问题,我深表歉意:我对API的合并仍然相当新。
我不能确定是否有更有效的解决方法,但是现在我已经能够通过剥离所有对Firebase的引用的代码并忽略它的实现来避免这个错误。有关我使用Firebase对Google登录进行身份验证的方式导致此错误发生。 Firebase移除后不再是问题。 El Tomato的问题让我尝试了这种方法,到目前为止,我至少能够取得进展。如果我有时间在项目中,我可以回过头来看看是否可以在没有错误发生的情况下实现它们,但是现在它是一个非重要的功能。