Swift Firebase Delete An user when app will terminate - ios

Im trying to Delete user when app will terminate but this does not work.
If I have an anonymous user signed in and the user close the app I don't want firebase to save the anonymous user. I want to delete it. This is my current code. Thank you in advance
func applicationWillTerminate(_ application: UIApplication) {
// Called when the application is about to terminate. Save data if appropriate. See also applicationDidEnterBackground:.
if let users = Auth.auth().currentUser {
if !users.isAnonymous {
return
}else {
users.delete(completion: { (error) in
if error != nil {
print(error!)
return
}
try! Auth.auth().signOut()
print("Anonymous user deleted")
})
}
}
}

I added listerners in the ViewDidLoad to the screen where I want the user the be deleted from. Like this:
NotificationCenter.default.addObserver(self, selector: #selector(suspending), name: NSNotification.Name.UIApplicationWillResignActive, object: nil)
NotificationCenter.default.addObserver(self, selector: #selector(suspending), name: NSNotification.Name.UIApplicationWillTerminate, object: nil)
And then I have this function to delete the user:
#objc func suspending () {
if Auth.auth().currentUser != nil {
let user = Auth.auth().currentUser
user?.delete { error in
if let error = error {
print("An error happened delting the user.")
} else {
print("Account succesfully deleted.")
}
}
}
}
The only thing that could happen is, that you need to re-authenticate. If that's the case, than follow this answer to implement it as well

Hei #pprevalon, I've been trying to achieve the same thing as you, just by updating a value on appWillTerminate, but here's what I found out from other members
Your implementation of this method has approximately five seconds to perform any tasks and return. If the method does not return before time expires, the system may kill the process altogether.
Still trying to figure out a way, since it happens 1/10 times for the func appWillTerminate to be called, but I cannot count on that.
P.S. Besides that, think about this : If the user switches from active -> background at first, followed by background -> kill app, the method will never get called.

Related

How to implement screen lock in swift?

I am trying to implement app lock on my iOS app written with swift 5 and using storyboard to create UI. I need to implement feature such that when user enables screen lock in settings of my app , the app lock activates and without biometrics success user cannot view app content , else app should work normally.
I have already implemented the authentication code using the following tutorial.
And I have implemented the same in SceneDelegate as follows :
func sceneDidBecomeActive(_ scene: UIScene) {
// Called when the scene has moved from an inactive state to an active state.
// Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
debugPrint("Did become active")
self.biometricIDAuth.canEvaluate { (canEvaluate, _, canEvaluateError) in
guard canEvaluate else {
// Face ID/Touch ID may not be available or configured
return
}
self.biometricIDAuth.evaluate { [weak self] (success, error) in
guard success else {
// Face ID/Touch ID may not be configured
return
}
// You are successfully verified
debugPrint("Success biometric : \(success)")
}
}
}
where biometricIDAuth is class name. Currently the code only shows the authentication UI when entering foreground after several minutes in background . how to implement it such that every time the user comes into foreground the UI for the same shows and if possible how to blur the content when default authentication ui shows.
This is used for faceid or touch id
import LocalAuthentication
func imageTapped() {
// Your action
let context = LAContext()
var error: NSError?
if context.canEvaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, error: &error){
let reason = "Identify Yourself!."
context.evaluatePolicy(.deviceOwnerAuthenticationWithBiometrics, localizedReason: reason) { [weak self] (success,authError) in
DispatchQueue.main.async {
if success {
// This is success faceId succeed to identify user.
// do your work here
} else {
// biometric is avaliable but could not be verified.
// try to use custom password. or whatever you like.
}
}
}
} else {
// there is neither touch id or face id.
}
}
when user is going to background use notification center to pop to authenticationVC.
let notificationCenter = NotificationCenter.default
notificationCenter.addObserver(
self, selector: #selector(popToAuthVC),
name: UIApplication.willResignActiveNotification, object: nil
)
make sure your info.plist has this
source

How to detect if In-App purchases process is in progress?

Is it possible in Swift to detect if user is purchasing something? The process usually takes about 15-20 seconds and shows 3-4 different alerts(alert typing password for Apple ID, for confirming purchase, for information if it is successfully purchased or not etc.).
My problem is showing ads(OpenAd) whenever the app is about to become active, so it is really bad user experience to see ads when he tries to buy premium account to remove ads...
This is the part of code where I present them(AppDelegate):
func applicationDidBecomeActive(_ application: UIApplication) {
// Restart any tasks that were paused (or not yet started) while the application was inactive. If the application was previously in the background, optionally refresh the user interface.
tryToPresentAd()
}
And here are the methods that do the presenting(Also written in AppDelegate):
func tryToPresentAd() {
let ad: GADAppOpenAd? = self.appOpenAd
self.appOpenAd = nil
if ad != nil {
guard let rootController = self.window?.rootViewController else { return }
ad?.present(fromRootViewController: rootController)
} else {
requestAppOpenAd()
}
}
func requestAppOpenAd() {
self.appOpenAd = nil
GADAppOpenAd.load(withAdUnitID: Bundle.getValue(forKey: "xyzID"), request: GADRequest(), orientation: .portrait) { (ad, error) in
if error != nil {
debugPrint("Failed to load app open ad", error as Any)
} else {
self.appOpenAd = ad
}
}
}
Is there any way to detect it? Maybe like putting some kind of flags or something, or maybe Apple have some built-in way to detect it? Thanks.
Perhaps you could try to create a bool which would turn to true whenever an user decides to purchase something, and check if that bool is false in order to tryToPresentAd().

Firebase - iOS Swift: FIRAuth.auth().signOut() not signing out current user

I'm building an app using Firebase with an initial SignInViewController that loads a sign in page for users to authenticate with email which triggers the following methods:
#IBAction func didTapSignIn(sender: AnyObject) {
let email = emailField.text
let password = passwordField.text
FIRAuth.auth()?.signInWithEmail(email!, password: password!) { (user, error) in
if let error = error {
print(error.localizedDescription)
return
}
self.signedIn(user!)
}
}
func signedIn(user: FIRUser?) {
AppState.sharedInstance.displayName = user?.displayName ?? user?.email
AppState.sharedInstance.signedIn = true
NSNotificationCenter.defaultCenter().postNotificationName(Constants.NotificationKeys.SignedIn, object: nil, userInfo: nil)
performSegueWithIdentifier(Constants.Segues.SignInToHome, sender: nil)
}
The SignInViewController also checks if there is a cached current user when the app launches and, if so, signs that user in:
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(true)
//Synchronously gets the cached current user, or null if there is none.
if let user = FirebaseConfigManager.sharedInstance.currentUser {
self.signedIn(user)
}
}
Once the user is signed in, the app segues to a HomeScreenViewController which displays a "Sign Out" button at the top left of the navigation bar. When a user taps the "Sign Out" button, that user is supposed to get signed out and the app should segue back to the SignInViewController with the following method:
#IBAction func didTapSignOut(sender: UIBarButtonItem) {
print("sign out button tapped")
let firebaseAuth = FIRAuth.auth()
do {
try firebaseAuth?.signOut()
AppState.sharedInstance.signedIn = false
dismissViewControllerAnimated(true, completion: nil)
} catch let signOutError as NSError {
print ("Error signing out: \(signOutError)")
} catch {
print("Unknown error.")
}
}
When I tap the "Sign out" button, the didTapSignOut method gets called and gets executed.
However, after the try firebaseAuth?.signOut() line of code gets executed, the current user should be nil. But when I print out the current user in the Xcode console, the current user is still logged in:
po FIRAuth.auth()?.currentUser
▿ Optional<FIRUser>
- Some : <FIRUser: 0x7fde43540f50>
Since the current user doesn't get signed out after firebaseAuth?.signOut() gets called, once the app segues back to the SignInViewController the app still thinks there is a cached current user so that user gets signed in again.
Could this be a Keychain issue?
Does it have to do with NSNotificationCenter.defaultCenter().postNotificationName being called?
My code comes directly from the Google Firebase Swift Codelab so I'm not sure why it's not working:
https://codelabs.developers.google.com/codelabs/firebase-ios-swift/#4
You can add a listener in your viewDidAppear method of your view controller like so:
FIRAuth.auth()?.addStateDidChangeListener { auth, user in
if let user = user {
print("User is signed in.")
} else {
print("User is signed out.")
}
}
This allows you to execute code when the user's authentication state has changed. It allows you to listen for the event since the signOut method from Firebase does not have a completion handler.
GIDSignIn.sharedInstance().signOut()
Use exclamation points not question marks.
try! FIRAuth.auth()!.signOut()
I actually had this issue as well. I was also logging out the user (as you are) with the method's provided by Firebase but when I printed to the console it said that I still had a optional user.
I had to change the logic of setting the current user so that it is always configured by the authentication handler provided by Firebase:
var currentUser: User? = Auth.auth().currentUser
var handle: AuthStateDidChangeListenerHandle!
init() {
handle = Auth.auth().addStateDidChangeListener { (auth, user) in
self.currentUser = user
if user == nil {
UserDefaults.standard.setValue(false, forKey: UserDefaults.loggedIn)
} else {
UserDefaults.standard.setValue(true, forKey: UserDefaults.loggedIn)
}
}
}
As long as you are referencing the current user from this handle, it will update the current user no matter the authentication state.
Some answers are using a force unwrap when the firebase signing out method can throw an error. DO NOT DO THIS!
Instead the call should be done in a do - catch - block as shown below
do {
try Auth.auth().signOut()
} catch let error {
// handle error here
print("Error trying to sign out of Firebase: \(error.localizedDescription)")
}
You can then listen to the state change using Auth.auth().addStateDidChangeListener and handle accordingly.
I just had what I think is the same problem - Firebase + Swift 3 wouldn't trigger stateDidChangeListeners on logout, which left my app thinking the user was still logged in.
What ended up working for me was to save and reuse a single reference to the FIRAuth.auth() instance rather than calling FIRAuth.auth() each time.
Calling FIRAuth.auth()?.signOut() would not trigger stateDidChangeListeners that I had previously attached. But when I saved the variable and reused it in both methods, it worked as expected.

Stop Music When App is running in background (i.e. when home button is pressed)

Hi I build a basic game with swift that has background music. The game has multiple image views. I have the music playing with with the code below. How do I stop the music if the user exits out of the app using the home button. as it is now the music continues to play even when the user presses the home button. The background music continues through out all image views which is what i want.
func playBackgroundMusic(thesong: String) {
let url = NSBundle.mainBundle().URLForResource(thesong, withExtension: nil)
guard let newURL = url else {
print("Could not find file: \(thesong)")
return
}
do {
backgroundMusicPlayer = try AVAudioPlayer(contentsOfURL: newURL)
backgroundMusicPlayer.numberOfLoops = -1
backgroundMusicPlayer.prepareToPlay()
backgroundMusicPlayer.play()
} catch let error as NSError {
print(error.description)
}
}
playBackgroundMusic("thesong.mp3")
You can use an observer in your controller like this:
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("pauseSong:"), name:UIApplicationDidEnterBackgroundNotification, object: nil)
And call this function when your controller detect when your app is running in background:
func pauseSong(notification : NSNotification) {
backgroundMusicPlayer.pause()
}
Then, when your app is opened again, add another observer to play the song:
NSNotificationCenter.defaultCenter().addObserver(self, selector: Selector("playSong:"), name:UIApplicationWillEnterForegroundNotification, object: nil)
Then call this function:
func playSong(notification : NSNotification) {
backgroundMusicPlayer.play()
}
Hope it helps you ;)
You have to use this function in your controller:
func applicationDidEnterBackground(application: UIApplication) {
// Use this method to release shared resources, save user data, invalidate timers, and store enough application state information to restore your application to its current state in case it is terminated later.
backgroundMusicPlayer.pause()
}

NSUbiquityIdentityDidChangeNotification does not fire with cloud kit

This is my current code
func applicationDidBecameActive(notification:NSNotification) {
println("Application is active")
NSNotificationCenter.defaultCenter().addObserver(self, selector: "handleIdentityChange:", name: NSUbiquityIdentityDidChangeNotification, object: nil)
}
func applicationBecameInactive(notification:NSNotification) {
println("Application is inactive")
NSNotificationCenter.defaultCenter().removeObserver(self, name: NSUbiquityIdentityDidChangeNotification, object: nil)
}
func handleIdentityChange(notification:NSNotification) {
println("this is working")
let fileManager = NSFileManager()
if let token = fileManager.ubiquityIdentityToken {
println("New token is \(token)")
}
else {
println("User has logged out of iCloud")
}
}
"Application is active" & "Application is inactive" works properly. There is no problem there.
I could not get it fire "This is working". By logging into different iCloud account or logging out of iCloud account.
I tried on simulator & on actual device.
Please help me fix this or suggest alternative method to achieve same goal(change in iCloud account).
In Swift I have sometimes needed to add #objc in front of a func for NSNotificationCenter to find it.
So instead of
func handleIdentityChange(notification:NSNotification) {
I'd try
#objc func handleIdentityChange(notification:NSNotification) {
Have a look at my comment in this question. I never received a NSUbiquityIdentityDidChangeNotification either. In iOS your app gets killed when you change accounts. In tvOS you can use NSUbiquitousKeyValueStoreAccountChange.

Resources