Facebook SDK swift: problems with fetching user data (segue) - ios

It does not seem like my data is passed to the new view controller.
Indeed, it only works in the (first) view controller once logged in.
I believe there are a few mistakes in my work.
Please tell me where I'm wrong, I am a beginner so please be precise and efficient. Thanks a lot.
class ViewController: UIViewController, FBSDKLoginButtonDelegate
{
override func viewDidLoad()
{
super.viewDidLoad()
if FBSDKAccessToken.currentAccessToken() != nil {
//user already has access token
self.logUserData()
} else {
loginButton.readPermissions = ["public_profile", "email", "user_friends"]
loginButton.delegate = self
self.Photo.image = UIImage(named: "Why subtle bckd image1")
self.Photo2.image = UIImage(named: "Un combo unique pict")
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
// MARK: PR/VAR
var firstName: String!
var lastName: String!
var email: String!
#IBOutlet var loginButton: FBSDKLoginButton!
#IBAction func loginButtonsend(sender: UIButton) {
sender.setTitle("firstName", forState: UIControlState.Normal)
sender.setTitle("lastName", forState: UIControlState.Normal)
sender.setTitle("email", forState: UIControlState.Normal)
}
#IBOutlet var Photo: UIImageView!
#IBOutlet var Photo2: UIImageView!
// MARK: FACEBOOK LOGIN
override func viewWillAppear(animated: Bool) {
self.logUserData()
}
func loginButton(loginButton: FBSDKLoginButton!, didCompleteWithResult result: FBSDKLoginManagerLoginResult!, error: NSError!)
{ if error == nil {
let fbAccessToken = FBSDKAccessToken.currentAccessToken().tokenString
println("Logged in")
} else { println(error.localizedDescription) } }
func loginButtonDidLogOut(loginButton: FBSDKLoginButton!)
{
println("User logged out.")
}
func logUserData()
{
let graphRequest = FBSDKGraphRequest(graphPath: "me?fields=id,email,first_name,last_name", parameters: ["fields": "first_name,email,last_name"])
graphRequest.startWithCompletionHandler { (connection, result, error) -> Void in
if error != nil
{
// Process error
println(error)
}
else
{
println(result.grantedPermissions)
println("fetched user = \(result)")
var firstName = result.valueForKey("first_name")
println("firstName = \(firstName)")
var lastName = result.valueForKey("last_name")
println("lastName is = \(lastName)")
var email = result.valueForKey("email")
println("email is = \(email)")
self.performSegueWithIdentifier("showNew", sender: self)
}
}
}
// SEGUE:
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
if (segue.identifier == "showNew") {
if let destinationVC = segue.destinationViewController as? NewViewController {
destinationVC.firstName = firstName
destinationVC.lastName = lastName
destinationVC.email = email
}
}
}
}
And in my new view controller I have the following:
class NewViewController: UIViewController {
var firstName: String!
var lastName: String!
var email: String!
#IBOutlet var firstNameLabel: UILabel!
#IBOutlet var lastNameLabel: UILabel!
#IBOutlet var emailLabel: UILabel!
override func viewWillAppear(animated: Bool) {
super.viewWillAppear(animated)
if (firstName != nil) {
firstNameLabel.text = firstName
}
if (lastName != nil) {
lastNameLabel.text = lastName
}
if (email != nil) {
emailLabel.text = email
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func logoutButtonTapped(sender: AnyObject) {
let loginManager = FBSDKLoginManager ()
loginManager.logOut()
let loginPage = self.storyboard?.instantiateViewControllerWithIdentifier("ViewController") as ViewController
//?
let loginPageNav = UINavigationController (rootViewController: loginPage)
let appDelegate = UIApplication.sharedApplication().delegate as AppDelegate
appDelegate.window?.rootViewController = loginPageNav
}
/*
// MARK: - Navigation
// In a storyboard-based application, you will often want to do a little preparation before navigation
override func prepareForSegue(segue: UIStoryboardSegue!, sender: AnyObject!) {
// Get the new view controller using segue.destinationViewController.
// Pass the selected object to the new view controller.
}
*/
}

It seems like you're never setting the variables from your ViewController, but instead are creating three which have the same name as the ones you should be using on logUserData. Just change to:
println(result.grantedPermissions)
println("fetched user = \(result)")
firstName = result.valueForKey("first_name")
println("firstName = \(firstName)")
lastName = result.valueForKey("last_name")
println("lastName is = \(lastName)")
email = result.valueForKey("email")
println("email is = \(email)")

Related

How can I pass dictionary from one view controller class to another?SWIFT

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")
}
}
}

User variable is nil when creating a user with Firebase

I am following a tutorial and cannot seem to register my user as the user variable in the Firebase .createUser method appears to be nil. Therefore, when I unwrap it, I get an error.
I have read through a lot of the documentation as well as checked many other questions similar to mine but nothing seems to work
import UIKit
import Firebase
import SwiftKeychainWrapper
class ViewController: UIViewController {
#IBOutlet weak var userImgView: UIImageView!
#IBOutlet weak var usernameField: UITextField!
#IBOutlet weak var emailField: UITextField!
#IBOutlet weak var passwordField: UITextField!
var imagePicker: UIImagePickerController!
var selectedImage: UIImage!
override func viewDidLoad() {
super.viewDidLoad()
imagePicker = UIImagePickerController()
imagePicker.allowsEditing = true
imagePicker.delegate = self
}
override func viewDidAppear(_ animated: Bool) {
if let _ = KeychainWrapper.standard.string(forKey: "uid") {
self.performSegue(withIdentifier: "toFeed", sender: nil)
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
func setupUser(userUid: String) {
if let imageData = self.userImgView.image!.jpegData(compressionQuality: 0.2) {
let imgUid = NSUUID().uuidString
let metaData = StorageMetadata()
Storage.storage().reference().child(imgUid).putData(imageData, metadata: metaData) { (metadata, error) in
let downloadURL = metadata
let userData = [
"username": self.usernameField.text!,
"userImg": downloadURL!
] as [String : Any]
Database.database().reference().child("users").child(userUid).setValue(userData)
self.performSegue(withIdentifier: "toFeed", sender: nil)
}
}
}
#IBAction func signInPressed(_ sender: Any) {
if let email = emailField.text, let password = passwordField.text {
Auth.auth().signIn(withEmail: email, password: password) { user, error in
if error != nil && !(self.usernameField.text?.isEmpty)! {
Auth.auth().createUser(withEmail: email, password: password) { (user, error) in
self.performSegue(withIdentifier: "toFeed", sender: nil)
let userID = (user?.user.uid)!
self.setupUser(userUid: userID)
KeychainWrapper.standard.set(userID, forKey: "uid")
}
} else {
if let userID = (user?.user.uid) {
KeychainWrapper.standard.set((userID), forKey: "uid")
self.performSegue(withIdentifier: "toFeed", sender: nil)
}
}
}
}
}
#IBAction func getPhoto (_ sender: AnyObject) {
present(imagePicker, animated: true, completion: nil)
}
}
extension ViewController: UIImagePickerControllerDelegate, UINavigationControllerDelegate {
internal func imagePickerController(_ picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey : Any]) {
if let image = info[.originalImage] as? UIImage {
userImgView.image = image
} else {
print("image wasnt selected")
}
imagePicker.dismiss(animated: true, completion: nil)
}
}
The error I am getting is one the "let userID = (user?.user.uid)!". It is
Thread 1: EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0)
The completion block for createUser(withEmail:,password:) gets called with either a AuthResult.user or an error. That why, as Joshua commented, you should check if error is nil before accessing any of the user properties.
From the auth quickstart for Swift:
Auth.auth().createUser(withEmail: email, password: password) { authResult, error in
strongSelf.hideSpinner {
guard let user = authResult?.user, error == nil else {
strongSelf.showMessagePrompt(error!.localizedDescription)
return
}
print("\(user.email!) created")
strongSelf.navigationController?.popViewController(animated: true)
}
}

Add elements to search history?

I have a model - Movies.
and two controllers - first for search movie by title, second - for display result with poster, title and year.
Now i need to create some history search on my third controller
(searchHistoryController - TableView) where displayed all movies, and when i tapped on cell with movie's title show movie info.
How I can build it?
I tried create array in my model. And write resutl in it, but each time when i use search it rewrite array, not add new element.
Maybe use realm
Need some help:)
Movie.swift
import Foundation
import UIKit
import Alamofire
import AlamofireImage
protocol MovieDelegate {
func updateMovieInfo()
}
class Movie {
private let omdbUrl = "http://www.omdbapi.com/?"
var title: String?
var filmYear: String?
var poster: String?
var delegete: MovieDelegate!
var historyMovie = [Movie]()
func getMovieInfo(title: String, completion: #escaping ()->()){
let params = ["t": title]
Alamofire.request(omdbUrl, method: .get, parameters: params).validate(statusCode: 200..<300).validate(contentType: ["application/json"]).responseJSON { (response) in
switch response.result {
case .success(let JSON):
let response = JSON as! NSDictionary
let status = response["Response"] as! String
if status == "True" {
self.title = (response["Title"] as! String)
self.filmYear = (response["Year"] as! String)
self.poster = (response["Year"] as! String)
// self.delegete.updateMovieInfo()
completion()
} else {
self.title = (response["Error"] as! String)
completion()
}
case .failure(let error):
print (error)
}
}
}
}
SearchVC
import UIKit
class SearchViewController: UIViewController {
var movie = Movie()
#IBOutlet weak var activityIndicator: UIActivityIndicatorView!
#IBOutlet weak var searchTextField: UITextField!
#IBOutlet weak var searchButton: UIButton!
#IBAction func searchButtonTapped(_ sender: UIButton) {
activityIndicator.startAnimating()
DispatchQueue.main.async {
self.movie.getMovieInfo(title: self.searchTextField.text!, completion: {
self.activityIndicator.stopAnimating()
self.performSegue(withIdentifier: "movieInfo", sender: self)
})
}
}
override func viewDidLoad() {
super.viewDidLoad()
}
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let secondVC = segue.destination as! DetailInfoViewController
secondVC.movieTitle = movie.title!
}
}
DetailVC
class DetailInfoViewController: UIViewController, MovieDelegate {
#IBAction func showHistory(_ sender: UIButton) {
performSegue(withIdentifier: "showHistory", sender: self)
}
#IBOutlet weak var posterImageView: UIImageView!
#IBOutlet weak var filmNameLabel: UILabel!
#IBOutlet weak var filmYearLabel: UILabel!
var movie = Movie()
var movieTitle = ""
override func viewDidLoad() {
super.viewDidLoad()
self.movie.getMovieInfo(title: movieTitle ) {
self.updateMovieInfo()
}
self.movie.delegete = self
}
func updateMovieInfo() {
getPoster(link: movie.poster)
filmNameLabel.text = movie.title
filmYearLabel.text = movie.filmYear
}
func getPoster(link: String?) {
if link != nil {
guard let url = URL(string: link!) else { return }
DispatchQueue.global().async {
if let data = try? Data(contentsOf: url) {
DispatchQueue.main.async {
self.posterImageView.image = UIImage(data: data)
}
}
} } else {
self.posterImageView.image = #imageLiteral(resourceName: "Image")
}
}
}
First of all, movieHistory should not be part of your Movie class, but part of your SearchViewController class.
Second of all, unless you want to persist your data, you don't need Realm for this.
Just save the movies in SearchViewController into an array once the search button has been tapped and send it to your other view controller in the segue. Like so
#IBAction func searchButtonTapped(_ sender: UIButton) {
activityIndicator.startAnimating()
DispatchQueue.main.async {
self.movie.getMovieInfo(title: self.searchTextField.text!, completion: {
self.activityIndicator.stopAnimating()
movieHistory.append(movie)
self.performSegue(withIdentifier: "movieInfo", sender: movieHistory)
})
}
}
Also, modify prepare(for segue:...) like this:
override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
let secondVC = segue.destination as! DetailInfoViewController
secondVC.movieTitle = movie.title!
secondVC.movieHistory = movieHistory
}
In detailVC override prepare(for segue:...) as well and send movieHistory to searchHistoryController the same way it is done in the previous VC.

Swift - Login with FireBase - Can't retrieve user data after login

Firebase Authentication - Can't retrieve user data after login.
I'm trying to retrieve the users email after sign in (logincontroller) and display it in a UILABEL on another controller (maincontroller). When you create a user or use an existing one, it works fine the first time after launching the app, but when you sign out and try to use another email it does not work.
I have two view controllers:
import UIKit
import Firebase
import FirebaseAuth
import FirebaseDatabase
class LoginViewController: UIViewController {
var ref: FIRDatabaseReference!
#IBOutlet weak var emailTextField: UITextField!
#IBOutlet weak var passwordTextField: UITextField!
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference()
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
#IBAction func signInButton(sender: AnyObject) {
FIRAuth.auth()?.signInWithEmail(emailTextField.text! , password: passwordTextField.text!, completion: { (user, error) in
if error != nil {
print(error!.localizedDescription)
}
})
self.performSegueWithIdentifier("MainViewSegue", sender: self)
}
#IBAction func createAccountButton(sender: AnyObject) {
FIRAuth.auth()?.createUserWithEmail(emailTextField.text!, password: passwordTextField.text!, completion: { (user, error) in
if error != nil {
print(error!.localizedDescription)
} else {
print("User Created.")
let userID: String = user!.uid
let userEmail:String = self.emailTextField.text!
self.ref.child("users").child(userID).setValue(["email": userEmail])
}
})
}
}
and:
import Foundation
import UIKit
import Firebase
import FirebaseDatabase
class MainViewController: UIViewController {
var ref: FIRDatabaseReference!
var refHandle: UInt!
#IBOutlet weak var userEmailLabel: UILabel!
override func viewDidLoad() {
super.viewDidLoad()
ref = FIRDatabase.database().reference()
refHandle = ref.observeEventType(FIRDataEventType.Value, withBlock: { (snapshot) in
let dataDict = snapshot.value as! [String: AnyObject]
print((dataDict))
})
let userID: String = (FIRAuth.auth()?.currentUser?.uid)!
ref.child("users").child(userID).observeSingleEventOfType(.Value, withBlock: { (snapshot) in
let userEmail = snapshot.value!["email"] as! String
self.userEmailLabel.text = userEmail
})
}
#IBAction func signOutButton(sender: AnyObject) {
try! FIRAuth.auth()!.signOut()
if let storyboard = self.storyboard {
let viewController = storyboard.instantiateViewControllerWithIdentifier("LoginViewController")
self.presentViewController(viewController, animated: false, completion: nil)
}
}
}
This is where I get the error EXC_BAD_INSTRUCTION:
let userID: String = (FIRAuth.auth()?.currentUser?.uid)!
CMD+CLICK on signInWithEmail in FIRAuth.auth()?.signInWithEmail(emailT... and you will be directed to its documentation, above that function in its documentation you will see :-
#param completion Optionally; a block which is invoked when the sign in flow finishes, or is
canceled. Invoked asynchronously on the main thread in the future.
which means your completionBlock is invoked when your user is either signed in or signInWithEmail function has given some error.But in your case self.performSegueWithIdentifier("MainViewSegue", sender: self) will get called even before your completionBlock: is called.
Try this:-
#IBAction func signInButton(sender: AnyObject) {
FIRAuth.auth()?.signInWithEmail(emailTextField.text! , password: passwordTextField.text!, completion: { (user, error) in
if error != nil {
print(error!.localizedDescription)
}else if error == nil{
self.performSegueWithIdentifier("MainViewSegue", sender: self)
}
})
}
Also replace :-
let userID: String = (FIRAuth.auth()?.currentUser?.uid)!
with
let userID: String = FIRAuth.auth()!.currentUser!.uid
No need to forcefully unwrap an optional value which you know does exist

Changing ViewController if user is Facebook Logged in

I'm having quite some troubles implementing Facebook login in my iOS , everything works fine if the user is not already logged in, the application fetches correctly the data from Facebook and pass them to the next ViewController , instead if is already logged in it should automatically segue to a recap page that shows user's info but i can't make it happen, currently I'm using this method :
override func viewDidLoad() {
super.viewDidLoad()
LoginButton.delegate = self
if (FBSDKAccessToken.currentAccessToken() != nil) {
self.performSegueWithIdentifier("Login", sender: self)
}
}
but in the console i get :
Facebook_Login.LoginViewController: 0x7fc04a519ca0 on Facebook_Login.ViewController: 0x7fc04a41c1e0 whose view is not in the window hierarchy!
i've also tried using the viewdidAppear method, but it segues to the recap page without updating the variables so i get an empty page
here' the complete code:
View Controller 1
import UIKit
import FBSDKLoginKit
class ViewController: UIViewController,FBSDKLoginButtonDelegate {
var nome1:String = ""
var cognome1:String = ""
var email1:String = ""
var compleanno:String = ""
var città:String = ""
var genere:String = ""
var immagine_url:String = ""
#IBOutlet weak var LoginButton: FBSDKLoginButton!
#IBAction func LoginAction(sender: AnyObject) {
LoginButton.delegate = self
LoginButton.readPermissions = ["email"]
}
func FetchInfo(){
print("scarico le informazioni...")
let parametri = ["fields":"email, first_name, last_name, birthday, hometown, gender, picture.type(large)"]
FBSDKGraphRequest(graphPath: "me", parameters: parametri).startWithCompletionHandler{(connection,result,error) -> Void in
if (error != nil){
print ("errore")
return
}
else {
if let email = result["email"] as? String {
print(email)
self.email1 = email
}
if let nome = result["first_name"] as? String {
print(nome)
self.nome1 = nome
}
if let cognome = result["last_name"] as? String {
print(cognome)
self.cognome1 = cognome
}
if let compleanno = result["birthday"] as? String{
print(compleanno)
}
if let città = result["hometown"] as? String{
print(città)
}
if var genere = result["gender"] as? String{
if (genere == "male"){
genere = "maschio"
}
else {
genere = "femmina"
}
print(genere)
}
}
if let picture = result["picture"] as? NSDictionary, data = picture["data"] as? NSDictionary, url = data["url"] as? String{
self.immagine_url = url
print(self.immagine_url)
}
}
return
}
func loginButton(loginButton: FBSDKLoginButton!, didCompleteWithResult result: FBSDKLoginManagerLoginResult!, error: NSError!){
if (result.isCancelled == true){
print("cancellato")
}
else {
print("login effettuato")
FetchInfo()
self.performSegueWithIdentifier("Login", sender: self)
}
}
func loginButtonDidLogOut(loginButton: FBSDKLoginButton!){
}
func loginButtonWillLogin(loginButton: FBSDKLoginButton!) -> Bool{
return true
}
#IBAction func returned(segue:UIStoryboardSegue){
}
override func viewDidLoad() {
super.viewDidLoad()
LoginButton.delegate = self
}
override func viewDidAppear(animated: Bool) {
if (FBSDKAccessToken.currentAccessToken() != nil) {
FetchInfo()
if(nome1 == ""){
}
else {
self.performSegueWithIdentifier("Login", sender: self)
}
}
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
let destinazione:LoginViewController = segue.destinationViewController
as! LoginViewController
destinazione.temp_nome = nome1
destinazione.temp_cognome = cognome1
destinazione.temp_email = email1
destinazione.img_profile_url = immagine_url
}
}
ViewController 2:
import UIKit
import FBSDKLoginKit
class LoginViewController: UIViewController, FBSDKLoginButtonDelegate {
var temp_nome = ""
var temp_cognome = ""
var temp_email = ""
var img_profile_url:String = ""
#IBOutlet weak var Nome_Utente: UILabel!
#IBOutlet weak var email: UILabel!
#IBOutlet weak var Immagine_Utente: UIImageView!
#IBOutlet weak var Login_button: FBSDKLoginButton!
#IBAction func Login_button_Action(sender: AnyObject) {
Login_button.delegate = self
}
func loginButton(loginButton: FBSDKLoginButton!, didCompleteWithResult result: FBSDKLoginManagerLoginResult!, error: NSError!){
}
func loginButtonDidLogOut(loginButton: FBSDKLoginButton!){
self.performSegueWithIdentifier("Back", sender: self)
}
override func viewDidLoad() {
super.viewDidLoad()
Nome_Utente.text = "\(temp_nome)" + " " + "\(temp_cognome)"
email.text = "\(temp_email)"
}
override func didReceiveMemoryWarning() {
super.didReceiveMemoryWarning()
// Dispose of any resources that can be recreated.
}
First of all, avoid to reset your LoginButton.delegate in the LoginAction() function.
If the user is already logged in, to avoid an useless call to the Facebook Graph API (if you do not necessarily need to update his informations), you can store your user informations in CoreData or in a NSUserDefault.
If you never used it you can use the CDHelper lib (CoreDataHelper) which will allow you to use it without difficulties.
Hope I was helpful, if not, do not hesitate to give us a feedback.
Btw, in your viewDidAppear(animated: Bool) function, you have to call super.viewDidAppear(animated) !

Resources