How to send Facebook values to next screen in Xcode 8, swift 3 - ios

I'm putting together a series of registration pages where users are first presented with a "create account using Facebook" button, which logs them in, and then presents them with a basic registration page filled with empty text boxes. However, I am trying to populate some of these text boxes with the users' values gathered from the Graph Request.
Here is the first screen with the registration button:
import UIKit
import FBSDKLoginKit
class RegisterVC: UIViewController, FBSDKLoginButtonDelegate {
var fbLoginSuccess = false
var fbName:String!
var fbEmail:String!
override func viewDidLoad() {
super.viewDidLoad()
let loginButton = FBSDKLoginButton()
view.addSubview(loginButton)
loginButton.frame = CGRect(x: 82, y: 325, width: view.frame.width - 210, height: 59)
loginButton.delegate = self
}
func loginButtonDidLogOut(_ loginButton: FBSDKLoginButton!) {
print("Did log out of facebook")
}
func loginButton(_ loginButton: FBSDKLoginButton!, didCompleteWith result: FBSDKLoginManagerLoginResult!, error: Error!) {
if error != nil {
print(error)
return
}
print("Successfully logged in")
FBSDKGraphRequest(graphPath: "/me", parameters: ["fields": "id, name, email"]).start {(connection, result, err) in
if err != nil {
print("Failed to start graph request", err)
return
} else {
guard let data = result as? [String:Any] else {return}
let fbEmail = data["email"]
let fbName = data["name"]
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as? CreateAccountVC
vc!.email.text = self.fbEmail
vc!.fullname.text = self.fbName
}
}
print(result)
}
performSegue(withIdentifier: "regSegue", sender: RegisterVC.self)
}
And these are the text boxes on the next screen:
import UIKit
class CreateAccountVC: UIViewController {
#IBOutlet weak var fullname: UITextField!
#IBOutlet weak var username: UITextField!
#IBOutlet weak var age: UITextField!
#IBOutlet weak var email: UITextField!
#IBOutlet weak var verifyEmail: UITextField!
#IBOutlet weak var password: UITextField!
#IBOutlet weak var verifyPassword: UITextField!
All the code above presents me with the registration page, but the text boxes are empty and not populated with the Facebook data. I'm not a great coder and really and help would be useful. Let me know if you have any solutions! Thanks.

The problem exists in this snippet:
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as? CreateAccountVC
vc!.email.text = self.fbEmail
vc!.fullname.text = self.fbName
CreateAccountVCs textfields have not been created at the moment when their .text attribute is being updated here, the textfields are nil at this point in time.
Evaluate passing the String objects retrieved from Graph API directly to CreateAccountVC & then using them to update the textfields.
Consider morphing existing implementation to something on these lines:
class CreateAccountVC: UIViewController {
var fbName:String!
var fbEmail:String!
#IBOutlet weak var fullname: UITextField!
#IBOutlet weak var username: UITextField!
#IBOutlet weak var age: UITextField!
...
The snippet mentioned above here would change to:
func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let vc = segue.destination as? CreateAccountVC
vc!.fbName = self.fbName
vc!.fbEmail = self.fbEmail
...

Related

The data I entered in the TextField does not transfer to another label

Hello guys can you help me, I have an app that has two ViewController and in the first VC I have four empty TextField and at the second VC I have four empty Labels that should receive new information and show I the label but my code doesn't work so could you help with this problem, I think something not right with my personalData
import UIKit
class ViewController: UIViewController {
#IBOutlet weak var name: UITextField!
#IBOutlet weak var age: UITextField!
#IBOutlet weak var city: UITextField!
#IBOutlet weak var mail: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
let tap = UITapGestureRecognizer(target: self, action: #selector(self.dismissKeyboard))
view.addGestureRecognizer(tap)
}
#objc func edit() {
print("Edit is done")
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard segue.identifier == "personalData" else { return }
guard let destination = segue.destination as? SecondViewController else { return }
destination.personalData = name.text ?? ""
destination.personalData = age.text ?? ""
destination.personalData = city.text ?? ""
destination.personalData = mail.text ?? ""
}
#objc func dismissKeyboard() {
view.endEditing(true)
}
}
//////////////////////////////////////
import UIKit
class SecondViewController: UIViewController {
struct User{
}
var personalData = ""
override func viewDidLoad() {
super.viewDidLoad()
firstProfileLabel.text = personalData
secondProfileLabel.text = personalData
thirdProfileLabel.text = personalData
lastProfileLabel.text = personalData
print("SecondVC", #function)
navigationItem.rightBarButtonItem = UIBarButtonItem(barButtonSystemItem: .edit,
target: self,
action: #selector(edit))
}
#objc func edit() {
print("Edit is done")
}
#IBOutlet weak var firstProfileLabel: UILabel!
#IBOutlet weak var secondProfileLabel: UILabel!
#IBOutlet weak var thirdProfileLabel: UILabel!
#IBOutlet weak var lastProfileLabel: UILabel!
}
My mentor said that "The problem is with the variable personalData. The variable is of the stripe type and can store only one value.
If you want to pass values through a variable and not directly, you can create a structure, e.g. User with variables Name, Age, City, etc., and make personalData a User type and empty array."
But I don't understand how exactly I should write it in code.
Start simple. Give your second view controller separate properties for each value you want to pass:
class SecondViewController: UIViewController {
var name: String
var age: String
var city: String
var mail: String
}
Then in your first view controller's perpare(for:) method, set each of those variables separately:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
guard segue.identifier == "personalData" else { return }
guard let destination = segue.destination as? SecondViewController else { return }
destination.name = name.text ?? ""
destination.age = age.text ?? ""
destination.city = city.text ?? ""
destination.mail = mail.text ?? ""
}
And rewrite your second view controller's viewDidLoad method to install each property into the correct field.
Once you've got that working, you can figure out how to instead pass all the string values in a single structure.
Hint:
Create a struct called something like UserInfo:
struct UserInfo {
let name: String
let age: String
let city: String
let mail: String
}
And then give your second view controller a property of type UserInfo, and set that in prepare(for:)

Delegate is still nil

I am passing user data from a view controller called CreateNewAccount to another view controller called ThanksForJoining. For some reason my delegate is nil. I am using a segue in order to set my vc.delegate to self (= self) and the segue identifier "thanksForJoining" refers to the segue that connects CreateNewAccount to ThanksForJoining on the storyboard. Somehow though, the delegate remains nil.
CreateNewAccount:
import UIKit
protocol UserInfoDelegate {
func sendUserInfo(firstName: String, lastName: String, username: String, password: String)
}
class CreateNewAccount: UIViewController{
#IBOutlet weak var FNInput: UITextField!
#IBOutlet weak var LNInput: UITextField!
#IBOutlet weak var usernameInput: UITextField!
#IBOutlet weak var passwordInput: UITextField!
var infoDelegate: UserInfoDelegate?
#IBAction func sendInfo(_ sender: Any) {
if(infoDelegate != nil){
if(FNInput.text != nil && LNInput.text != nil && usernameInput.text != nil && passwordInput.text != nil){
let firstName = FNInput.text
let lastName = LNInput.text
let username = usernameInput.text
let password = passwordInput.text
infoDelegate?.sendUserInfo(firstName: firstName!, lastName: lastName!, username: username!, password: password!)
}
}
}
}
ThanksforJoining:
import UIKit
class ThanksForJoining: UIViewController, UserInfoDelegate {
#IBOutlet weak var fName: UILabel!
func sendUserInfo(firstName: String, lastName: String, username: String, password: String) {
print(firstName)
fName.text = firstName
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if (segue.identifier == "thanksForJoining") {
let createNewAccount: CreateNewAccount = segue.destination as! CreateNewAccount
createNewAccount.infoDelegate = self
}
}
}
First of all, you need to confirm that:
You connected CreateNewAccount to ThanksForJoining via a segue.
The segue's Identifier is set to thanksForJoining correctly
(Be careful about the letter cases.)
If any of the two is not true, you have lost a little time and I have lost my time to prepare for a big typhoon. Update your question to clarify what's happening and wait for someone to help you...
Assuming two things above, prepare(for:sender:) is called on the source view controller. You need to implement it in your CreateNewAccount class.
CreateNewAccount:
import UIKit
class CreateNewAccount: UIViewController {
#IBOutlet weak var firstNameInput: UITextField!
#IBOutlet weak var lastNameInput: UITextField!
#IBOutlet weak var usernameInput: UITextField!
#IBOutlet weak var passwordInput: UITextField!
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
if segue.identifier == "thanksForJoining" {
let destinationVC = segue.destination as! ThanksForJoining
if
let firstName = firstNameInput.text, !firstName.isEmpty,
let lastName = lastNameInput.text, !lastName.isEmpty,
let username = usernameInput.text, !username.isEmpty,
let password = passwordInput.text, !password.isEmpty
{
destinationVC.receiveUserInfo(firstName: firstName, lastName: lastName, username: username, password: password)
}
}
}
}
ThanksForJoining:
import UIKit
class ThanksForJoining: UIViewController {
var firstName: String?
#IBOutlet weak var firstNameLabel: UILabel!
override func viewWillAppear(_ animated: Bool) {
super.viewWillAppear(animated)
firstNameLabel.text = firstName
}
func receiveUserInfo(firstName: String, lastName: String, username: String, password: String) {
print(firstName)
self.firstName = firstName
}
}
Seems delegate pattern is sort of too much for your purpose and you just need to define a data passing method in the destination view controller ThanksForJoining.
I assume you have connected your segue from some button of your CreateNewAccount. If the segue is connected from the view controller (not from a button), the code above needs small modification.
But anyway, in your original code, the method prepare(for:sender:) in ThanksForJoining would never be called, so the delegate would never be set. Thus, the delegate remains nil.
First you need a reference to the CreateNewAccount class in the class ThanksForJoining class. Then you need to activate the delegate by setting it equal to self in the ThanksForJoing class in viewDidLoad.
class ThanksForJoining: UIViewController, UserInfoDelegate {
var createNewAccount: CreateNewAccount?
override func viewDidLoad() {
super.viewDidLoad()
createNewAccount?.infoDelegate = self
}
{
Then your delegate methods will work.
The only possible issue is segue identifier mismatch in code and storyboard
In the story board, select the segue between two VCs
And then go the attributes inspector and set the ID "thanksForJoining" in "Identifier" field
Some suggestions
If your intention is to check whether user has entered all the fields before sending the data back, then this code will serve the purpose better
if (infoDelegate != nil) {
if let firstName = FNInput.text, firstName.count>0,
let lastName = LNInput.text, lastName.count>0,
let username = usernameInput.text, username.count>0,
let password = passwordInput.text, password.count>0 {
infoDelegate?.sendUserInfo(firstName: firstName, lastName: lastName, username: username, password: password)
}
}

data not showing up in firebase database

im attempting to add user data via a create account view controller which contains all UITextFields (password, confirm password, first name, last name, phone number). when the create account button is tapped, the users email shows up in the authentication section on the firebase website but the user information from the first name, last name and phone number text fields are not passed into the database. I'm new to iOS development and have never used firebase so im unsure what the issue is. the app runs without crashing.
below is my Create Account view controller
thanks in advance
import UIKit
import FirebaseAuth
import QuartzCore
import FirebaseDatabase
import Firebase
class CreateAccount: UIViewController {
var refUsers: DatabaseReference!
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
#IBOutlet weak var confirmPasswordTextField: UITextField!
#IBOutlet weak var firstNameTextField: UITextField!
#IBOutlet weak var lastNameTextField: UITextField!
#IBOutlet weak var phoneNumberTextField: UITextField!
#IBOutlet weak var alreadyHaveAccountLabel: UILabel!
#IBAction func loginButtonTapped(_ sender: Any) {
performSegue(withIdentifier: "showLoginScreen", sender: self)
}
override func viewDidLoad() {
super.viewDidLoad()
self.refUsers = Database.database().reference().child("Users");
// Do any additional setup after loading the view.
}
override func viewDidAppear(_ animated: Bool) {
if Auth.auth().currentUser != nil {
print("success")
self.presentMainScreen()
}
}
#IBAction func createAccountTapped(_ sender: Any) {
if let email = emailTextField.text, let password = passwordTextField.text {
Auth.auth().createUser(withEmail: email, password: password, completion:{ user, error in
if let firebaseError = error {
print(firebaseError.localizedDescription)
return
} else {
self.addUser()
print("this is the first name:", self.firstNameTextField.text!)
print("this is the last name:", self.lastNameTextField.text!)
print("this is the phone number" , self.phoneNumberTextField.text!)
print("success")
self.presentMainScreen()
}
})
}
}
func addUser(){
let key = refUsers.childByAutoId().key
let user = ["id":key,
"FirstName":firstNameTextField.text! as String,
"LastName":lastNameTextField.text! as String,
"PhoneNumber":phoneNumberTextField.text! as String
]
refUsers.child(key).setValue(user)
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
/*
// 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.
}
*/
func presentMainScreen(){
let mainstoryboard = UIStoryboard(name: "Main", bundle: nil)
let mainTabController = mainstoryboard.instantiateViewController(withIdentifier: "MainTabController") as! MainTabController
mainTabController.selectedViewController = mainTabController.viewControllers?[0]
self.present(mainTabController, animated: true, completion: nil)
//let storyboard:UIStoryboard = UIStoryboard(name:"Main", bundle:nil)
//let loggedInVC:LoggedInVC = storyboard.instantiateViewController(withIdentifier: "LoggedInVC") as! LoggedInVC
//self.present(loggedInVC, animated: true, completion: nil)
}
}
Try this:
Instead of set value use update value
let childUpdates = ["/user/\(key)": user]
refUser.updateChildValues(childUpdates)
Hope this helps :)

Unresolved identifier using segue when passing data

In my app I am using segue to pass data between two viewcontrollers and that should be easy enough, but for som reason I can`t see there I keep getting "Unresolved Identifier"
Her are some of the code that has to do with that function.
from ViewController 1
import UIKit
import CoreData
class ViewController: UIViewController, UITextFieldDelegate
{
#IBOutlet var panelWidthTextField: UITextField!
#IBOutlet var panelHightTextField: UITextField!
#IBOutlet var panelsWideTextField: UITextField!
#IBOutlet var panelsHightTextField: UITextField!
#IBOutlet var panelPitchTextField: UITextField!
#IBOutlet var calculateButton: UIButton!
#IBOutlet var resultWithLabel: UILabel!
#IBOutlet var resultHightLabel: UILabel!
#IBOutlet var fillAllFieldsLabel: UILabel!
var pawidth:String!
var pahight:String!
var papitch:String!
override func viewDidLoad()
{
super.viewDidLoad()
panelWidthTextField.text = pawidth
panelHightTextField.text = pahight
panelPitchTextField.text = pap itch
From Second ViewController
import UIKit
import CoreData
class DataBase: UIViewController, UITextFieldDelegate
{
#IBOutlet var makerTextField: UITextField!
#IBOutlet var modelTextField: UITextField!
#IBOutlet var stPanelWidthTextField: UITextField!
#IBOutlet var stPanelHightTextField: UITextField!
#IBOutlet var stPitchTextField: UITextField!
let moc = (UIApplication.sharedApplication().delegate as! AppDelegate).managedObjectContext
// Removes keyboard when touch outside edit field.
override func touchesBegan(touches: Set<UITouch>, withEvent event: UIEvent?)
{
view.endEditing(true)
super.touchesBegan(touches, withEvent: event)
}
#IBAction func saveButton(sender: UIButton)
{
let ed = NSEntityDescription.entityForName("Ledinfo", inManagedObjectContext: moc)
let model = Ledinfo(entity:ed!, insertIntoManagedObjectContext:moc)
model.manufactor = makerTextField.text
model.model = modelTextField.text
model.panelwidth = stPanelWidthTextField.text
model.panelhight = stPanelHightTextField.text
model.pitch = stPitchTextField.text
do {
try moc.save()
makerTextField.text = ""
modelTextField.text = ""
stPanelWidthTextField.text = ""
stPanelHightTextField.text = ""
stPitchTextField.text = ""
Alert.show("Succsess", message: "Your Record Is Saved", vc: self)
}
catch _ as NSError
{
Alert.show("Failed", message: "Something Went Wrong", vc: self)
}
}
#IBAction func searchButton(sender: UIButton)
{
let ed = NSEntityDescription.entityForName("Ledinfo", inManagedObjectContext: moc)
let req = NSFetchRequest()
req.entity = ed
let cond = NSPredicate(format: "manufactor = %#", makerTextField.text!)
req.predicate = cond
do {
let result = try moc.executeFetchRequest(req)
if result.count > 0
{
let model = result[0] as! Ledinfo
makerTextField.text = model.manufactor
modelTextField.text = model.model
stPanelWidthTextField.text = model.panelwidth
stPanelHightTextField.text = model.panelhight
stPitchTextField.text = model.pitch
} else
{
Alert.show("Failed", message: "No Record Is Found", vc: self)
}
} catch _ as NSError!
{
Alert.show("Failed", message: "No Record Is Found" , vc: self)
}
}
#IBAction func transfereButton(sender: UIButton) {
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject!) {
if (segue.identifier == "transfereButton") {
let svc = segue.destinationViewController as! ViewController
svc.pawidth = stPanelWidthTextField.text
svc.pahight = stPanelHightTextField.text
svc.papitch = stPitchTextField.text
}
}
}
It can not find panelWidthTextField.text, panelHightTextField.text and panelPitchTextField.text as identifier.
I have check spelling and just can`t seem to be able to find what is missing.
Any help is appreciated
"Segue" means, that in "prepareForSegue" method you set the property of ViewController to some data in your DataBase controller. In your example, this can be done like this:
svc.pawidth = someDataFromDataBaseWhichYouWantToPassToSecondVC
svc.pahight = someDataFromDataBaseWhichYouWantToPassToSecondVC
svc.papitch = someDataFromDataBaseWhichYouWantToPassToSecondVC
And then, you can manipulate this data from your ViewController class.
You mistake that you are not passing the data from one VC to another, instead of that you are trying to set the property of 1stVC to another property of 1stVC, and there is no segue needed.
This has nothing to do with segues. do you have 3 text fields in your DataBase class with names panelWidthTextField, panelHightTextField and panelPithcTextField? It's complaining about not being able to find those variables.
You should call the performSegueWithIdentifier("transfereButton", sender: nil) inside your transfereButton IBOutlet action to actually make the prepareForSegue to run.

How to call performSegueWithIdentifier in Swift

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.

Resources