3

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)
            }
        }
    }

3
  • 1
    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. Commented Apr 10, 2019 at 12:42
  • 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 me Commented Apr 10, 2019 at 13:08
  • @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? Commented Apr 11, 2019 at 12:56

2 Answers 2

1

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

Sign up to request clarification or add additional context in comments.

4 Comments

Did the changes but didn't help. Regarding the the customerID, my original code had a few more variables and I changed it a bit to simplify the code for posting here. So I forgot a couple of things here and there. But the original code works just the same. I posted the client code too. Any ideas why the async function might not be returning?
Well did you tried to exchange that return of cloud function to something static like return ['onekey','secondKey'] ?
You are exactly right. I tried to just return just a value and indeed it didn't return, so I took look again at the client code and I was mistakingly doing changes in the wrong swift class. So now, I do get the call back but now I don't know how to parse the result.data. When I do dump(result?.data) I can see the keys in XCode's console, but if let licenseKeys = (result?.data as? [String]) doesn't execute. Isn't the result an array of strings?
@UMAD create new ticket for that. Because its not bug of cloud function
0

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) }

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.