How to run a function after setupIntentConfirmation succeeds? - ios

I can successfully create and confirm a setupIntent. After the user enters his credit card details and presses the "confirm" button, his payment method is successfully attached to their stripe customer account. However, when the setupIntent is confirmed, I'd like to run a function on my client to send the user to a different screen.
This is where I check the completion of the setupIntentConfirmation. The print statement shows in the console but for some reason, my function is not running.
func onCompletion(status: STPPaymentHandlerActionStatus, si: STPSetupIntent?, error: NSError?) {
self.setupIntentStatus = status
self.error = error
if status == .succeeded {
print("succeeded")
setIsVerifiedToTrue()
}
}
Here is the function I am trying to run:
func setIsVerifiedToTrue() {
let ref = FirebaseReferenceManager.root.collection(FirebaseKeys.CollectionPath.requirements).document(Auth.auth().currentUser!.uid)
ref.updateData([FirebaseKeys.RequirementsFieldPath.verified : true]) { error in
if let error = error {
print("Error updating document: \(error)")
} else {
print("isVerified set to true")
}
}
}
any help would be very much appreciated! :)

Related

RevenueCat - Why am I getting: Error Domain=SKErrorDomain Code=2 "Cannot connect to iTunes Store"

I have two auto-renewing subscriptions, monthly and yearly (iOS). I can make a purchase when I use a new fresh sandbox user. Although I have to enter my password three times. Here's the flow:
Tap on a subscription
Enter Password
Promted to enter password again
get "cannot connect to iTunes Store" error
try again and enter password
purchase succeeds.
Moving on, so once it succeeds, I'm now subscribed and my UI is updated via a listener in appDelegate which posts a notification that I subscribe to. But when I try to switch subscriptions, from monthly to yearly, or vice versa, it always gives me the "Cannot connect to iTunes Store" error. No UI updates. Here's the flow:
Tap on the other subscription
Prompted to enter iTunes password
Receive the "confirm purchase" dialog which states I'm modifying my subscription
Tap continue
Receive the "You're all set" success alert.
Dismiss alert
Receive "Cannot connect to iTunes Store" error
My listener wasn't called, UI wasn't updated, etc.
But then if I dismiss the error and tap the subscription again, I get an alert that says I'm already subscribed to that plan, even though the error was thrown and my listener didn't pick up the change.
I'm using firebase. I followed the quickstart and the Firebase specific instructions in the RevenueCat docs. All of my debug logs seem to be fine, no non-200 statuses, no invalid product Ids. Here are some code snippets:
AppDelegate:
Purchases.debugLogsEnabled = true
Purchases.configure(withAPIKey: Constants.revenueCatKey)
Purchases.shared.delegate = self
FirebaseApp.configure()
authHandle = Auth.auth().addStateDidChangeListener() { (auth, user) in
if let uid = user?.uid {
Purchases.shared.identify(uid, { (info, error) in
if let e = error {
print("sign in error: \(e.localizedDescription)")
} else {
print("User \(uid) signed in")
}
})
}
...
}
}
extension AppDelegate: PurchasesDelegate {
func purchases(_ purchases: Purchases, didReceiveUpdated purchaserInfo: PurchaserInfo) {
if self.currentUser != nil {
if purchaserInfo.activeSubscriptions.contains(Constants.audiomeshSubscriptions.monthly) {
guard let myRef = DataService.instance.REF_PRIVATE else { return }
myRef.updateData(["plan" : "paidMonthly"]) { err in
if let err = err {
print("error updating user-private with new subscription: \(err)")
} else {
NotificationCenter.default.post(name: Notification.Name(rawValue: "purchaserInfoUpdated"), object: nil)
}
}
}
else if purchaserInfo.activeSubscriptions.contains(Constants.audiomeshSubscriptions.yearly) {
//do the same for yearly subscription
}
else {
//handle non-paying users here
}
}
}
}
UpgradeController (the purchasing UI):
#objc func purchaseButtonSelected(sender: AudiomeshButton) {
let buttonTag = sender.tag
guard let option = options?[buttonTag] else { return }
let product:SKProduct = option.product
Purchases.shared.makePurchase(product, { (transaction, purchaserInfo, error) in
if let error = error {
print("error making purchase: \(error)")
} else {
print("Purchase Successful")
}
})
}
So this one is actually relatively easy to answer but the answer is rather unsatisfying.
Upgrades and crossgrades don't work in Sandbox.
This error is almost always returned in that case. The good news is that it works on production and RevenueCat tracks all the cases correctly.
So that's the generic NSError message that gets thrown for all SKErrors. Error code 2 is "payment cancelled". However, that's also the error that gets thrown when you're already subscribed to an item.
Are you sure you're letting the yearly subscription expire before you try to resubscribed? With annual subscriptions they will renew every hour, 6 times, before expiring.
To see the specific SKError, you'll do something like:
if let error = error as? SKError {
print("SKError - ")
switch error.code { // https://developer.apple.com/reference/storekit/skerror.code
case .unknown:
print("unknown error")
case .paymentCancelled:
print("cancelled error")
case .clientInvalid:
print("client invalid")
case .paymentInvalid:
print("payment invalid")
case .paymentNotAllowed:
print("payment not allowed")
case .cloudServiceNetworkConnectionFailed:
print("cloud service network connection failed")
case .cloudServicePermissionDenied:
print("cloud service permission denied")
case .storeProductNotAvailable:
print("store product not available")
case .cloudServiceRevoked:
print("cloud service revoked")
}
}
Once you know the SKError that's being returned I can update my answer if needed with more information on what could be going on.

Firebase Email Verification Redirect Url

I incorporated Firebase's email verification for my iOS mobile app and am trying to resolve the following issues:
The length of the redirect url appears extremely long. It looks like it repeats itself.
https://app.page.link?link=https://app.firebaseapp.com//auth/action?apiKey%3XXX%26mode%3DverifyEmail%26oobCode%3XXX%26continueUrl%3Dhttps://www.app.com/?verifyemail%253Demail#gmail.com%26lang%3Den&ibi=com.app.app&ifl=https://app.firebaseapp.com//auth/action?apiKey%3XXX%26mode%3DverifyEmail%26oobCode%3XXX%26continueUrl%3Dhttps://www.app.com/?verifyemail%253Demail#gmail.com%26lang%3Den
When I set handleCodeInApp equal to true, and am redirected back to the app when I click on the redirect url, the user's email is not verified. Whereas when I set it to false and go through Firebase's provided web widget, it does get verified. Wasn't able to find documentation that outlined handling the former in swift...
Any thoughts are appreciated.
func sendActivationEmail(_ user: User) {
let actionCodeSettings = ActionCodeSettings.init()
let redirectUrl = String(format: "https://www.app.com/?verifyemail=%#", user.email!)
actionCodeSettings.handleCodeInApp = true
actionCodeSettings.url = URL(string: redirectUrl)
actionCodeSettings.setIOSBundleID("com.app.app")
Auth.auth().currentUser?.sendEmailVerification(with: actionCodeSettings) { error in
guard error == nil else {
AlertController.showAlert(self, title: "Send Error", message: error!.localizedDescription)
return
}
}
}
Make sure you're verifying the oobCode that is part of the callback URL.
Auth.auth().applyActionCode(oobCode!, completion: { (err) in
if err == nil {
// reload the current user
}
})
Once you have done that, try reloading the the user's profile from the server after verifying the email.
Auth.auth().currentUser?.reload(completion: {
(error) in
if(Auth.auth().currentUser?.isEmailVerified)! {
print("email verified")
} else {
print("email NOT verified")
}
})

Swift - Firebase Transactions not completing before user signs out?

In my iOS app, I'm using Firebase Transactions to update scoreboards based on user-generated data to avoid mistakes with concurrent updates. I save data at three points:
when the user presses 'Sign Out'
when the user force quits the app (not working yet due to timeout issue)
when the day changes while the app is running
Below is the transaction code:
func saveDataToFirebase(signUserOut: Bool)
{
let countyRef = Database.database().reference().child("countyleaderboard")
countyRef.child(self.userCounty).runTransactionBlock({ (currentData: MutableData) in
var value = currentData.value as? Float
if value == nil
{
value = Float(0.00)
}
currentData.value = value! + self.currentSessionAlt
return TransactionResult.success(withValue: currentData)
}, andCompletionBlock: {
error, commited, snap in
if commited && signUserOut
{
do
{
print("transaction complete")
try Auth.auth().signOut()
}
catch let logoutError
{
print(logoutError)
}
}
})
}
The code reaches the sign out method, but the database is not being updated. I ran the code separately, without including user sign out and the transaction completes fine.
Why does the transaction not actually complete before signing the user out? And is there a way to fix this?

How do I run a Cloud Code on Heroku?

With the Parse's announcement of their retirement, I have migrated my Parse Server onto Heroku. With my still neophyte knowledge of Heroku, I do not know if they have a similar function to that of Cloud Code, but I do know that a few months ago Parse Introduced a Heroku + Parse feature that allows you to run Cloud Code on any node.js environment, particularly Heroku.
My dilemma is, I have already migrated my server from parse to Heroku prior to learning about this feature :/ , so I cannot run any parse cloud code form my terminal because there is no existing server there anymore. So the question is, how can I emulate this following Cloud Code in Heroku & How do I adjust my swift?
Cloud Code:
// Use Parse.Cloud.define to define as many cloud functions as you want.
// For example:
Parse.Cloud.define("isLoginRedundant", function(request, response) {
Parse.Cloud.useMasterKey();
var sessionQuery = new Parse.Query(Parse.Session);
sessionQuery.equalTo("user", request.user);
sessionQuery.find().then(function(sessions) {
response.success( { isRedundant: sessions.length>1 } );
}, function(error) {
response.error(error);
});
});
and here is my swift back in xcode:
PFUser.logInWithUsernameInBackground(userName!, password: passWord!) {
(user, error) -> Void in
if (user != nil) {
// don't do the segue until we know it's unique login
// pass no params to the cloud in swift (not sure if [] is the way to say that)
PFCloud.callFunctionInBackground("isLoginRedundant", withParameters: [:]) {
(response: AnyObject?, error: NSError?) -> Void in
let dictionary = response as! [String:Bool]
var isRedundant : Bool
isRedundant = dictionary["isRedundant"]!
if (isRedundant) {
// I think you can adequately undo everything about the login by logging out
PFUser.logOutInBackgroundWithBlock() { (error: NSError?) -> Void in
// update the UI to say, login rejected because you're logged in elsewhere
// maybe do a segue here?
let redundantSession: String = "you are already logged in on another device"
self.failedMessage(redundantSession)
self.activityIND.stopAnimating()
self.loginSecond.userInteractionEnabled = true
}
} else {
// good login and non-redundant, do the segue
self.performSegueWithIdentifier("loginSuccess", sender: self)
}
}
} else {
// login failed for typical reasons, update the UI
dispatch_async(dispatch_get_main_queue()) {
self.activityIND.stopAnimating()
self.loginSecond.userInteractionEnabled = true
if let message = error?.userInfo["error"] as? String
where message == "invalid login parameters" {
let localizedMessage = NSLocalizedString(message, comment: "Something isn't right, check the username and password fields and try again")
print(localizedMessage)
self.failedMessage(localizedMessage)
}else if let secondMessage = error?.userInfo["error"] as? String
where secondMessage == "The Internet connection appears to be offline." {
self.failedMessage(secondMessage)
}
}
}
}
I would first checkout the example repo and read the parse-server documentation. Parse server supports cloud code out of the box and you simply specify which file contains your functions and triggers in the parse-server config. The link you posted with the integration between parse and heroku is not relevant for parse-server.

Messenger cannot connect to a XMPP server from the first attempt

I wrote 2 functions:
if let stream = xmppStream {
if stream.isAuthenticated(){
println("Logged In")
} else {
println("something is wrong")
}
}
func xmppStreamDidConnect(sender: XMPPStream) {
println("xmppStreamDidConnect")
isOpen = true
var error: NSError?
if (xmppStream!.authenticateWithPassword(password.text, error: &error) ) {
println("authentification successful")
performSegueWithIdentifier("goToBuddyList", sender: nil)
}
}
And when I run my app it prints in the terminal:
something is wrong
xmppStreamDidConnect
authentification successful
Even if I putted true credentials it prints at first [something is wrong] and just later [authentification successful]. Why it happens?
I want alert users just in case of [something is wrong], not in [successful] case, but it alert it in successful case, too.
Implement xmppStreamDidAuthenticate and didNotAuthenticate methods of XMPPStreamDelegate .
After calling authenticateWithPassword client authentication will surely end up in above two function (xmppStreamDidAuthenticate and didNotAuthenticate) until then stream.isAuthenticated() will return “0”.

Resources