This question already has answers here:
Passing data between view controllers
(45 answers)
Closed 4 years ago.
I want to pass the value "Email" from one VC to another when I press the button. So i want create a sign up with firebase and swift 4, first a view controller asks me the email, after entering the email, I click on next, and another view controller will ask me for the password, then click on register. How do I save the email of first ViewController and pass it to the second ViewController? And then send email of first vc and password of second vc to firebase? sorry for my english. In practice it must work like the instagram sign up.
import Foundation
import Firebase
import FirebaseAuth
import UITextField_Shake
internal class SignUpSegue0 : UIViewController {
#IBOutlet weak var labelwrongpass: UILabel!
#IBOutlet weak var Emailsign: UITextField!
func isValidEmail(testStr:String) -> Bool {
print("validate emilId: \(testStr)")
let emailRegEx = "[A-Z0-9a-z._%+-]+#[A-Za-z0-9.-]+\\.[A-Za-z]{2,4}"
let emailTest = NSPredicate(format:"SELF MATCHES %#", emailRegEx)
let result = emailTest.evaluate(with: testStr)
return result
}
#IBAction func Nextsign1(_ sender: UIButton) {
guard let email = Emailsign.text else { return }
if Emailsign.text == "" {
self.labelwrongpass.text = "Please enter your email"
self.Emailsign.shake(10,withDelta: 5.0)
} else {
Auth.auth().fetchProviders(forEmail: email, completion: {
(providers, error) in
if error != nil {
print("Email disponibile")
self.labelwrongpass.text = "Email inserita correttamente"
self.labelwrongpass.textColor = UIColor.green
func prepare(for segue: UIStoryboardSegue, sender: Any?)
{
if segue.destination is SignUpSegue1
{
let vc = segue.destination as? SignUpSegue1
vc?.email = ""
}
}
} else if providers != nil {
print("L'email inserita è già esistente")
self.labelwrongpass.text = nil
self.labelwrongpass.text = "L'email inserita è già esistente"
self.Emailsign.shake(10,withDelta: 5.0)
}
})
}
}
}
You can create a var in your destination controller:
class DestinationViewController: UIViewController {
var variableToShare : String?
//the rest of the class methods....
And in your controller override PrepareForSegue and something like:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
// Create a variable that you want to send
var toShare = "Email"
// Create a new variable to store the instance of DetinationViewController
let destinationVC = segue.destinationViewController as DetinationViewController
destinationVC.variableToSharer = toShare
}
}
Related
I'm learning both Swift and firebase by making an app. In my current firestore, I have a database with collection name Players, and inside of the collection, there is a document with auto-documentID and two fields, such as name and email. Like this.
firestore image
I also use Firebase Auth, so whenever a user makes an account I put their email into firestore with field name email.
Here is the code for RegisterViewController where a user register their account with email and password.
import UIKit
import Firebase
class RegisterViewController: UIViewController {
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
let db = Firestore.firestore()
override func viewDidLoad() {
super.viewDidLoad()
}
//MARK: - register a new account
#IBAction func registerPressed(_ sender: UIButton) {
if let email = emailTextField.text, let password = passwordTextField.text {
Auth.auth().createUser(withEmail: email, password: password) { authResult, error in
if error != nil {
// some codes here
} else {
// Add a new document with a generated ID
var ref: DocumentReference? = nil
ref = self.db.collection(K.FStore.playersCollection).addDocument(data: [K.FStore.emailField: email, K.FStore.nameField: ""]) { (error) in
if let err = error {
print("There was an issue storing data to firestore. \(err)")
} else {
print("Document added with ID: \(ref!.documentID)")
}
}
// pass this data
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == K.registerToHome {
let homeVC = segue.destination as! HomeViewController
homeVC.documentID = ref?.documentID
}
}
self.performSegue(withIdentifier: K.registerToHome, sender: self)
}
}
}
}
}
I'm planning to check if the user has a name in their document name field in the next HomeViewController, so if the value of the name field in the document is "", I want to display an alert with a textfeild, so the user can add their name. However, in the HomeViewController, I need the documentID because I will use this code, db.collection(K.FStore.playersCollection).document(documentID).getDocument in HomeVC, so I want to pass the user's documentID (in the RegisterVC, I was able to get the value as ref?.documentID, so I just want to pass the value to HomeVC) to HomeVC. I tried using func prepare(for segue: UIStoryboardSegue, sender: Any?), but I was not able to pass the value...
Change your code to :
//Top of the your code define id :
var id = ""
else {
// Add a new document with a generated ID
var ref: DocumentReference? = nil
ref = self.db.collection(K.FStore.playersCollection).addDocument(data: [K.FStore.emailField: email, K.FStore.nameField: ""]) { (error) in
if let err = error {
print("There was an issue storing data to firestore. \(err)")
} else {
print("Document added with ID: \(ref!.documentID)")
self.id = ref!.documentID
self.performSegue(withIdentifier: K.registerToHome, sender: self)
}
}
}
//Out of registerPressed func :
// pass this data
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == K.registerToHome {
let homeVC = segue.destination as! HomeViewController
homeVC.documentID = self.id
}
}
I am trying to make a list of users and their passwords in one view controller, save that information in a dictionary, and send that dictionary to another view controller which asks the user to input their username/password combination to authorize the log in. (the key is the username and the value is the password). Is there a way I can send the dictionary from SecondVC to the FirstVC?
First View Controller
class ViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var Username: UITextField!
#IBOutlet weak var Verification: UILabel!
#IBOutlet weak var Password: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
Username.delegate = self
Password.delegate = self
}
var usersDict = [String : String]()
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let des = segue.destination as? AccountViewController {
des.usersDict = usersDict
}
}
#IBAction func Authorization(_ sender: Any) {
for ( key , value ) in usersDict{
let v = key.count
var start = 0
if start <= v{
if Username.text == key{
if Password.text == value{
Verification.text = "Looks Good"
}
}
else{
start += 1
}
}
else{
Verification.text = "Yikes"
}
}
}
}
Second View Controller
class AccountViewController: UIViewController, UITextFieldDelegate {
#IBOutlet weak var CreateUsername: UITextField!
#IBOutlet weak var CreatePassword: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
CreateUsername.delegate = self
CreatePassword.delegate = self
// Do any additional setup after loading the view.
}
var usersDict = [ String : String ]()
#IBAction func MakeANewAccount(_ sender: Any) {
usersDict[CreateUsername.text!] = CreatePassword.text!
}
}
I have made there dictionary, but it will only send in the beginning and won't update after creating the original account. (dictionary it is sending is empty)
With a segue add this method inside ViewController
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let des = segue.destination as? AccountViewController {
des.usersDict = yourDicHere
}
}
Here's a general pattern for making a controller work with data from some object it creates, in this case a second controller.
Try applying it to your situation and let me know if you run into problems.
protocol Processor {
func process(_ dict: [String : String])
}
class FirstController: UIViewController, Processor {
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if let controller = segue.destination as? SecondController {
controller.delegate = self
} else {
print("Unexpected view controller \(segue.destination)")
}
}
func process(_ dict: [String : String]) {
}
}
class SecondController: UIViewController {
var delegate: Processor?
func someWork() {
if let processor = delegate {
processor.process(["Name" : "Pwd"])
} else {
print("Delegate not assigned")
}
}
}
In order to practice my networking, I built an app with a text field where you can input something. I use the wikipedia API to fetch the definition of that term / name/ expression. My goal is to then display that definition into another view controller.
A button performs the segue to the new view controller, where a label displays that definition.
The get request works, but when tapping the button, I get a fatalError : "Fatal error: Unexpectedly found nil while implicitly unwrapping an Optional value".
I would like to add that the error is displayed in the "prepare for segue" function.
Here is the code for my first view controller
import UIKit
import Alamofire
import SwiftyJSON
class ViewController: UIViewController {
#IBOutlet weak var textEntryLabel: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
//MARK: - Relevant variables
let wikipediaURl = "https://en.wikipedia.org/w/api.php"
var termDefinitionInfo: String = ""
let segueName: String = "toDefinition"
#IBAction func buttonToDefinition(_ sender: UIButton) {
// on fait la requete ici
httpCall(termDefinition: textEntryLabel.text ?? "nothing to pass")
performSegue(withIdentifier: segueName , sender: self)
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == segueName {
let secondVC = segue.destination as! DefinitionViewController
secondVC.definitionLabel.text = termDefinitionInfo
}
}
//MARK: - NETWORKING
func httpCall(termDefinition: String) {
let parameters : [String:String] = [
"format" : "json",
"action" : "query",
"prop" : "extracts",
"exintro" : "",
"explaintext" : "",
"titles" : termDefinition,
"indexpageids" : "",
"redirects" : "1",
]
//
request(wikipediaURl, method: .get, parameters: parameters).responseJSON { (response) in
if response.result.isSuccess {
//1. on affiche le tableau json initial
let definitionJSON: JSON = JSON(response.result.value)
print(definitionJSON)
// deux valeurs : pageID et definition
let pageId = definitionJSON["query"]["pageids"][0].stringValue
let pageDefinition = definitionJSON["query"]["pages"][pageId]["extract"].stringValue
self.termDefinitionInfo = pageDefinition
print(self.termDefinitionInfo)
} else {
print("Error! Could not fetch data!")
}
}
}
}
Here is the code for the second view controller
import SwiftyJSON
import Alamofire
class DefinitionViewController: UIViewController {
#IBOutlet weak var definitionLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
}```
Tip: Try to avoid force down casting
In your case you are trying to assign a value to an IBOutlet when it's not wired to its parent view controller. You better do this:
class DefinitionViewController: UIViewController {
#IBOutlet weak var definitionLabel: UILabel!
var labelValue: String?
override func viewDidLoad() {
super.viewDidLoad()
definitionLabel.text = labelValue
}
}
And in your first view:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == segueName {
if let secondVC = segue.destination as? DefinitionViewController {
secondVC.labelValue = termDefinitionInfo
}
}
}
I have two viewcontrollers(SignInViewcontroller.swift and ProfilePage.swift)
I want to pass the string from SignInViewcontroller to ProfilePage viewcontroller.
I created a protocol in SignInViewcontroller.And I delegate the method in ProfilePage controller.When I send the string through protocols I didn't receive that string in ProfilePage viewcontroller Where I am wrong.please help me to solve.
Here is my code:
SignInViewController.swift
protocol sendTokenDelegate: class {
func sendToken(login:String)
}
class SignInViewController: UIViewController {
weak var delegateToken:sendTokenDelegate?
func loginAzure(email: String, password: String) {
token = "abcdefgh"
self.delegateToken?.sendToken(login: token)
}
}
ProfilePage.swift
class ProfilePage: UIViewController, UITableViewDelegate, UITableViewDataSource, sendTokenDelegate {
override func viewDidLoad() {
let signInVC = SignInViewController()
signInVC.delegateToken = self
}
func sendToken(login: String) {
self.logInToken = login
print("Login Token in Profile Page is \(login)")
}
}
In that case, you need object that will store token from SignInViewController, until ProfilePage is requesting it.
class TokenStorage {
static let shared = TokenStorage()
public var token: String = ""
}
then you receive token call:
TokenStorage.shared.token = receivedToken
and in ProfilePage request it:
print(TokenStorage.shared.token)
If you are coming from SignUpViewController to ProfilePageViewController, you can pass the string values upon navigation after getting the singIn token you want from your logingAzure() I assume:
If you navigate using segues -> self.performSegue(withIdentifier: "signUpToProfile", sender: self)
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "signUpToProfile" {
if let profileVC : ProfilePageViewController =
segue.destination as? ProfilePageViewController {
profileVC.loginToken = token
}
}
}
If you are using self.navigationController?.pushViewController
let storyboard = UIStoryboard(name: "Profile", bundle: Bundle.main)
if let profileVC = storyboard.instantiateViewController(withIdentifier:
"ProfilePageViewController") as? ProfilePageViewController {
profileVC.loginToken = token
}
EDIT
If you are not going to profilePage directly from SignUpViewController,
then just save the token in your Keychain OR UserDefaults.
Do this by creating a SessionManager singleton to handle tokens and other resources when logging in or signing up
Just use "prepare for segue"
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "ProfileSegue" {
if let vc = segue.destination as? ProfilePage {
vc.token = self.token
}
}
}
When you are going to NextVC that doesn't exist in the navigation controller then you have to bind data with the instance of the class when you are pushing just like that:
let vc = UIStoryboard(name: "ProfilePage", bundle: nil).instantiateInitialViewController() as! ProfilePage
vc. logInToken = "abcdefgh"
self.navigationController?.pushViewController(vc, animated: true)
class ProfilePage: UIViewController {
var logInToken = "" // you will receive the token in this variable.
}
In you case, it seems like ProfilePage doesn't exist.
NOTE:- Delegate will use just opposite case when you want to pass the value from ProfilePage to SignInViewController.
OR
If your all API wants the token so you can declare at the class level or save it to UserDefauls:
1)
var logInToken = "" // your variable visible the entire application classes
class ProfilePage: UIViewController {
}
func loginAzure(email: String, password: String) {
logInToken = "abcdefgh" //Just assign and use it
}
2)
UserDefaults.standard.set("aasdfa", forKey: "token")
let token = UserDefaults.standard.value(forKey: "token"
Also, you are doing the bad coding you have to understand the OOP's
let signInVC = SignInViewController()
signInVC.delegateToken = self
This will reperensent the seprate instance in the memory and every
object has its own properties and behavior.
Try using:
protocol sendTokenDelegate: class {
func sendToken(login:String)
}
class SignInViewController: UIViewController {
weak var delegateToken:sendTokenDelegate?
func loginAzure(email: String, password: String) {
token = "abcdefgh"
if self.delegateToken != nil{
self.delegateToken?.sendToken(login: token)
}
}
}
class ProfilePage: UIViewController, UITableViewDelegate, UITableViewDataSource, sendTokenDelegate {
override func viewDidLoad() {
//get your instantiateViewController from storyboard
let signInVC = self.storyboard?.instantiateViewController(withIdentifier: "SignInViewControllerIdentifire") as! SignInViewController
signInVC.delegateToken = self
}
func sendToken(login: String) {
self.logInToken = login
print("Login Token in Profile Page is \(login)")
}
}
I have created a prepareForSegue method and I am trying to call it from a button that I created by using the performSegueWithIdentifier method. The app is crashing when I load the simulator and it's not getting me a complete error message. Can someone please lead me in the right direction?
import Foundation
import UIKit
import Alamofire
import FBSDKCoreKit
import FBSDKShareKit
import FBSDKLoginKit
class PageContentViewController: UIViewController {
#IBOutlet weak var logoImageView: UIImageView!
#IBOutlet weak var contentLabel: UILabel!
#IBOutlet weak var backgroundImageView: UIImageView!
#IBOutlet weak var pageControl: UIPageControl!
#IBOutlet weak var facebookButton: UIButton!
var index : Int = 0
var logoFile: String = ""
var content: String = ""
var backgroundFile: String = ""
let facebookReadPermissions = ["public_profile", "email", "user_friends"]
override func viewDidLoad() {
super.viewDidLoad()
pageControl.currentPage = index
facebookButton.hidden = (index == 3 ) ? false : true
pageControl.hidden = (index == 3) ? true: false
logoImageView.image = UIImage(named: logoFile)
contentLabel.text = content
backgroundImageView.image = UIImage(named: backgroundFile)
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if let destinationController = segue.destinationViewController as? PaymentSubViewController
where segue.identifier == "payment" {
// Do something with `destinationController`
}
}
#IBAction func test(sender: AnyObject) {
self.performSegueWithIdentifier("payment", sender: self)
}
#IBAction func fbTouched(sender: AnyObject) {
FBSDKLoginManager().logInWithReadPermissions(self.facebookReadPermissions, handler: { (result:FBSDKLoginManagerLoginResult!, error:NSError!) -> Void in
if error != nil {
//According to Facebook:
//Errors will rarely occur in the typical login flow because the login dialog
//presented by Facebook via single sign on will guide the users to resolve any errors.
// Process error
FBSDKLoginManager().logOut()
} else if result.isCancelled {
// Handle cancellations
FBSDKLoginManager().logOut()
} else {
let fbToken = result.token.tokenString
Alamofire.request(Router.FacebookAuth(fbToken)).validate(statusCode: 200 ..< 300).responseJSON(completionHandler: { (request, response, JSON, error) in
if let json = JSON as? Dictionary<String, AnyObject> {
if let token = json["token"] as? String {
Router.OAuthToken = token
self.performSegueWithIdentifier("showHomeFeed", sender: self)
}
}
})
}
})
}
}
Because you are force unwrapping the destinationViewController using as!, if that value is nil or not a PaymentSubViewController, the app will crash.
The better way to implement this is with an optional binding (if let) and a conditional downcast (as?):
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if let destinationController = segue.destinationViewController as? PaymentSubViewController
where segue.identifier == "payment" {
// Do something with `destinationController`
}
}
Of course, that'll stop the crash but won't answer the question of why segue.destinationViewController is nil or of another type. Make sure that you segue is configured properly in interface builder and that the destination view controller actually has segue.destinationViewController for its Class value in the identity inspector tab.
I had a map object on the storyboard and I did not add an outlet for the object which was creating an error message.