3
votes

I am rather new to Firebase/Firestore/Cloud functions and been trying a little project where a client app calls a Firebase Cloud Function to generate some random keys (random numbers), adds them to Firestore, and when successfully written, returns these keys to the client app. Kinda like a random number generator.

The function is called correctly by the client (according to Firebase Console), does generate the keys, checks if they exist in the Firestore, and if not adds them. All works up to the part where it should return the result to the client. Here the client never gets the result (the keys array). In fact, the callback in the client app (iOS/Swift) is never called.

I am suspecting the problem lies in the promise being returned? According to the Firebase documentation here, async callables should return a Promise although I am not entirely sure what I am doing is correct https://firebase.google.com/docs/functions/callable

Here is the code for the cloud function:

export const generateRandomKeys = functions.https.onCall(async (data, context) => {

  // Read data passed from client
  const numberOfKeys = data.numberOfKeys
  console.log("Number of keys to generate: ", numberOfKeys)

  // Generate some keys
  const generatedKeys = KeyMaker.newKeys(numberOfKeys)

  try {

    const randomkeys = []

    // Write keys to DB
    for (const key of generatedKeys) {
      const addedKey = await writeKeyToDB(key)
      randomkeys.push(addedKey)
    }

    return Promise.resolve(JSON.stringify(randomkeys))

  } catch (error) {
      console.log("Error has occured: ", error)
      throw new Error("An Error has occured: " + error)
  }
})


async function writeKeyToDB(key: string){

  try {
    // Check if a document with the same key already exists in the DB
    const docRef = db.collection("randomKeys").doc(key)
    const doc =  await docRef.get()

    // Document with same key found!
    if (doc.exists) {
      // Generate a new key and try again
      const newKey = KeyMaker.newKey()

      console.log("Will generate a new key and try again!. New key: ", newKey)
      await writeKeyToDB(newKey)
    }

    const keyDoc = {
      somefield: somevalue,     
    }

    // Write to DB then return result
    await docRef.set(keyDoc)

    return Promise.resolve(key)

  } catch (error) {
      return Promise.reject(error)
  }
}

Client (Swift)

     public static func generateNewRandomNumbers(numberOfKeys: Int) {

        FirebaseApp.configure()

        let functions = Functions.functions(region: FIRESTORE_REGION)

        functions.httpsCallable("generateRandomKeys").call(["numberOfKeys": numberOfKeys]) { (result, error) in

            // Nothing here executes                                                    
            print("----------got reply---------")
            if let error = error as NSError? {
                if error.domain == FunctionsErrorDomain {
                    let code = FunctionsErrorCode(rawValue: error.code)
                    let message = error.localizedDescription
                    print("Error \(String(describing: code)): " + message)
                }
            }

            if let keys = (result?.data as? [String]) {
                dump(keys)
            }
        }
    }

2
If you're using async/await, you don't need to return a promise explicitly. The return value will automatically be wrapped in a promise. But I don't know if that's the problem here.Doug Stevenson
Thanks for the quick reply. I removed the promises and returned directly the values but didn't seem to make a difference. I have added to the OP the client code, it doesn't seem there is an issue there to meUMAD
@DougStevenson I now got the result to the client app finally, I can print it using dump(results?.data) which results inOptional(["838483","33411","94949"]) but I can't determine the type of results.data to read it. I tried [String] but that doesn't seem to be the case. What type would that be?UMAD

2 Answers

1
votes

Dont combine Async/Await and Promise. Async functions as itself returning Promise.

First change return of your cloud function to :

return JSON.stringify(randomkeys);

Also in writeKeyToDb change return to:

return key;

and catch part to:

throw Error(error);

I see also problem that in cloud function you calling your writeKeyToDb function with 2 parameters, but that function have only one. But that code probably is in progress

0
votes

Finally found the issue, thanks for Doug and Dominik for guiding me in the right direction. I removed the promises and returned directly the values but more importantly, I didn't need to convert the array to JSON. I came across HTTPSCallableResult documentation

I simply changed this

return JSON.stringify(randomkeys);

to

return randomkeys

and on the client, instead of

if let keys = (result?.data as? [String]) { dump(keys) }

I do

if let keys = (result?.data as? NSArray) { dump(keys) }