Firebase Cloud Functions will not fire from iOS app - swift

I'm building an iOS app using Firebase for the backend and need to use Firebase Callable Cloud Functions. I have everything set up as per docs and functions fire and behave as expected when launched from browser or curl but I cannot get them to fire from my app.
I've set up a test app just using the functions and I cannot get that to fire from iOS app either. Not even a basic hello world. Nothing is getting through.
All of the following has been done:
Firebase side:
Firebase is connected to app successfully
Installed/Initialised the Firebase Functions locally.
Written function code in VSCode trying both Javascript and TypeScript
Successfully deployed to Firebase and can see function in console
Tested function via command line and browser on local server all works as expected
iOS side:
Pod installed Firebase functions
imported Firebase functions into ViewController
Used Firebase SDK to invoke function matching name of function on Firebase - attached to button trigger
And I get nothing... the print statement on the button works but the function doesn't fire and nothing gets logged to Firebase logs.
What am I missing or doing wrong here?
Tried new project with new instance of Firebase.
Copied code directly from examples on Firebase docs and followed everything step by step
FIREBASE CODE
const functions = require('firebase-functions');
const admin = require('firebase-admin')
exports.helloWorld = functions.https.onCall((data, context) => {
const text = data.text;
console.log("Text: " + text);
const uid = context.uid;
console.log("UID: " + uid);
const name = context.name;
console.log("Name: " + name);
console.log('Hello world fucking worked baby!');
return {
message: text
}
});
SWIFT CODE
import UIKit
import FirebaseFunctions
class ViewController: UIViewController {
lazy var functions = Functions.functions()
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func fireFunction(_ sender: Any) {
print("Button Fire")
let data = ["text": "hello!"]
functions.httpsCallable("helloWorld").call(data) { (result, error) in
print("Function returned")
if let err = error {
print(err)
}
if let res = result {
print(res)
}
}
}
}

It seems that the Google Cloud Function is working properly if it is being called from a browser. You can also view the LOGs of the Cloud Function. Simply go to the Cloud Functions page and click on your function. Click on the VIEW LOGS button and try calling the URL again, if there is an error with the way Swift calls it, it will be logged there.
I have not worked with iOS Swift very much, but since you are trying to call the function via internet, I am thinking that you might need to give permissions to the App to access the Internet. It looks like the call is never initiated in the Cloud Function, that is why it is not triggered.
I assume that the Cloud Function is an HTTP triggered one. Therefore as a workaround, you can call the function initiating an HTTP request from the App. Again, this won't work though, unless you make sure that your App has the permissions to use the internet.

Related

Firebase OpenID Authentication redirects to about:blank

My App uses the Firebase Auth SDK (10.4.0) to authenticate users with OAuth. In my project settings I enabled OpenID Connect as a Sign-In-Method. I use an education institute named "IServ" as my openid auth provider. They have provided me with the ClientID, Issuer-URL and the Client Secret which I entered in the firebase console. I am able to launch the login form of my provider in a WebView with the following code:
let oAuthProvider = OAuthProvider(providerID: "oidc.iserv")
oAuthProvider.scopes = ["roles"]
oAuthProvider.getCredentialWith(nil) { credential, error in
print(credential, error)
}
After entering the login information in the login form of my provider and pressing continue the browser strangely redirects to a white webpage and after a few seconds it redirects to "about:blank".
I have two iOS Apps that are both connected to the same Firebase project. In one of the two apps the oauth login is working and the user gets redirected to the app (i.e. the WebView closes automatically). But in my other app the redirect is not working, although its the exact same code.
I can't retrieve any error in my Xcode console, nor in the WebView. Is this a common issue and has this something to do with the Xcode project setup?
I would really appreciate some help regarding this.
I had the same issues in a SwiftUI app, which I solved with the following approach.
Just before the about:blank is loaded, the app's callback onOpenURL is called (it's just the SwiftUI version of UIApplicationDelegate application:openURL:options).
In the callback, add the following code that triggers the authentication flow continuation:
.onOpenURL { url in
let authCanHandle = Auth.auth().canHandle(url)
if authCanHandle {
return
}
/* Here other dynamic link stuff */
}
This did the trick, for AppDelegate add the following code
func application(_ app: UIApplication, open url: URL, options: func application(_ app: UIApplication, open url: URL, options: [UIApplication.OpenURLOptionsKey : Any] = [:]) -> Bool {
let twitterhandeled = Auth.auth().canHandle(url)
return twitterhandeled
}

Flutter oAuth : how to get started with OAuth and Stripe connect

I am trying to implement stripe connect in my flutter app. Here are the steps I need to implement. Can anyone please navigate me on how I could achieve this in Flutter?
I am able to create a button with the endpointUrl but that's all..
Thanks
I found out this myself using firebase cloud functions:
first you create an https function in the firebase cloud function
then you add the link created by the function to your stripe dashboard
then you write the following logic to your function
obtain the the authorisation code
fetch data from stripe
save the response somewhere (in my case in realtime database)
Here is the function
exports.connectStripeStandardAccount = functions.https.onRequest((req, res) => {
let authCode = req.query.code;
return stripe.oauth.token({
grant_type: 'authorization_code',
code: authCode,
}).then(async response => {
await admin.database()
.ref(`/accounts/${authCode}`)
.set(response);
return res.send("Well done, account integration is completed. You can now close the window and go back to the app");
});
});
The answer selected is not completely correct:
If you dont assign the account_id to a user then it's of no use.
The only way to pass the user_id (fUser.uid) is to pass it using the state parameter.
exports.StripePI = functions.https.onRequest(async (req, res) => {
// console.log('accountIdq ' + req.query.error);
// console.log('accountIdq ' + req.query.state);
// return;
// if(!req.query.code)
// return res.send("An Error has occured please try again");
const response = await stripe.oauth.token({
grant_type: 'authorization_code',
code: req.query.code,
}).then(async response => {
var connected_account_id = response.stripe_user_id;
await admin.firestore().collection('Registration').doc(req.query.state)
.update({customer_id : connected_account_id});
return res.send("Well done, account integration is completed. You can now close the window and go back to the app");
});
});
If you want to create an in-app stripe connect account registration with flutter you will need these:
A server or service to complete the OAuth like Firebase Functions or Integromat (I used Integromat)
A link that will redirect to your app (I used Firebase Dynamic Link)
STEPS TO CREATE THE REGISTRATION FLOW
INTEGROMAT/FIREBASE FUNCTIONS SETUP
I decided to use Integromat instead of Firebase Functions because is easier to set up, doesn't need any code, and decreases my server load.
If you want to create it on Firebase Functions you will need to have a Blaze Plan
If you don't know it, Integromat will automate processes that you currently handle manually, via webhooks. It is not only capable of connecting apps (like GoogleCloud, Facebook, AWS...) but can also transfer and transform data.
Create a new scenario and add a Custom Webhook. Click on it and click on add, name it, and save it. It will now create a custom link to your webhook.
Close and click on the semi-sphere next to the webhook, to add the new module.
Select HTTP and Make a Request.
In the URL section insert https://connect.stripe.com/oauth/token.
Method POST.
Body Type Application/x-www-form-urlencoded.
Create now those fields :
Key client_secret - value your stripe client secret You can find it on your stripe dashboard. I advise you to first use the test mode and after that, change the value to the live key.
Key grant_type - value authorization_code
Key code - leave the value blank. We will add it later.
Save and close
For Firebase Functions you can create a new HTTPS function (I didn't test this)
var stripe = require("stripe")(*your stripe client secret*);
exports.connectStripeStandardAccount = functions.https.onRequest((req, res) =>{
let authCode = req.query.code;
return stripe.oauth.token({
grant_type: 'authorization_code',
code: authCode,
});
});
Remember to install stripe package npm install stripe
STRIPE SETUP
If you are in the test mode go to this link
If you are in the live mode go to this link
Go on the bottom and activate oAuth for standard accounts or for Express Account.
Click on Add URI and add the webhook link of Integromat that you created or the link related to your Firebase function.
If you used Firebase add this link https://us-central1-<project-id>.cloudfunctions.net/connectStripeStandardAccount
For Integromat you will need to create the structure. To do this click on Test OAuth, copy the link, and open it in incognito mode. Open your Integromat scenario and click on your webhook. Now click on Re-determine data structure.
Return to your stripe registration page and click on Ignore account form at the top.
Return on Integromat and select the HTTPS request, modify the field code, and insert the variable code (will open a dialog with all queries from the webhook). Confirm and save.
Now click on the play button and reopen the stripe registration link in incognito mode and click on Ignore account form. Return in Integromat and add a JSON module after the HTTPS request. In the JSON string insert the Data variable and save. Create a Webhook Response module after the JSON module.
In the status put 301, then click on Ok.
DEEP LINK SETUP
It's time to set up the redirect link that will return the user to our flutter app or on our website if the user hasn't it installed.
I used Firebase Dynamic Link You can follow this tutorial for set up.
Go to the dashboard and create a new Link prefix and a new dynamic link, remember to select to redirect your users to the right app.
Click on the three dots in your dynamic link row and click on Link Details. Copy the extended link.
Open Integromat and select the last module you created (Webhook Response). Click on Show advanced settings and on the Header add :
Key Location - value the extended dynamic link that you copied.
If you want your app to elaborate data from the stripe OAuth response you can modify the extended dynamic link by adding ? on the link parameter: link=https://test.page.link?stripe_user_id={{14.stripe_user_id}}
And select the variable parsed from the JSON module. Remember to click on the save icon to save your scenario.
On Firebase Functions you can do this when the function stripe.oauth.token finish (I didn't test it):
res.setHeader('Location', your dynamic link);
res.status(301).send();
Remember to deploy it.
FLUTTER APP SETUP
The code here is very simple. To initialize the connect account registration you only need to set up a button that will launch the stripe connect URL. You can use launch(url);
You can find that URL here. Remember to be logged in to your stripe account to get the right stripe client id. You can easily get it in the same section you added the webhook link in your stripe connect settings.
Delete &redirect_uri=https://sub2.example.com on the URL.
Now you can test your app and will see that when you complete your stripe connect registration/login you will be redirected to your app.
If you want to have an in-app web view you can use this package
To handle the response, you need to have installed the package firebase_dynamic_links
Set your Main widget Stateful and on the initState run the method getDynamic() :
void getDynamic() {
FirebaseDynamicLinks.instance.getInitialLink().then((value) {
if (value != null) {
_connect(value);
}
});
FirebaseDynamicLinks.instance.onLink(onSuccess: (value) async {
if (value != null) {
_connect(value);
}
}, onError: (error) async {
debugPrint('DynamicLinks onError $error');
});
}
void _connect(value) {
Uri deepLink = value.link;
print("Link :" + deepLink.path);
print("Query :" + deepLink.queryParameters.toString());
String stripeUserId = deepLink.queryParameters["stripe_user_id"];
}
You need to have both of them to handle dynamic links when your app is running and when it's closed.

Sinch: user does not receive the call for the first time

I implemented Sinch + PushKit + CallKit, everything works fine, but there is one script that does not work correctly. The first user uses the application and removes it from the device's memory, the second user calls for the first time, the first user does not receive the call, if the second user immediately calls the second time, the first user receives a call (later the first user also receives a call). If the first user opens the application (that is, becomes online for the system), the first user will not receive the call again. How can I fix it?
initialization of the sinch client
open func setup() {
guard sinch == nil else { return }
guard let userID = UserRealmManager().getUser()?.id else { return }
sinch = Sinch.client(withApplicationKey: key, applicationSecret: secret, environmentHost: host, userId: userID)
sinch?.delegate = self
sinch?.call().delegate = self
sinch?.setSupportCalling(true)
sinch?.enableManagedPushNotifications()
sinch?.setSupportPushNotifications(true)
sinch?.start()
sinch?.startListeningOnActiveConnection()
}
Update: I also found that if I restart the iPhone then calls through CallKit start to show up in 2-4 minutes, I decided to test it on the famous messangers such as What's app and Telegram and they have exactly the same behavior. Of course, I think it needs to be asked as an additional question.
My devices are iPhone 6 and 7.
This was my mistake, since I initialized SinchManager (this is the manager that manages SINClient) only in MainTabBarController viewDidLoad(), after I began to initialize it in AppDelegate didFinishLaunchingWithOptions, everything works fine.

Google places api error in ionic 2

I am using google places api in ionic app.I places google places api script in index.html. The app allow the user to access wifi within specific place. User get internet access after login with our app. When application launch and user is connecting with our network, user do not have access to the internet. So i get error:
Application Error connection to the server was unsuccessful.
Is there anyway to call the google places api after user has access to the internet to avoid the error?
This has been quickly tested in ionic 1 and it works, no reason it shouldn't in ionic 2. But if you know a proper way to add the script tag in angular, just replace this part. The logic is simply to add your resource on the fly to index.html. Don't forget that connexion can appear while app is already started, and that you don't need to add the tag twice (verifications have to be done). Some edits might be done to adapt to ionic 2, sorry my version was made on the 1.
the function to add the script:
function addScriptTag(){
//STORE HERE VALUE TO VERIFY SCRIPT HAS ALREADY BEEN ADDED
var script = document.createElement('script');
script.src = 'http://maps.google.com...';
script.type = 'text/javascript';
document.head.appendChild(script);
}
in $ionicPlatform.ready (network is not available before that)
//CONNECTED AT APP LOADING
var networkState = navigator.connection.type;
if(networkState !== Connection.NONE){
addScriptTag();
}
//IN CASE CONNEXION ARRIVES LATER
$rootScope.$on('$cordovaNetwork:online', function(event, networkState){
if( /* VERIFY SCRIPT NOT ADDED BEFORE */ ){
addScriptTag();
}
}, false);

getUser() Method on ApiAssistant

I'm trying to get the User_id from a request. Currently I am just using API.AI to build the skill before wiring in the actual Google Home API. When I use the following snippet, I get
"TypeError: Cannot read property 'data' of null
at Assistant.ApiAiAssistant.getUser"
const assistant = new ApiAiAssistant({request: request, response: response});
let userId = assistant.getUser().user_id;
Am I just unable to use the getUser() method when testing directly with API.AI? Do I need to fully deploy and test with the Home Simulator in order to make a call to getUser() without causing an error?
Thanks!