How to create a search field on swift code by using firebase? - ios

I'm trying to do an iOS app with two auth level using firebase.
The second auth must be validate by insert on a text field a code witch is located in databse.
json root
//
// AccessoTerzoLivello.swift
// amesci
//
// Created by Gianluca Caliendo on 07/07/17.
// Copyright © 2017 Amesci. All rights reserved.
//
import UIKit
import Firebase
class AccessoTerzoLivello: UIViewController {
#IBOutlet weak var CodiceVolontarioTxt: UITextField!
#IBAction func Accedi(_ sender: UIButton) {
var rootRef: DatabaseReference!
rootRef = Database.database().reference()
var conditionalRef = rootRef.child("SchedaVolontari")
conditionalRef.observe(.value) {(snap: DataSnapshot) in }
if self.CodiceVolontarioTxt.text = snap
}
else {
let alert = UIAlertController(title: "Errore", message: "Codice volontario non valido", preferredStyle: UIAlertControllerStyle.alert)
alert.addAction(UIAlertAction(title: "ok", style: UIAlertActionStyle.default, handler: nil))
self.present(alert, animated: true, completion: nil)
}
}
}

You are on the right way. There is a problem with your line:
if self.CodiceVolontarioTxt.text = snap
Snap is a dictionary which holds your codes, you can't compare it to your text input. First of all you have to get all the codes from it.
Here is some code. I haven't tested this but it will give you an idea. Replace the if else statement in your completion block with this:
// This is empty array which will contain all the codes
var codesArray: NSMutableArray = []
// Get all the children from snapshot you got back from Firebase
let snapshotChildren = snap.children
// Loop over all children (code) in Firebase
while let child = snapshotChildren.nextObject() as? FIRDataSnapshot {
// Get code node key and save it to codes array
codes.add(child.key)
}
// Compare if inserted text matches any code from database
if codesArray.contains(self.CodiceVolontarioTxt.text) {
print("Code exists in Firebase")
} else {
print("Code does not exist in Firebase")
}

Related

How can I make a variable inside a function global?

I currently have the following function called saveRun()
func saveRun() {
let startLoc = locations[0]
let endLoc = locations[locations.count - 1]
let startLat = startLoc.coordinate.latitude
let startLong = startLoc.coordinate.longitude
let endLat = endLoc.coordinate.latitude
let endLong = endLoc.coordinate.longitude
//1. Create the alert controller
let alert = UIAlertController(title: "Save the Run", message: "Choose a name: ", preferredStyle: .alert)
//2. Add the text field
alert.addTextField { (textField) in
textField.text = ""
}
// 3. Grab the value from the text field, and print it when the user clicks OK
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: { [weak alert] (_) in
let textField = alert?.textFields![0] // Force unwrapping because we know it exists.
// Create name for run
let runName = textField?.text
let run = self.databaseRef.child(runName!)
let user = FIRAuth.auth()?.currentUser?.uid
// Enter run info into db
run.child("startLat").setValue(startLat)
run.child("startLong").setValue(startLong)
run.child("endLat").setValue(endLat)
run.child("endLong").setValue(endLong)
run.child("distance").setValue(self.distance)
run.child("time").setValue(self.seconds)
run.child("user").setValue(user)
// Enter locations into db
var i = 0
for location in self.locations {
run.child("locations").child("\(i)").child("lat").setValue(location.coordinate.latitude)
run.child("locations").child("\(i)").child("long").setValue(location.coordinate.longitude)
i = i + 1
self.performSegue(withIdentifier: DetailSegueName, sender: nil)
}
}))
// 4. Present the alert
self.present(alert, animated: true, completion: nil)
}
My problem is that I am trying to extract 'runName' from the action that I am adding when the user clicks 'Ok' on the alert controller and using it in the following function:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let detailViewController = segue.destination as? DetailViewController {
detailViewController.runName = self.runName
}
}
When I try to print 'runName' in DetailViewController, the value of runName is nil. The issue I think is that I cannot set a global variable inside the action I have added as it is in a function. Is there any other way I can obtain this variable's value and use it outside of the function?
Class YourClassName:UIViewController {
var runName:String = "" // This will be global for your class
//Remove local decalration of runName variable
func saveRun() { // your function
alert.addAction(
//.....
self.runName = textfield?.text
)
}
}
Now you can use in whole class.
I solved this thanks to #DSDharma pointing out that even if 'runName' was set as a global variable, using it as a global variable inside of an alert function block required the 'self' keyword.
For example, before I had the following inside of the alert function block:
let runName = textField?.text
This needed to be changed to:
self.runName = textField?.text

Referencing IBOutlet in another View Controller

So, I have been having some major trouble figuring this out and I have searched extensively for a solution but I surprisingly could not find one. I am attempting to create a multiple page (5, to be exact) Sign-Up for users.
I'll start off by showing you the layout of page 1 and 5 (since solving that issue will solve the issue for page 2-4):
Sign Up Page #1
Sign Up Page #5
As you may see (from the page control dots), I am using a page view controller to allow users to scroll from page to page. What I am trying to accomplish is giving the user the ability to enter their sign-up information in pages 1-5 before submitting it all at once (which can be located on page 5).
Here is the current code I am using for page #1:
class SignUpInfoViewController: UIViewController {
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
}
Here is the current code I am using for page #5:
class TermsOfUseViewController: UIViewController {
let minPasswordCharCount = 6
#IBAction func signUpAction(_ sender: Any) {
let providedEmailAddress = SignUpInfoViewController().emailTextField.text!
let providedPassword = SignUpInfoViewController().passwordTextField.text!
let trimmedPassword = providedPassword.trimmingCharacters(in: CharacterSet.whitespacesAndNewlines)
if !(validEmail(enteredEmail: providedEmailAddress) && validPassword(enteredPassword: trimmedPassword)) {
invalidCredentialsAlert()
}
else {
FIRAuth.auth()?.createUser(withEmail: providedEmailAddress, password: providedPassword) { user, error in
if error == nil {
FIRAuth.auth()!.signIn(withEmail: providedEmailAddress,
password: providedPassword)
}
else {
let alertController = UIAlertController(title: "Error", message: error?.localizedDescription, preferredStyle: .alert)
let defaultAction = UIAlertAction(title: "OK", style: .cancel, handler: nil)
alertController.addAction(defaultAction)
self.present(alertController, animated: true, completion: nil)
}
}
}
}
// Email is valid if it has a standard email format
func validEmail(enteredEmail: String) -> Bool {
let emailFormat = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,64}"
let emailPredicate = NSPredicate(format:"SELF MATCHES %#", emailFormat)
return emailPredicate.evaluate(with: enteredEmail)
}
// Password is valid if it is not empty or greater than a specified number of characters
func validPassword(enteredPassword: String) -> Bool {
if (enteredPassword != "" && enteredPassword.characters.count >= minPasswordCharCount) {
return true
}
return false
}
In the TermsOfUseViewController class, I am attempting to use the emailTextField and passwordTextField outlets from the SignUpInfoViewController, but I am receiving the following error:
fatal error: unexpectedly found nil while unwrapping an Optional value
I debugged the error and saw that the emailTextField property from SignUpInfoViewController is nil and so force unwrapping it will cause the app to crash (Note: I have correctly connected the IBOutlets to the SignUpInfoViewController, so no issue there).
How can I safely transfer the usage of the IBOutlets from the SignUpInfoViewController class to the TermsOfUseViewController class without it crashing? In other words, how can I make it to where the IBOutlets are no longer nil when I reference them in the TermsOfUseViewController class?
Thank you!
That is a perfect scenario for delegate pattern
protocol SignUpProtocol: class {
func didProvideUserData(username: String ,password: String)
}
In your signup class declare a delegate: public weak var delegate:SignUpProtocol?
I am assuming when the user has provided the require info, they need to press some button to go to the next step: Thus in that button you should raise the delegate
#IBAction func nextButton(sender:UIButton) {
guard let username = usernameTextfield?.text, let password = passwordTextField?.text, else { fatalError("textfields were empty") }
if delegate != nil { // this saying when someone is listening to me, I will expose any method associated to me
delegate?.didProvideUserData(username:username, password:password) // passing the username and password from textfield
}
}
if you don't have a button, then look at property observer, where you could have some property
var didFulfill:Bool? = nil {
didSet {
if didFulfill != nil && didFulfill == true {}
// here you check if your textfields are sets then raise the delegate
}
}
set this property didFulfill = when both textfields are not empty :)
Now in your Terms class, just subscribe to that delegate
class TermsOfUseViewController: UIViewController, SignUpProtocol {
var signUpVc: SignUpInfoViewController?
override func viewDidLoad() {
super.viewDidLoad()
signUpVc = SignUpInfoViewController()
signUpVc?.delegate = self
}
func didProvideUserData(username: String, password:String) {
// there is your data
}
}
You have to take in account that you don't have all references for all UIPageViewControllers all the time. That being said, I would suggest either to keep object in UIPageViewController with updated information or using Singleton Pattern to use it to store info into it and later use it. UIPageViewController are being reused and you might have one before and one after and relying onto having them would be wrong.
You can use UIPageViewController as self.parentViewController or something like that.

How does encoding and decoding functions work in Swift 3 using NSCoding?

Good afternoon!
Can anyone help me understand how saving data using NSCoding works in Swift? I am a beginner in this language and I am currently watching some tutorials on how to work with table views(create cells, saving data, etc).
The code below creates a UITableView where I can just add the first and last name of an employee. However, I cannot understand how these encoding and decoding functions work, because it has been assigned just one key to the first name and one key to the last name(that's what I understood). I mean that, because it is an array of employees, and is these functions intelligent enough to give the same keys to the first and last names of all the employees and then to retrieve the data?
ViewController class:
class ViewController: UIViewController, UITableViewDataSource, UITableViewDelegate {
var data = [Employee]()
#IBOutlet weak var myTableView: UITableView!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
loadData()
}
var filePath: String {
let manager = FileManager.default
let url = manager.urls(for: .documentDirectory, in: .userDomainMask).first
return url!.appendingPathComponent("Data").path
}
private func loadData(){ //Decode
if let ourData = NSKeyedUnarchiver.unarchiveObject(withFile: filePath) as? [Employee] {
data = ourData
}
}
private func saveData(employee: Employee){ //Encode
self.data.append(employee)
NSKeyedArchiver.archiveRootObject(data, toFile: filePath)
}
func numberOfSections(...)
func tableView(...)
func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell {
//Could be UITableViewCell(), but for a better performance we use this reusable form below:
let cell = tableView.dequeueReusableCell(withIdentifier: "Cell", for: indexPath)
//"indexPath" will return the information based on the number of rows we have. The number of rows in this case is "data.count"
cell.textLabel?.text = data[indexPath.row].Name
cell.detailTextLabel?.text = data[indexPath.row].LastName
return cell
}
#IBAction func addEmployee(_ sender: AnyObject) {
let alert = UIAlertController(title: "Add New Employee", message: "Enter Employee's name", preferredStyle: .alert)
let saveButon = UIAlertAction(title: "Save", style: .default){
(alertAction: UIAlertAction) in
let employeeName = alert.textFields?[0].text!
let employeeLastName = alert.textFields?[1].text!
let newEmployee = Employee(name: employeeName!, lastName: employeeLastName!)
self.saveData(employee: newEmployee)
self.myTableView.reloadData()
}
let cancelButton = UIAlertAction(title: "Cancel", style: .default, handler: nil)
alert.addTextField(configurationHandler: nil)
alert.addTextField(configurationHandler: nil)
alert.addAction(saveButon)
alert.addAction(cancelButton)
self.present(alert, animated: true, completion: nil)
}
}
Employee class image:
I hope you understood my questions, otherwise let me know. Thank you very much!
The keys are what the coder uses to serialize and deserialize the properties of your class. You can give them any string value. You could call the firstName property key "firstName" if you like instead of "name" but they have to match the value of when you code and decode the object. If they do not you will not get the values you expect. The class has to match as well to the class being decoded and encoded so beware of changing this in the future or you will have a crash. The data will absolutely persist between launches. The data is stored in the app file directory. Here is a link to a project that uses the exact code above. You can see for yourself that it will persist between launches.
https://www.dropbox.com/s/zuxd1kt3brkxtgi/Archiving.zip?dl=0

How to find child name from a value [duplicate]

This question already has an answer here:
How to fetch email id from JSON file in firebase using swift
(1 answer)
Closed 6 years ago.
I need your help. I am developing an IOS app using firebase and swift. I have a list of users in JSON file, using that JSON file I am trying to authenticate user.
At the moment, my code can retrieve value of email. My goal is to get the ID if I give my Input as email. For examlple: IF i give email as sandeep#gmail.com then I need it ID as 231. For this I have written a function findParent(), but i am getting this error "cannot convert value of type 'UITextfield' to expected argument type 'string'". Please help me.
Below are supplements:
Json File:
[
{
"ID": 231,
"iN_Hub Membership": 12345,
"title": "Mr.",
"firstname": "kumar",
"lastname": "sandeep",
"email": "sandeep#gmail.com",
},
Swift code:
import UIKit
import Firebase
import FirebaseAuth
//import FirebaseDatabase
class ViewController: UIViewController {
#IBOutlet weak var emailofUser: UITextField!
#IBOutlet weak var passwordofUser: UITextField!
var ref:FIRDatabaseReference! //created a variable ref of type firebase database reference
var databaseHandle:FIRDatabaseHandle? //to handle to database listener like to stop or start it
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference()
let newRef = ref.child("4")
newRef.queryOrderedByKey().observeEventType(.Value, withBlock: { snapshot in
let userId = snapshot.value!["ID"]
let memberhipNum = snapshot.value!["iN_Hub Membership"]
let emailID = snapshot.value!["email"]
print(userId)
print(memberhipNum)
print(emailID)
})
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func LoginPage(sender: UIButton) {
func findParent(emailid: String) {
//set firebase reference
ref = FIRDatabase.database().reference()
ref.child(emailid ).queryOrderedByKey().observeEventType(.Value, withBlock: { snapshot1 in
let myuserID = snapshot1.value!["ID"]
print(myuserID)
})
}
findParent(emailofUser)
if let email = emailofUser.text, pass = passwordofUser.text {
FIRAuth.auth()?.signInWithEmail(email, password: pass, completion: { (user, error) -> Void in
if error == nil{
print("Now its working. Well done Sandeep!!")
self.performSegueWithIdentifier("UserDetails", sender: self)
}
else{
print("nonononono")
let alertController = UIAlertController(title: "oops!", message: "user name and password combination is wrong!", preferredStyle: .Alert)
let defaultAction = UIAlertAction(title: "Ok", style: .Cancel, handler: nil)
alertController.addAction(defaultAction)
self.presentViewController(alertController, animated: true, completion: nil)
}
})
}
}
}
Hope anyone will help me. Thank you in advance.
One mistake that could be the problem:
findParent(emailofUser)
Should be:
findParent(emailofUser.text)

Xcode 7 + swift. "Use of local variable '_' before its declaration" Error

I get the above error when trying to create a function to check user inputs and store data. My project builds fine until I reach this function RegisterButtonTapped(). Does anyone have some structural or syntax changes that could get rid of this error?
#IBAction func RegisterButtonTapped(sender: AnyObject) {
let userEmail = userEmailTextField.text;
let userPassword = userEmailTextField.text;
let userRepeatPassword = userRepeatPasswordTextField.text;
// Check for empty fields
if(userEmail!.isEmpty || userPassword!.isEmpty || userRepeatPassword!.isEmpty){
displayAlertMessage("All fields are required");
return;
}
// Check if passwords match
if(userPassword != userRepeatPassword){
displayAlertMessage("Passwords do not match");
return;
}
// Store Data
NSUserDefaults.standardUserDefaults().setObject(userEmail, forKey: "userEmail");
NSUserDefaults.standardUserDefaults().setObject(userEmail, forKey: "userPassword");
NSUserDefaults.standardUserDefaults().synchronize();
// Display Alert message with confirmation
var myAlert = UIAlertController(title:"Alert",message:"Registration is successful, thank you.",preferredStyle: UIAlertControllerStyle.Alert);
func displayAlertMessage(userMessage:String){
var myAlert = UIAlertController(title:"Alert",message: userMessage, preferredStyle: UIAlertControllerStyle.Alert);
let okAction = UIAlertAction(title:"ok",style: UIAlertActionStyle.Default, handler:nil);
myAlert.addAction(okAction);
self.presentViewController(myAlert, animated: true, completion: nil);
} // END OF FUNCTION 'displayAlertMessage()'
} // END of FUNCTION 'RegisterButtonTapped()'
When you have a nested function, you have to declare it before you can call it. In this case, move the "displayAlertMessage(userMessage:String)" function above the "// Check for empty fields" comment, then it should compile.
You have declared displayAlertMessage after you are calling it, move the declaration of it near the top of RegisterButtonTapped() if you want to keep it as a nested function, otherwise move it out of RegisterButtonTapped().
Apart from that you have two variables both called myAlert, the first is useless, and you are saving userEmail as both the email and the password, also calling synchronize() is not required.
There are some details to get it running right. The displayAlertMessage was declared inside the register function after the calling and like that we get the warning. When you get success, you must call the function with the success message, and not declaring var myAlert like inside the alert function. And final detail: when getting the UITextFields values, you got email input field and set it to password value, so the validation will be wrong.
Here a sample of code that works great:
class ViewController: UIViewController {
#IBOutlet var userEmailTextField:UITextField!
#IBOutlet var userPassTextField:UITextField!
#IBOutlet var userRepeatPasswordTextField:UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func RegisterButtonTapped(sender:UIButton) {
let userEmail = userEmailTextField.text;
let userPassword = userPassTextField.text;
let userRepeatPassword = userRepeatPasswordTextField.text;
// Check for empty fields
if(userEmail!.isEmpty || userPassword!.isEmpty || userRepeatPassword!.isEmpty){
displayAlertMessage("All fields are required");
return;
}
// Check if passwords match
if(userPassword != userRepeatPassword){
displayAlertMessage("Passwords do not match");
return;
}
// Store Data
NSUserDefaults.standardUserDefaults().setObject(userEmail, forKey: "userEmail");
NSUserDefaults.standardUserDefaults().setObject(userEmail, forKey: "userPassword");
NSUserDefaults.standardUserDefaults().synchronize();
displayAlertMessage("Registration is successful, thank you.")
}
// Display Alert message with confirmation
func displayAlertMessage(userMessage:String){
let myAlert = UIAlertController(title:"Alert",message: userMessage, preferredStyle: UIAlertControllerStyle.Alert);
let okAction = UIAlertAction(title:"ok",style: UIAlertActionStyle.Default, handler:nil);
myAlert.addAction(okAction);
self.presentViewController(myAlert, animated: true, completion: nil);
}
}

Resources