douglasrcjames
douglasrcjames

Reputation: 1665

Firebase function call return value in Swift

I am trying to access the token returned from the Firebase function twilioToken with Swift as I did with Redux JS. I attached my code I used for JS so I can mimic that with Swift, but not sure how to access the result.token from the firebase function call. Am I missing something here? Do I need to get the value from the https request differently or am I close with my current code? Let me know if I need to elaborate, thanks!

Current error on output.token is Value of tuple type 'Void' has no member 'token'.

Attempted Swift code:

import UIKit
import MBProgressHUD
import FirebaseFunctions

class CallRoomVC: UIViewController {
    private var appDelegate: AppDelegate!
    private var userSession: UserSession = FirebaseUserSession.shared

    lazy var functions = Functions.functions()

    override func viewDidLoad() {
        super.viewDidLoad()
        appDelegate = UIApplication.shared.delegate as? AppDelegate

    }

    override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)

        guard let user = userSession.user else {
            fatalError("User instance must be created")
        }

        var output = functions.httpsCallable("twilioToken").call(["uid": user.id]) { (result, error) in
            if let error = error as NSError? {
                if error.domain == FunctionsErrorDomain {
                    let code = FunctionsErrorCode(rawValue: error.code)
                    let message = error.localizedDescription
                    let details = error.userInfo[FunctionsErrorDetailsKey]
                }
                // ...
                // or
                // print("Result: \(result.token)")
            }

        }
        if (output != nil) {
            print("Result: \(output.token)")
        }
    }
}

Firebase Function:

"use strict";
const functions = require("firebase-functions");
const admin = require("firebase-admin");
// eslint-disable-next-line import/no-extraneous-dependencies
const google_cloud_logging = require("@google-cloud/logging");
const twilio = require("twilio");
const cors = require("cors")({
  origin: true
});
// eslint-disable-next-line import/no-extraneous-dependencies
admin.initializeApp(functions.config().firebase);

exports.twilioToken = functions.https.onRequest((req, res) => {
  return cors(req, res, () => {
    const token = new twilio.jwt.AccessToken(
      "xxxxxxxxxxxxxx", // Account ID
      "xxxxxxxxxxxxxx", // API Key SID
      "xxxxxxxxxxxxxx" // API Key Secret
    );
    token.identity = req.query.uid;
    token.addGrant(new twilio.jwt.AccessToken.VideoGrant());
    console.log("Sending token: ", token);
    res.status(200).send({ token: token.toJwt() });
  });
});

JS Redux code:

function* getTokenSaga(action) {
  const token = yield call(
    rsf.functions.call,
    "twilioToken",
    {
      uid: action.uid
    },
    {
      method: "GET"
    }
  );

  yield put(retrievedToken(token.token));
}

export function* twilioRootSaga() {
  yield all([takeEvery(types.TOKEN.GET, getTokenSaga)]);
}

Upvotes: 0

Views: 893

Answers (2)

douglasrcjames
douglasrcjames

Reputation: 1665

Phil Nash's explanation for the Swift side of things worked, but the issue lied in my Firebase function which I had to create a new function for based on the Twilio/Firebase Function API documentation:

exports.twilioTokenV2 = functions.https.onCall((data, context) => {
    const AccessToken = twilio.jwt.AccessToken;
    const VideoGrant = AccessToken.VideoGrant;
    const twilioAccountSid = functions.config().twilio_api.account_sid;
    const twilioApiKey = functions.config().twilio_api.key;
    const twilioApiSecret = functions.config().twilio_api.secret;
    // Grab uid passed in for identity
    const identity = data.uid;

    // Grab question ID passed in for room name
    const videoGrant = new VideoGrant({
      room: data.qid,
    });

    // Create an access token which we will sign and return to the client,
    // containing the grant we just created
    const token = new AccessToken(twilioAccountSid, twilioApiKey, twilioApiSecret);
    token.addGrant(videoGrant);
    token.identity = identity;

    console.log("Sending token: ", token);
    return {
      token: token.toJwt()
    }

});

Upvotes: 0

philnash
philnash

Reputation: 73075

Twilio developer evangelist here.

Your call to the Firebase function is asynchronous, as it is making an HTTP request. The result is not returned to your output variable, but it is available within the callback as the result object. You need to use that result instead, with something like this:

override func viewWillAppear(_ animated: Bool) {
    super.viewWillAppear(animated)

    guard let user = userSession.user else {
        fatalError("User instance must be created")
    }

    functions.httpsCallable("twilioToken").call(["uid": user.id]) { (result, error) in
        if let error = error as NSError? {
            if error.domain == FunctionsErrorDomain {
                let code = FunctionsErrorCode(rawValue: error.code)
                let message = error.localizedDescription
                let details = error.userInfo[FunctionsErrorDetailsKey]
            }
        }
        if let token = (result?.data as? [String: Any])?["token"] as? String {
            print("Result: \(token)")
        }
    }

}

This example was adapted from the Firebase documentation here.

Let me know if it helps at all.

Upvotes: 1

Related Questions