Been smashing my face against the wall all day trying to upgrade my app to the Firebase 3.x code.
I was having a ton of trouble with updating my original userAuth code and decided to just start from scratch. I haven't really been able to test it though because when I run the app it is calling the segue immediately upon loading the initial VC. Obviously I don't want it to do this and I don't know what is causing it.
I've tried deleting the app from the simulator and when I load it back up I get the same result.
Here is my code for the VC:
import UIKit
import FirebaseAuth
class SignInViewController: UIViewController {
#IBOutlet weak var emailField: UITextField!
#IBOutlet weak var passwordField: UITextField!
override func viewDidAppear(animated: Bool) {
if let user = FIRAuth.auth()?.currentUser {
self.signedIn(user)
}
}
#IBAction func didTapSignIn(sender: AnyObject) {
// Sign In with credentials.
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!)
}
}
#IBAction func didTapSignUp(sender: AnyObject) {
let email = emailField.text
let password = passwordField.text
FIRAuth.auth()?.createUserWithEmail(email!, password: password!) { (user, error) in
if let error = error {
print(error.localizedDescription)
return
}
self.setDisplayName(user!)
}
}
func setDisplayName(user: FIRUser) {
let changeRequest = user.profileChangeRequest()
changeRequest.displayName = user.email!.componentsSeparatedByString("#")[0]
changeRequest.commitChangesWithCompletion(){ (error) in
if let error = error {
print(error.localizedDescription)
return
}
self.signedIn(FIRAuth.auth()?.currentUser)
}
}
#IBAction func didRequestPasswordReset(sender: AnyObject) {
let prompt = UIAlertController.init(title: nil, message: "Email:", preferredStyle: UIAlertControllerStyle.Alert)
let okAction = UIAlertAction.init(title: "OK", style: UIAlertActionStyle.Default) { (action) in
let userInput = prompt.textFields![0].text
if (userInput!.isEmpty) {
return
}
FIRAuth.auth()?.sendPasswordResetWithEmail(userInput!) { (error) in
if let error = error {
print(error.localizedDescription)
return
}
}
}
prompt.addTextFieldWithConfigurationHandler(nil)
prompt.addAction(okAction)
presentViewController(prompt, animated: true, completion: nil);
}
func signedIn(user: FIRUser?) {
MeasurementHelper.sendLoginEvent()
AppState.sharedInstance.displayName = user?.displayName ?? user?.email
AppState.sharedInstance.photoUrl = user?.photoURL
AppState.sharedInstance.signedIn = true
NSNotificationCenter.defaultCenter().postNotificationName(Constants.NotificationKeys.SignedIn, object: nil, userInfo: nil)
performSegueWithIdentifier(Constants.Segues.SignInToFp, sender: nil)
}
}
Can someone please help? Thank you in advance.
Related
The app crashes when I perform the segue. I checked if there was any sigbrt errors, but there was not. I think it is firebase analytics from the log. This error was from the log :
terminating with uncaught exception of type NSException.
The code
#IBOutlet weak var email: UITextField!
#IBOutlet weak var password: UITextField!
#IBOutlet weak var adduser: UIButton!
#IBOutlet weak var errormessege: UILabel!
var databaseref = FIRDatabase.database().reference()
override func viewDidLoad() {
super.viewDidLoad()
password.isSecureTextEntry = true
adduser.isEnabled = false
}
#IBAction func didtapcancel(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
#IBAction func didtapadd(_ sender: Any) {
adduser.isEnabled = false
FIRAuth.auth()?.createUser(withEmail: email.text!, password: password.text!, completion: {(user,error) in
if error != nil {
if error!._code == 17999 {
self.errormessege.text = "Invalid email address" }
else {
self.errormessege.text = error?.localizedDescription
}
}
else
{
FIRAuth.auth()?.signIn(withEmail: self.email.text!, password: self.password.text!, completion: {(user,error) in
if (error == nil) {
self.databaseref.child("users").child(user!.uid).child("email").setValue(self.email.text!)
self.performSegue(withIdentifier: "hi", sender: nil)
}
else {
self.errormessege.text = error?.localizedDescription
}
})
}
}
)
}
#IBAction func didtextchange(_ sender: Any) {
if((email.text?.characters.count)!>0){
adduser.isEnabled = true}
else{
adduser.isEnabled = false}
}
#IBAction func did4(_ sender: Any) {
if((password.text?.characters.count)!>0){
adduser.isEnabled = true}
else{
adduser.isEnabled = false}
}
Without more code and the entire scenario in front of me it is hard to specifically nail down what is happening between your code and Firebase. One thing that will significantly help you though in tracking down this error is to validate the data you are pulling out of your #IBOutlets each steps of the way. That way your program is not force unwrapping these objects and leaving opening up your code to risk.
#IBOutlet weak var email: UITextField!
#IBOutlet weak var password: UITextField!
#IBOutlet weak var adduser: UIButton!
#IBOutlet weak var errormessege: UILabel!
var databaseref = FIRDatabase.database().reference()
override func viewDidLoad() {
super.viewDidLoad()
password.isSecureTextEntry = true
adduser.isEnabled = false
}
#IBAction func didtapcancel(_ sender: Any) {
dismiss(animated: true, completion: nil)
}
#IBAction func didtapadd(_ sender: Any) {
adduser.isEnabled = false
guard let emailText = email.text,
let passwordText = password.text else {
// Handle error safely
print("Error unrapping email text or password text")
return
}
FIRAuth.auth()?.createUser(withEmail: emailText, password: passwordText, completion: { (user,error) in
if error != nil {
if error!._code == 17999 {
self.errormessege.text = "Invalid email address"
} else {
self.errormessege.text = error?.localizedDescription
}
} else {
FIRAuth.auth()?.signIn(withEmail: emailText, password: passwordText, completion: { (user, error) in
if (error == nil) {
guard let uid = user.uid else {
// handle error safely
print("Error with user uid")
return
}
self.databaseref.child("users").child(uid).child("email").setValue(emailText)
self.performSegue(withIdentifier: "hi", sender: nil)
}
else {
self.errormessege.text = error?.localizedDescription
}
})
}
})
}
#IBAction func didtextchange(_ sender: Any) {
guard let emailText = email.text else {
// Return error safely
print("Error unrapping email text")
return
}
if emailText.characters.count> 0 {
adduser.isEnabled = true
} else{
adduser.isEnabled = false
}
}
I can't transfer the login manager result between view controllers,
The segue is associated to the button and its identifier is s1.
My setup is correct.The program is crashing with green breakpoints.
here is my code:
for the first VC:
import FBSDKLoginKit
class ViewController: UIViewController {
var user_name: String?
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view, typically from a nib.
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let fbLoginManager = FBSDKLoginManager()
fbLoginManager.logIn(withReadPermissions: ["public_profile", "email"], from: self) { (result, error) in
if let error = error {
print("Failed to login: \(error.localizedDescription)")
return
}
guard let accessToken = FBSDKAccessToken.current() else {
print("Failed to get access token")
return
}
let credential = FIRFacebookAuthProvider.credential(withAccessToken: accessToken.tokenString)
FBSDKGraphRequest(graphPath: "/me", parameters: ["fields" : "email, id, locale"])
.start(completionHandler: { (connection, result, error) in
guard let result = result as? NSDictionary,
let user_name = result["user_name"] as? String,
else {
return
}
if(segue.identifier == "s1"){
if let v = segue.destination as? Re {
v.uname=user_name ?? ""
//v.uname = usr.text ?? ""
}
}
})
// Perform login by calling Firebase APIs
FIRAuth.auth()?.signIn(with: credential, completion: { (user, error) in
if let error = error {
print("Login error: \(error.localizedDescription)")
let alertController = UIAlertController(title: "Login Error", message: error.localizedDescription, preferredStyle: .alert)
return
}
})
}
}
}
And for Re,the next VC:
class Re: UIViewController {
var uname: String?
#IBOutlet weak var l1: UILabel!
var userfb: String?
override func viewDidLoad() {
super.viewDidLoad()
l1.text=uname
// Do any additional setup after loading the view.
}
}
You need to use instance variable which you have declared at top as below.
Now you have create new user_name and use another user_name
guard let result = result as? NSDictionary,
user_name = result["user_name"] as? String,// make change here
else {
return
}
I've successfully parsed an result from a SOAP WebServices request I made, by sending in a userName and password.
But now I need to validate this login function and if true segue to a new view programmatically:
let menuPageView = (self.storyboard?.instantiateViewController(withIdentifier: "MenuCentral"))!
self.present(menuPageView, animated: true, completion: nil)
The problem is I don't know how or where to add such validation.
class LoginCentralViewController: UIViewController, SOAPServiceProtocol {
var chave = ChaveWebService().chave()
var soapService : SOAPService?
var resultadoLoginCentral : [LoginCentralModel]!
#IBOutlet weak var txtUsuarioOUTLET: UITextField!
#IBOutlet weak var txtSenhaOUTLET: UITextField!
#IBOutlet weak var btnAcessarOUTLET: UIButton!
override func viewDidLoad() {
super.viewDidLoad()
soapService = SOAPService(delegate: self)
print(chave)
}
#IBAction func btnAcessarACTION(_ sender: Any) {
soapService?.loginCentral(userName: txtUsuarioOUTLET.text!, password: txtSenhaOUTLET.text!, methodName: nomeServico)
UIApplication.shared.isNetworkActivityIndicatorVisible = true
}
func didSuccessRequest(results: XMLIndexer, requestName: String) {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
switch requestName {
case nomeServico:
do{
resultadoLoginCentral = try LoginCentralModel.realizarLoginCentral(results: results)
} catch let error as XMLParseError{
print(error.description)
return
} catch {
print(error)
return
}
print("codigoCliente = ", resultadoLoginCentral[0].codigoCliente)
print("permissoes = " , resultadoLoginCentral[0].permissoes)
break
default:
break
}
}
func didFailRequest(err: String, requestName: String) {
UIApplication.shared.isNetworkActivityIndicatorVisible = false
switch requestName {
case nomeServico:
return
default:
break
}
}
func showAlert() {
let loginAlert = UIAlertController(title: "Central do Assinante", message: "Login/Senha inválidos", preferredStyle: .alert)
let acaoDefault = UIAlertAction(title: "OK", style: .destructive, handler: nil)
loginAlert.addAction(acaoDefault)
present(loginAlert, animated: true, completion: nil)
}
}
You can put your validation code here
do{
resultadoLoginCentral = try LoginCentralModel.realizarLoginCentral(results: results)
//Put here code
// we need to call this in main thread
DispatchQueue.main.sync {
if resultadoLoginCentral.codigoCliente.characters.count > 0 && resultadoLoginCentral.permissoes.characters.count > 0{{
// Login process
}else{
//Show Alert
}
}
}
Hope this helps
As per title, I'm having some trouble dealing with socket.io. It connects really well and accordingly in the first view controller but weird things happen when it comes to second controller.
Here's the code:
First Controller: I have declared some global variable for connection purposes between both view controller.
import UIKit
import SocketIOClientSwift
import SwiftyJSON
import CoreData
//declare some global variable
var patientCoreData = [NSManagedObject]()
var numberOfUsersExisting:Int = 0 //assign to 0 by default
var appUserData: Patient? //for specific user
var pSample: Array<Patient> = [] //for all user
//initiate socket globally
let socket = SocketIOClient(socketURL: "localhost:3000", options: [
"reconnects": true
])
func reportStatus(){
socket.on("connect") {data, ack in
print("Report status: View Controller connected")
socket.emit("click", "Client app connected")
}
}
func readDataFromSocket(completion: (data:AnyObject)-> ()){
socket.on("reply") {data, ack in
print("database replied")
completion(data: data)
}//socket
}//readDataFromSOCKET
func importData(){
reportStatus()
socket.connect()
readDataFromSocket(){ data in
let json = JSON(data)
let nou = json[0].count
if nou > 0 {
print("Test(1st VC): grabbing data from database")
for var i=0; i<nou; ++i{
numberOfUsersExisting = nou
pSample += [Patient(id: json[0][i]["ID"].intValue, name: json[0][i]["Name"].stringValue, gender: json[0][i]["Gender"].stringValue, mileage: json[0][i]["Mileage"].doubleValue)]
pSample.sortInPlace({$0.globalPatientMileAge < $1.globalPatientMileAge})
}
print("Successfully grabbed data")
}else{
print("No user in the database")
numberOfUsersExisting = 0
}
}//readDataFromSocket
}
class ViewController: UIViewController, UICollectionViewDataSource, UICollectionViewDelegate, UICollectionViewDelegateFlowLayout{
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func viewDidAppear(animated: Bool) {
super.viewDidAppear(animated)
print("First view appeared")
let prefs = NSUserDefaults.standardUserDefaults()
//if an user has logged in
let isLoggedIn = prefs.integerForKey("ISLOGGEDIN") as Int
if (isLoggedIn != 1){
print("No user currently, so heading to login screen")
socket.disconnect()
self.performSegueWithIdentifier("gotoLogin", sender: self)
}else{
print("ViewDidAppear: An user has been logged in")
let permissionToLoadData = prefs.integerForKey("ISLOGGEDIN")
if (permissionToLoadData != 1) {
print("Please grant permission to get data")
}else{
print("First view: connecting to database")
importData()
}//permission to load data
}
}//end of viewDidAppear
}
Second Controller:
import UIKit
import SocketIOClientSwift
import SwiftyJSON
import CoreData
var nou:Int?
class LoginViewController: UIViewController {
let prefs:NSUserDefaults = NSUserDefaults.standardUserDefaults()
let registeredUserID = NSUserDefaults.standardUserDefaults().stringForKey("registerPatientID")
let appDel:AppDelegate = UIApplication.sharedApplication().delegate as! AppDelegate
func displayAlertMessage(userMessage:String){
let alert = UIAlertController(title: "Alert", message: userMessage, preferredStyle: UIAlertControllerStyle.Alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil)
alert.addAction(okAction)
self.presentViewController(alert, animated: true, completion: nil)
}
func successMessage(userMessage:String){
let alert = UIAlertController(title: "Welcome Back", message: userMessage, preferredStyle: UIAlertControllerStyle.Alert)
let okAction = UIAlertAction(title: "OK", style: UIAlertActionStyle.Default, handler: nil)
alert.addAction(okAction)
self.presentViewController(alert, animated: true, completion: nil)
}
#IBOutlet weak var loginPatientID: UITextField!
#IBAction func LoginButton(sender: AnyObject) {
let logInUserID = loginPatientID.text
if (logInUserID!.isEmpty){
displayAlertMessage("Please enter your Patient ID!")
return
}else{
print("Test: requesting login permission from database")
socket.emit("loginRequest", logInUserID!)
print("Test: requested")
socket.on("loginReply") {data, ack in
let jsonLogin = JSON(data)
if jsonLogin[0].intValue == 1{
print("Test: ID Matched, putting up ViewController")
self.prefs.setObject(logInUserID, forKey: "AppUserID")
self.prefs.setInteger(1, forKey: "ISLOGGEDIN")
self.prefs.synchronize()
let permissionToLoadData = self.prefs.integerForKey("ISLOGGEDIN")
if (permissionToLoadData != 1) {
print("Please grant permission to get data")
}else{
print("First view: connecting to database")
importData()
print("Did you import?")
}//permission to load data
self.loginPatientID.resignFirstResponder()
self.dismissViewControllerAnimated(true, completion: nil)
}else if jsonLogin[0].intValue == 0{
self.displayAlertMessage("Sorry, you are not assigned to this program")
}else if jsonLogin[0].intValue == 3{
print("Test: Query problem")
}else{
print("Test: not getting anything from ID database")
}
}//socket.on
}//else
}//login button
override func viewDidLoad() {
super.viewDidLoad()
print("Login View Controller loaded")
}
override func viewDidAppear(animated: Bool) {
socket.connect()
print("LoginVC: establishing connection")
}
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?) {
self.view.endEditing(true)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
}
}
You may have noticed that in First view controller, when the viewDidAppear() is launched, the app will checks if user is login or not. If somebody has already logged in, it's fine. If there is nobody logging in, it will perform a segue(modally segue) to Second view controller.
A login form will be presented in second view controller and once user hits the login button, you might wanna look at the code.
Let's assume that everything goes right until it comes to importData(), the function isn't launched at all but the app just goes on, why?
Here's a screenshot of the console, pay attention to "Did you import?", if the function is launched, the app should return some additional message from 1st view controller.
After struggling for a few days, I think I may have found the correct answer.
Eventually I defined 2 different socket handlers connection as such:
let loginSocket = SocketIOClient(socketURL: "localhost:3000")
let socket = SocketIOClient(socketURL: "localhost:3000", options: [
"reconnects": true
])
for both view controller.
If there is a conclusion I can draw from this conundrum is that we can't use single socket handler for socket methods from different view controller.
Im working on an app that uses phone number and SMS verification to login. It all works well except for one small issue. If I logout of one user, then login with another, the previous users data is loaded, however a new user is created but the previous users data is displayed. I have to logout and login the new user again to load their data. Anybody see whats going on?
Login code:
class LoginViewController: UIViewController {
func displayAlert(title: String, message: String) {
var alert = UIAlertController(title: title, message: message, preferredStyle: UIAlertControllerStyle.Alert)
alert.addAction(UIAlertAction(title: "Ok", style: .Default, handler: { (action) -> Void in
}))
self.presentViewController(alert, animated: true, completion: nil)
}
#IBOutlet weak var instructionLabel: UILabel!
#IBOutlet weak var phoneNumberTextField: UITextField!
#IBOutlet weak var sendCodeButton: UIButton!
var phoneNumber: String = ""
override func viewDidLoad() {
super.viewDidLoad()
first()
self.editing = true
}
func first() {
phoneNumber = ""
phoneNumberTextField.placeholder = "555-555-5555"
instructionLabel.text = "Enter your phone number to login or sign up"
sendCodeButton.enabled = true
}
func second() {
phoneNumber = phoneNumberTextField.text!
phoneNumberTextField.text = ""
phoneNumberTextField.placeholder = "1234"
instructionLabel.text = "Enter your 4 digit security code"
sendCodeButton.enabled = true
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
phoneNumberTextField.becomeFirstResponder()
}
#IBAction func didTapSendCodeButton() {
let preferredLanguage = NSBundle.mainBundle().preferredLocalizations[0]
let textFieldText = phoneNumberTextField.text ?? ""
if phoneNumber == "" {
if (preferredLanguage == "en" && textFieldText.characters.count != 10) {
displayAlert("Phone Login", message: NSLocalizedString("warningphone", comment: "You must enter a 10 digit US phone number including area code"))
return first()
}
self.editing = false
let params = ["phoneNumber" : textFieldText, "language" : preferredLanguage]
PFCloud.callFunctionInBackground("sendCode", withParameters: params) { response, error in
self.editing = true
if let error = error {
var description = error.description
if description.characters.count == 0 {
description = NSLocalizedString("warningGeneral", comment: "Something went Wrong. Please try again")
} else if let message = error.userInfo["error"] as? String {
description = message
}
self.displayAlert("Login Error", message: description)
return self.first()
}
return self.second()
}
} else {
if textFieldText.characters.count == 4, let code = Int(textFieldText) {
return doLogin(phoneNumber, code: code)
}
displayAlert("Code Entry", message: NSLocalizedString("warningCodeLength", comment: "You must enter the 4 digit code texted to your number"))
}
}
func doLogin(phoneNumber: String, code: Int) {
self.editing = false
let params = ["phoneNumber": phoneNumber, "codeEntry": code] as [NSObject:AnyObject]
PFCloud.callFunctionInBackground("logIn", withParameters: params) { response, error in
if let description = error?.description {
self.editing = true
return self.displayAlert("Login Error", message: description)
}
if let token = response as? String {
PFUser.becomeInBackground(token) { user, error in
if let _ = error{
self.displayAlert("Login Error", message: NSLocalizedString("warningGeneral", comment: "Something happened while logging in. Please try again"))
self.editing = true
return self.first()
}
return self.dismissViewControllerAnimated(true, completion: nil)
}
} else {
self.editing = true
self.displayAlert("Login Error", message: NSLocalizedString("warningGeneral", comment: "Something went wrong. Please try again"))
return self.first()
}
}
}
override func setEditing(editing: Bool, animated: Bool) {
sendCodeButton.enabled = editing
phoneNumberTextField.enabled = editing
if editing {
phoneNumberTextField.becomeFirstResponder()
}
}
}
extension LoginViewController : UITextFieldDelegate {
func textFieldShouldReturn(textField: UITextField) -> Bool {
self.didTapSendCodeButton()
return true
}
}
Found the problem I had to update the label in viewWillAppear