Automatically delete data from Firebase Database - swift

I have seen some other questions asked but I am having trouble getting it to work. I have a Mac app coded in swift and it has a Firebase login but the user types a key in that is stored on Firebase, is there a way to automatically delete that key when the user has successfully used it?
This is my database.
This is the code that is used currently.
import Cocoa
import FirebaseAuth
import FirebaseDatabase
class LoginViewController: NSViewController {
#IBOutlet weak var textUsername: NSTextField!
#IBOutlet weak var textPassword: NSSecureTextFieldCell!
#IBOutlet weak var btnLogin: NSButton!
var keyArray = \[Int64\]()
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewWillAppear() {
}
func getLoginState() -> Bool{
let state = UserDefaults.standard.bool(forKey: "isRegistered")
if (state) {
return true
} else {
return false
}
}
override func viewDidAppear() {
let state = self.getLoginState()
if (state){
self.performSegue(withIdentifier: NSStoryboardSegue.Identifier(rawValue: "loginsegue"), sender: nil)
self.view.window?.close()
}
var ref: DatabaseReference!
ref = Database.database().reference()
let keyRef = ref.child("key1")
keyRef.observe(DataEventType.childAdded, with: { (snapshot) in
// let postDict = snapshot.value as? \[String : AnyObject\] ?? \[:\]
let keyStr = snapshot.value as? Int64
if let actualPost = keyStr{
self.keyArray.append(actualPost)
}
})
}
#IBAction override func dismissViewController(_ viewController: NSViewController) {
dismiss(self)
}
#IBAction func close(sender: AnyObject) {
self.view.window?.close()
}
#IBAction func onSignup(_ sender: Any) {
// self.performSegue(withIdentifier: NSStoryboardSegue.Identifier(rawValue: "gotosignup"), sender: sender)
// self.view.window?.close()
}
func dialogOK(question: String, text: String) -> Void {
let alert: NSAlert = NSAlert()
alert.messageText = question
alert.informativeText = text
alert.alertStyle = NSAlert.Style.warning
alert.addButton(withTitle: "OK")
alert.runModal()
}
#IBAction func onLogin(_ sender: Any) {
//self.btnLogin.isEnabled = false
var isKey = false
if (!self.textUsername.stringValue.isEmpty) {
for key in keyArray{
if(Int64(self.textUsername.stringValue)! == key)
{
UserDefaults.standard.set(true, forKey:"isRegistered")
self.performSegue(withIdentifier: NSStoryboardSegue.Identifier(rawValue: "loginsegue"), sender: nil)
self.view.window?.close()
isKey = true
}
}
if (!isKey){
self.dialogOK(question: "Error", text: "Invalid Key")
}
} else {
self.dialogOK(question: "Error", text: "Please Input Key")
}
}
}

You can't sort your database like that and expect a working code, even if there's any. It will make a messy code:
You need to:
Sort your database like [1220:0]. the key first. 0 & 1 as an indicator if it's used or not.
Once the user taps onLogin() you need to set the used key value to 1
Setup Cloud Functions to check if the used key is equal to 1, if yes. then remove the key.
Do the rest of the work.
Related Articles to get you started:
Extend Realtime Database with Cloud Functions
functions.database.RefBuilder

Related

How do I access the variables that google provides outside of the function?

i'm trying to access the emailAddress variable in a different view controller however its always not in scope.
i want to call something like
login.emailAddress
in a different vc.
heres my code for your refeerence, i understand that there are similar questions however i struggle to translate that into my code. .
import UIKit
import GoogleSignIn
let login = LoginController()
class LoginController: UIViewController {
#IBOutlet weak var signInButton: GIDSignInButton!
let signInConfig = GIDConfiguration(clientID: "12345-abcdef.apps.googleusercontent.com")
#IBAction func signIn(_ sender: Any) {
GIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self) { user, error in
guard error == nil else { return }
guard let user = user else { return }
var emailAddress = user.profile?.email
var fullName = user.profile?.name
var givenName = user.profile?.givenName
var familyName = user.profile?.familyName
var profilePicUrl = user.profile?.imageURL(withDimension: 320)
let userProfile = (fullName, givenName, emailAddress, profilePicUrl)
print("Sign in Sucessfull")
print(fullName!)
print(givenName!)
print(familyName!)
print(emailAddress!)
print(profilePicUrl!)
// If sign in succeeded, display the app's main content View.
let vc = self.storyboard?.instantiateViewController(withIdentifier: "NavigationViewController") as! UINavigationController
self.navigationController?.pushViewController(vc, animated: true)
self.present(vc, animated: true, completion: nil)
}
}
}
An option would be storing the value in a class property:
class LoginController: UIViewController {
#IBOutlet weak var signInButton: GIDSignInButton!
private var userMail: String?
let signInConfig = GIDConfiguration(clientID: "12345-abcdef.apps.googleusercontent.com")
#IBAction func signIn(_ sender: Any) {
GIDSignIn.sharedInstance.signIn(with: signInConfig, presenting: self) { user, error in
guard error == nil else { return }
guard let user = user else { return }
self.userMail = user.profile?.email
[...]
}
}
}

UIStepper - start counting from 1

I have successfully implemented core data and UISteppers. Every time I try to edit a saved record the UI Stepper starts over from 0. Please help me to figure put what additional code I need to retain the already edited value.
// This function adds the stepper to a field
//issue: it does not remember the score when i edit it and starts over
#IBAction func counterStepperPressed(_ sender: UIStepper) {
counterTF.text = Int(sender.value).description
}
#IBAction func pointStepperPressed(_ sender: UIStepper) {
pointTF.text = Int(sender.value).description
}
#IBAction func savingsStepperPressed(_ sender: UIStepper) {
savingsTF.text = Int(sender.value).description
}
}
I have linked core data like so:
import CoreData
class AktieViewController: UIViewController {
#IBOutlet weak var counterStepper: UIStepper!
#IBOutlet weak var pointsStepper: UIStepper!
#IBOutlet weak var savingsStepper: UIStepper!
var selectedAktie: Aktie? = nil
override func viewDidLoad()
{
super.viewDidLoad()
if(selectedAktie != nil) {
savingsTF.text = selectedAktie?.saving
counterTF.text = selectedAktie?.counter
pointTF.text = selectedAktie?.point
}
}
#IBAction func saveAction(_ sender: Any) {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
let context: NSManagedObjectContext = appDelegate.persistentContainer.viewContext
if(selectedAktie == nil)
{
let entity = NSEntityDescription.entity(forEntityName: "Aktie", in: context)
let newAktie = Aktie (entity: entity!, insertInto: context)
newAktie.saving = savingsTF.text
newAktie.point = pointTF.text
newAktie.counter = counterTF.text
do {
try context.save()
aktieList.append(newAktie)
navigationController?.popViewController(animated: true)
}
catch
{
print("context save error")
}
}
I also have an edit and delete function.
This function eventually solved my question:
#IBAction func counterStepperPressed(_ sender: UIStepper) {
let initialValue=Int(counterTF.text) ?? 0
let newValue=Int(sender.value)+initialValue
counterTF.text="\(newValue)"
}
I have managed to add the following code to remember the value in the stepper.
if let value=UserDefaults.standard.value(forKey: "counterStepper") as? Double {
counterStepper.value=value counterTF.text=String(describing: value)
And in the action I have added the following code.
#IBAction func counterStepperPressed(_ sender: UIStepper) {
counterTF.text=String(describing: sender.value)
UserDefaults.standard.setValue(sender.value, forKey: "counterStepper")
NotificationCenter.default.post(Notification.init(name: Notification.Name("StepperDidChangeValue")))
}
The only issue I have is that if I edit a second item it remembers the value of the first item. Somehow it is not remembering the original value of the item.

RealmSwift currentUser cannot be called if more that one valid, logged-in user exists

I have a realm platform and I can register and login a user fine. When I try to open a Synced Realm file with the current User it will not allow it and Xcode throws the error currentUser cannot be called if more that one valid, logged-in user exists. I have followed all the information I can find on realms docs and I can't make sense as to why this is happening. I have a Login View Controller and a separate View Controller that contains the realm file. Here is example code of the problem I am dealing with.
LogInViewController
import Cocoa
import RealmSwift
class LoginViewController: NSViewController {
#IBOutlet weak var username: NSTextField!
#IBOutlet weak var password: NSSecureTextField!
let realm = try! Realm()
override func viewDidLoad() {
super.viewDidLoad()
}
#IBAction func loginButtonPressed(_ sender: NSButton) {
let login = SyncCredentials.usernamePassword(username: "\(username.stringValue)", password: "\(password.stringValue)", register: false)
SyncUser.logIn(with: login, server: Constants.AUTH_URL, onCompletion: { [weak self] (user, err) in
if let _ = user {
if let mainWC = self?.view.window?.windowController as? MainWindowController {
mainWC.segueToHome()
print("Login Pressed")
}
} else if let error = err {
fatalError(error.localizedDescription)
}
})
}
}
SecondViewController
import Cocoa
import RealmSwift
class ViewController: NSViewController {
#IBOutlet weak var textField: NSTextField!
override func viewDidLoad() {
super.viewDidLoad()
let config = SyncUser.current!.configuration(realmURL: Constants.REALM_URL, fullSynchronization: false, enableSSLValidation: true, urlPrefix: nil)
self.realm = try! Realm(configuration: config)
}
func save(data: Data) {
do {
try realm.write {
realm.add(data)
}
} catch {
print("there was an error saving data \(error)")
}
}
#IBAction func saveData(_ sender: NSButton) {
let saveData = Data()
saveData.textField = textField.stringValue
save(data: data)
}
func loadData() {
data = realm.objects(Data.self).sorted(byKeyPath: "timestamp", ascending: false)
}
}
Maybe, this page help you solve the problem.
I solved the same problem.
///
/// quit all other users session
///
for u in SyncUser.all {
u.value.logOut()
}
///
///
///
let creds: SyncCredentials = SyncCredentials.usernamePassword(
username: YOUR_USER,
password: YOUR_PASS
)
SyncUser.logIn(
with: creds,
server: YOUR_AUTH_URL
) { (user: SyncUser?, err: Error?) in
///
/// Your action ...
///
}
After logOut and logIn, this method will success probably.
let config = SyncUser.current!.configuration(realmURL: Constants.REALM_URL, fullSynchronization: false, enableSSLValidation: true, urlPrefix: nil)
self.realm = try! Realm(configuration: config)
If you are using Realm with Firebase, a snippet like this will save you from authentication headaches.
Check to see if the SyncUser uid matches the uid from firebase, and sign out extraneous accounts.
for syncUser in SyncUser.all {
if(syncUser.key != (FirebaseAuth.Auth.auth().currentUser?.uid ?? "")) {
syncUser.value.logOut()
}
}

UserDefaults checking for nil value not working

Any idea why my if statement always gives else answer?
I noticed from the print that the object is stored as "optional()", maybe this optional state is different from nil?
class ViewController: UIViewController {
#IBOutlet weak var phoneLabel: UITextField!
#IBOutlet weak var phoneDisplayLabel: UILabel!
#IBAction func storeButton(_ sender: Any) {
UserDefaults.standard.removeObject(forKey: "Nb")
UserDefaults.standard.set("\(phoneLabel.text!)", forKey: "Nb")
print(UserDefaults.standard.object(forKey: "Nb") as Any)
}
#IBAction func retrieveButton(_ sender: Any) {
let phoneNB = UserDefaults.standard.value(forKey: "Nb")
if let phoneNbDisplay = phoneNB as? String {
if UserDefaults.standard.object(forKey: "Nb") != nil {
phoneDisplayLabel.text = "Your NB is \(phoneNbDisplay)"
}
else {
phoneDisplayLabel.text = "enter a number first"
}
}
}
}
The recommended way (by Apple!) is to register key/value pairs in UserDefaults to provide – as the class name implies – default values. Those default values are considered until the value is changed the first time.
The Swift benefit is that there is always a (non-optional) value which can be unwrapped safely.
As soon as possible (e.g. applicationWillFinishLaunching) add these lines. They are supposed to be executed every time the application launches. The key Nb is registered with an empty string default value.
let defaultValues = ["Nb" : ""]
UserDefaults.standard.register(defaults: defaultValues)
In storeButton don't remove the key and don't use String Interpolation for a String but check if the text property is nil and save an empty string in this case. The print line is nonsensical because reading from UserDefaults right after writing will not get the changes and you know what's been written
#IBAction func storeButton(_ sender: Any) {
UserDefaults.standard.set(phoneLabel.text ?? "", forKey: "Nb")
}
In retrieveButton get the string with the dedicated method string(forKey, unwrap it (due to registering the key there is always a value) and only check for empty string. And don't read the (same) value 3 times.
#IBAction func retrieveButton(_ sender: Any) {
let phoneNbDisplay = UserDefaults.standard.string(forKey: "Nb")!
if phoneNbDisplay.isEmpty {
phoneDisplayLabel.text = "enter a number first"
} else {
phoneDisplayLabel.text = "Your NB is \(phoneNbDisplay)"
}
}
As vadian commented, you should not and have no need to use KVC method value(forKey:) in this case.
Try this:
#IBAction func storeButton(_ sender: Any) {
UserDefaults.standard.removeObject(forKey: "Nb")
UserDefaults.standard.set(phoneLabel.text ?? "", forKey: "Nb") //<-You have no need to use String Interpolation.
print (UserDefaults.standard.object(forKey: "Nb") as Any)
}
#IBAction func retrieveButton(_ sender: Any) {
if let phoneNB = UserDefaults.standard.string(forKey: "Nb") {
if !phoneNB.isEmpty {
phoneDisplayLabel.text = "Your NB is \(phoneNB)"
} else {
phoneDisplayLabel.text = "enter a number first"
}
} else {
//### What do you want to do if "Nb" is not set?
}
}
try this
if (UserDefaults.standard.value(forKey: "etc") != nil)
{
if (UserDefaults.standard.value(forKey: "etc") as! String).character.count == 0
{
//value is ""
}else{
//having some value
}
}
You might forget to call synchronize while storing value.You can try this.
#IBAction func storeButton(_ sender: Any) {
UserDefaults.standard.removeObject(forKey: "Nb")
UserDefaults.standard.set("\(phoneLabel.text!)", forKey: "Nb")
UserDefaults.standard.synchronize()
print (UserDefaults.standard.object(forKey: "Nb") as Any)
}
#IBAction func retrieveButton(_ sender: Any) {
if let phoneNB = UserDefaults.standard.string(forKey: "Nb"){
phoneDisplayLabel.text = "Your NB is \(phoneNB)"
}else {
phoneDisplayLabel.text = "enter a number first"
}
}

Custom FirebaseUI AuthViewController

Using the FirebaseUI Auth 1.0 version and swift 3.
Followed the sample and read the explains how to subclass the FUIAuthPickerViewController but everything I tried it's giving me a
fatal error: unexpectedly found nil while unwrapping an Optional valueonlet controller = self.authUI!.authViewController()
I exactly copied the example and there is it working, so can someone explain what I did wrong or why this is happening?
Code in my ViewController where I want the user to login
ViewController.swift
import Firebase
import FirebaseAuthUI
import FirebaseGoogleAuthUI
import FirebaseFacebookAuthUI
var ref: FIRDatabaseReference!
var remoteConfig: FIRRemoteConfig!
fileprivate var _refHandle: FIRDatabaseHandle!
fileprivate var _authHandle: FIRAuthStateDidChangeListenerHandle!
fileprivate(set) var auth: FIRAuth? = FIRAuth.auth()
fileprivate(set) var authUI: FUIAuth? = FUIAuth.defaultAuthUI()
fileprivate(set) var customAuthUIDelegate: FUIAuthDelegate = FUICustomAuthDelegate()
func configAuth() {
//listen for changes in auth state
_authHandle = FIRAuth.auth()?.addStateDidChangeListener({ (auth: FIRAuth, currentuser: FIRUser?) in
self.users.removeAll(keepingCapacity: false)
self.tableView.reloadData()
if currentuser != nil {
// User is signed in.
self.fetchUser()
} else {
// No user is signed in.
//self.login()
self.loginSession()
}
})
}
func loginSession() {
self.authUI?.delegate = self.customAuthUIDelegate
let googleAuth = FUIGoogleAuth(scopes: [kGoogleUserInfoEmailScope,
kGooglePlusMeScope,
kGoogleUserInfoProfileScope])
let facebookAuth = FUIFacebookAuth(permissions: ["public_profile",
"email",
"user_friends"])
self.authUI?.providers = [googleAuth, facebookAuth]
let controller = self.authUI!.authViewController()
self.present(controller, animated: true, completion: nil)
}
Here the
FUICustomAuthDelegate.swift
import UIKit
import FirebaseAuthUI
import FirebaseAuth
class FUICustomAuthDelegate: NSObject, FUIAuthDelegate {
func authUI(_ authUI: FUIAuth, didSignInWith user: FIRUser?, error: Error?) {
guard let authError = error else { return }
let errorCode = UInt((authError as NSError).code)
switch errorCode {
case FUIAuthErrorCode.userCancelledSignIn.rawValue:
print("User cancelled sign-in");
break
default:
let detailedError = (authError as NSError).userInfo[NSUnderlyingErrorKey] ?? authError
print("Login error: \((detailedError as! NSError).localizedDescription)");
}
}
func authPickerViewController(forAuthUI authUI: FUIAuth) -> FUIAuthPickerViewController {
return FUICustomAuthPickerViewController(authUI: authUI)
}
and the
FUICustomAuthPickerViewController.swift
import FirebaseAuthUI
#objc(FUICustomAuthPickerViewController)
class FUICustomAuthPickerViewController: FUIAuthPickerViewController {
#IBAction func onClose(_ sender: AnyObject) {
self.cancelAuthorization()
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}