完成执行后如何返回“ GTLRSheetsQuery_SpreadsheetsValuesGet”查询的结果(使用Sheets API进行Swift处理)?

时间:2019-07-16 21:57:44

标签: ios swift google-sheets-api

我正在尝试使用Swift和Google Sheets API为iOS创建一个库存管理应用。在开始之前,我想熟悉API并确保一切正常。为此,我想创建一个getCell函数,该函数以硬编码的行和列返回单元格的内容(根据需要以A1表示法给出)。由于Swift中Sheets API的实现似乎需要回调模型(我一般是回调和异步编程的初学者),因此我还没有弄清楚如何在查询完成后返回函数中单元格的内容

我一直遵循Google(https://developers.google.com/sheets/quickstart/ios?ver=swift&hl=zh-cn)提供的Sheets API iOS快速入门,以在开发人员控制台中启用API并安装必要的Cocoapods(我的Podfile安装了“ GoogleAPIClientForREST / Sheets”和“ GoogleSignIn”)。将某些部分转换为较新的Swift语法后,我可以使包含的代码正常运行和运行。

当我在ViewController.swift文件中编写getCell函数时,出现了问题。如下面的代码所示,我假设查询将在getCellQuery完成时完成,以便getCell能够访问由回调函数(displayResultWithTicket)更新的全局变量。但是,我输入的调试打印语句表明,在getCell检查全局变量并没有发现新内容之后,回调函数仅在程序末尾执行。请注意,电子表格ID需要替换为真实的电子表格ID才能运行此代码。

import GoogleAPIClientForREST
import GoogleSignIn
import UIKit

struct MyVariables {
    static var currentCell: String? = nil
}

class ViewController: UIViewController, GIDSignInDelegate, GIDSignInUIDelegate {

    private let scopes = [kGTLRAuthScopeSheetsSpreadsheets]
    private let service = GTLRSheetsService()

    @IBOutlet weak var signInButton: GIDSignInButton!

    override func viewDidLoad() {
        super.viewDidLoad()

        GIDSignIn.sharedInstance().delegate = self
        GIDSignIn.sharedInstance().uiDelegate = self
        GIDSignIn.sharedInstance().scopes = scopes
    }

    func sign(_ signIn: GIDSignIn!, didSignInFor user: GIDGoogleUser!, withError error: Error!) {
        if let error = error {
            print("\(error.localizedDescription)")
            self.service.authorizer = nil
        } else {
            self.signInButton.isHidden = true
            self.service.authorizer = user.authentication.fetcherAuthorizer()

            //main program starts here
            print("Cell: " + getCell(a1: "Inventory!A1"))
        }
    }

    func getCell(a1: String) -> String {
        print("Started getCell")
        getCellQuery(a1: a1)
        if MyVariables.currentCell != nil {
            print("Finished getCell")
            return MyVariables.currentCell!
        } else {
            print("Error: the global variable is nil and probably hasn't been changed")
            print("Finished getCell")
            return ""
        }
    }

    func getCellQuery(a1: String) {
        print("Started getCellQuery")
        let spreadsheetId = "INSERT ID HERE"

        // arbitrary row and column in A1 notation
        let getRange = a1
        let getQuery = GTLRSheetsQuery_SpreadsheetsValuesGet.query(withSpreadsheetId: spreadsheetId, range:getRange)
        service.executeQuery(getQuery, delegate: self, didFinish: #selector(displayResultWithTicket(ticket:finishedWithObject:error:)))
        print("Finished getCellQuery")
    }

    @objc func displayResultWithTicket(ticket: GTLRServiceTicket,
                                       finishedWithObject result : GTLRSheets_ValueRange,
                                       error : NSError?) {
        print("Started callback")
        if let error = error {
            print("\(error.localizedDescription)")
            return
        }

        // turn 2d array into string
        let rows = result.values!
        for row in rows {
            for item in row {
                // update global variable
                MyVariables.currentCell = item as? String
            }
        }
        print("Finished callback")
    }
}

登录后,我希望控制台中显示以下内容:

Started getCell
Started getCellQuery
Started callback
Finished callback
Finished getCellQuery
Finished getCell
Cell: <Whatever the cell has>

相反,我得到了:

Started getCell
Started getCellQuery
Finished getCellQuery
Error: the cell value is nil and probably hasn't been changed
Finished getCell
Cell: 
Started callback
Finished callback

另外,我也尝试过使用while循环来等待全局变量被更新,但是看来Swift一直选择在整个程序结束时调用回调函数,这很烦人。 / p>

如果对于这个简单的任务没有那么笨拙的方法,那真是太好了。

1 个答案:

答案 0 :(得分:0)

您的问题似乎与异步调用模式有关。当您调用service.executeQuery时,它将开始一个异步查询,但是由于它正在访问Web服务,因此该查询可能需要很长时间才能完成。 executeQuery函数立即返回,查询本身可能已被其库排队。在下一次机会发送请求时,当回复返回时,将调用displayResultWithTicket(ticket:finishedWithObject:error :)回调,但与此同时,其余的getCellQuery函数也将完成,如getCell一样。

一个更好的模式是,既然您有响应(并在通知中传递回来的值),然后在回调中触发某个notification事件,然后让该通知的侦听器获取响应并对结果做适当的事情。

或者,Sheets API中可能有一种方法可以传递closure,这很像回调,但是它的代码位于传递它的函数中,因此状态管理要容易一些。我不熟悉该API,所以不确定该API是否存在。

这是Swift中managing async code上的一篇文章,其中涵盖了一些选项。虽然有点旧,但涵盖了基础知识。