Login details remains after user logout swift using Token authentication - swift

I have an application that uses rest framework to provide API. I have the application all setup but I have a challenge in that when a user login the user's details is gotten. login is achieved via token authentication and this is stored in UserDefaults when the logout button is clicked the user details is removed from the device and login in with another user is supposed to be displayed on login i.e the logged in user,s email and username but instead it shows the initial logged in user,s details and no matter how much I try it it never displays the current logged in users details but instead it displays the initial users details.
My login code is
let defaults = UserDefaults.standard
var isLoggedIn : Bool {
get {
return defaults.bool(forKey: LOGGED_IN_KEY)
}
set {
defaults.set(newValue, forKey: LOGGED_IN_KEY)
}
}
var authToken: String {
get {
return defaults.value(forKey: TOKEN_KEY) as? String ?? ""
}
set {
defaults.set(newValue, forKey: TOKEN_KEY)
}
}
var userUsername: String {
get {
return defaults.value(forKey: USER_USERNAME) as? String ?? ""
}
set {
defaults.set(newValue, forKey: USER_USERNAME)
}
}
//MARK :- LOGGIN
func findUserByUserName(completion: #escaping CompletionHandler) -> Void {
Alamofire.request(URL_USER_BY_USERNAME, method: .get, parameters: nil, encoding: JSONEncoding.default, headers: TOKEN_HEADER).validate().responseJSON { (response) in
print("URL USER BY HEADER \(self.authToken)")
if response.result.error == nil {
guard let data = response.data else {return}
let jsonString = String(data: data, encoding: .utf8)
print(jsonString as Any)
self.setUserInfo(data: data)
completion(true)
}
else {
completion(false)
debugPrint("ERROR 22222\(response.result.error as Any)")
}
}
}
func setUserInfo(data: Data) -> Void {
do {
let json = try JSON(data: data)
let pk = json["pk"].intValue
let username = json["username"].stringValue
let email = json["email"].stringValue
let firstName = json["first_name"].stringValue
let lastName = json["last_nameme"].stringValue
print("THE USERNAME IZZZZ \(username)")
UserDataService.instance.setUserData(pk: pk, username: username, email: email, firstName: firstName, lastName: lastName)
} catch {
print(error)
}
}
UserDetails
class UserDataService {
static let instance = UserDataService()
public private(set) var pk = 0
public private(set) var username = ""
public private(set) var email = ""
public private(set) var firstName = ""
public private(set) var lastName = ""
func setUserData(pk: Int, username: String, email: String, firstName: String, lastName: String) -> Void {
self.pk = pk
self.username = username
self.email = email
self.firstName = firstName
self.lastName = lastName
}
func logoutUser() -> Void {
self.pk = 0
self.username = ""
self.email = ""
self.firstName = ""
self.lastName = ""
AuthService.instance.isLoggedIn = false
AuthService.instance.authToken = ""
AuthService.instance.userUsername = ""
}}
For my logout I have
#IBAction func logoutPressed(_ sender: Any) {
UserDataService.instance.logoutUser()
NotificationCenter.default.post(name: NOTIFY_USER_DATA_DID_CHANGE, object: nil)
dismiss(animated: true, completion: nil)
}
Part of login
func setupUserInfo() {
if AuthService.instance.isLoggedIn {
loginBtn.setTitle(UserDataService.instance.username, for: .normal)
} else {
loginBtn.setTitle("Login", for: .normal)
userImg.image = UIImage(named: "menuProfileIcon")
userImg.backgroundColor = UIColor.clear
tableView.reloadData()
}
}
Thanks in advance and further codes would be supplied on request

Related

when i second time signup it clears realtime database from firebase in swift

When I signup second time from my app It allow me sign up but when I chek In data base my old acc was removed, I really need help in this please your help would be greatful for me.
Here is my Appdelegate func
var oldToken = ""
var dToken = ""
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
FirebaseApp.configure()
Database.database().isPersistenceEnabled = true
let preferences = UserDefaults.standard
if preferences.object(forKey: "dtoken") != nil {
oldToken = preferences.object(forKey: "dtoken") as! String
}
if let uuid = UIDevice.current.identifierForVendor?.uuidString {
dToken = uuid
print(uuid)
if oldToken != dToken && oldToken != "" {
preferences.set(dToken, forKey: "dtoken")
preferences.set(oldToken, forKey: "dtoken_old")
preferences.synchronize()
print("ohoh TOKEN CHANGED!!!")
//Change Tokens in DB.
}
}
return true
}
Here is My Signup Viewcontroller
func guidancefromcoach() {
guard let email = txtEmail.text else { return }
guard let pass = txtPassword.text else { return }
Auth.auth().createUser(withEmail: email, password: pass) { result, error in
if error != nil {
self.Alert(title: "Error", message: error!.localizedDescription)
}else {
if self.currentReachabilityStatus == .notReachable {
print("There is no internet connection")
self.retryAlert()
} else {
let preferences = UserDefaults.standard
preferences.set(dToken, forKey: "dtoken")
userIsCoach = false
preferences.set(userIsCoach, forKey: "userIsCoach")
authorizedCoachCode = self.txtEnterCoachId.text!
preferences.set(authorizedCoachCode, forKey: "authorizedCoachCode")
preferences.synchronize()
let userItem = UserStruct(dToken, self.txtFName.text!, self.txtLName.text!, self.txtEmail.text!, self.txtPhone.text!, coachCode, authorizedCoachCode, userIsCoach)
self.ref.child(dToken).setValue(userItem.toAnyObject())
print("dtoken......\(dToken)")
print("saved new user")
self.performSegue(withIdentifier: "fromSignupSegue", sender: Any?.self)
}
}
}
}
Here is my USERSTRUCT
struct UserStruct {
let ref: DatabaseReference?
var token = ""
var fname = ""
var lname = ""
var email = ""
var phone = ""
var coachCode = ""
var authorizedCoachCode = ""
var isCoach = false
init(_ token:String, _ fname:String, _ lname:String, _ email:String, _ phone:String, _ coachCode:String, _ authorizedCoachCode:String, _ isCoach:Bool) {
self.ref = nil
self.token = token
self.fname = fname
self.lname = lname
self.email = email
self.phone = phone
self.coachCode = coachCode
self.authorizedCoachCode = authorizedCoachCode
self.isCoach = isCoach
}
init?(snapshot: DataSnapshot) {
guard
let value = snapshot.value as? [String: AnyObject],
let token = value["token"] as? String,
let fname = value["fname"] as? String,
let lname = value["lname"] as? String,
let email = value["email"] as? String,
let phone = value["phone"] as? String,
let coachCode = value["coachCode"] as? String,
let authorizedCoachCode = value["authorizedCoachCode"] as? String,
let isCoach = value["isCoach"] as? Bool else {
return nil
}
self.ref = snapshot.ref
//self.key = snapshot.key
self.token = token
self.fname = fname
self.lname = lname
self.email = email
self.phone = phone
self.coachCode = coachCode
self.authorizedCoachCode = authorizedCoachCode
self.isCoach = isCoach
}
func toAnyObject() -> Any {
return [
"token": token,
"fname": fname,
"lname": lname,
"email": email,
"phone": phone,
"coachCode": coachCode,
"authorizedCoachCode": authorizedCoachCode,
"isCoach": isCoach
]
}
Your help would be really greatful Thankyou in advance.
It seems that you're calling SetValue(), your old account isn't getting removed, it's getting set to different info. I'm not sure what the problem is but i suggest using this code:
Auth.auth().createUser(withEmail: email, password: pass) { (result, error) in
if err != nil {
//error code here
}
else {
guard let userID = Auth.auth().currentUser?.uid else { return }Firestore.firestore().collection("users").document(userID).setData(["email": email]) { (error) in
if error != nil {
//error happened
}
}
//Success, email has been saved to database
}
}
You can add all your other stuff to this code(userDefaults, etc.)
but this alone should be able to add a new document every time.

Swift Firebase get Data to Class Object

I want to get the following structure (screenshot of Firebase Database):
In chats I have the id of the chat. There are the users with the child userid and the values of id and name.
At first I look for the chats which a user have and want to get then the details of the chatId (users with their id and name)
I have the following class in Swift:
class Chat {
var chatId: String!
var userIds: [String]!
var userNames: [String]!
}
I have the following code to get the details, but I get not the userIds or userNames from the chatId:
func getChatsFromFirebase() {
self.ref = Database.database().reference()
self.ref?.child("users").child(userdefaults.getUserId()).child("chats").observe(.childAdded, with: { (snapshot) in
let chat = Chat()
chat.chatId = snapshot.key
chat.userIds = []
chat.userNames = []
//print(chat.chatId)
for i in 0..<self.chats.count {
let usersRef = self.ref.child("chats").child(self.chats[i].chatId).child("users").observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
for userid in value!.allKeys as! [String] {
let usersdetailsRef = self.ref.child("chats").child(self.chats[i].chatId).child("users").child(userid).queryOrdered(byChild: "name").observeSingleEvent(of: .value, with: { (snapshot) in
let value = snapshot.value as? NSDictionary
//print(value)
let id = value?["id"] as? String ?? ""
let name = value?["name"] as? String ?? ""
//print( id + ": " + name)
chat.userIds.append(id)
chat.userNames.append(name)
})
}
})
}
self.chats.append(chat)
self.tableView.reloadData()
})
}
I am very new to the Firebase topic. Can someone help me here?
Thanks.
Well You need to change your datamodel first. You dont need to store id value in , 12345 in this case. you can already fetch the key. Also, in /users/chats, you just can just save the chat id as either chat1 : IBDrbfku887BLIY or IBDrbfku887BLIY : true. You can always fetch them through value or the key respectively.
And in your chat document, you just need to reference the user id, i.e just get them and store them as user1 and user2. You can add more users if your usecase requires more.
Reconfigure your Data Model as follows.
Now You need 2 Objects Users and Chats as follows :
Users.swift
class User : NSObject {
private var _name: String!
private var _username: String!
private var _userid: String!
private var _userRef: DatabaseReference!
var name: String! {
get {
return _name
} set {
_name = newValue
}
}
var username : String! {
get {
return _username
} set {
_username = newValue
}
}
var userid: String! {
get {
return _userid
} set {
_userid = newValue
}
}
var userRef: DatabaseReference! {
get {
return _userRef
} set {
_userRef = newValue
}
}
init(userid: String, userData: Dictionary<String, Any>){
self._userid = userid
_userRef = Database.database().reference().child(_userid)
if let username = userData["username"] as? String {
self._username = username
}
if let name = userData["name"] as? String {
self._name = name
}
}
}
Chats.swift
class Chat : NSObject {
private var _chatid: String!
private var _user1: String!
private var _user2: String!
private var _chatRef: DatabaseReference!
var user1: String! {
get {
return _user1
} set {
_user1 = newValue
}
}
var user2 : String! {
get {
return _user2
} set {
_user2 = newValue
}
}
var chatid: String! {
get {
return _chatid
} set {
_chatid = newValue
}
}
var chatRef: DatabaseReference! {
get {
return _chatRef
} set {
_chatRef = newValue
}
}
init(chatid: String, chatData: Dictionary<String, Any>){
self._chatid = chatid
_chatRef = Database.database().reference().child(_chatid)
if let user = chatData["users"] as? Dictionary<String, Any> {
if let user1 = user["user1"] as? String {
self._user1 = user1
}
if let user2 = user["user2"] as? String {
self._user2 = user2
}
}
}
}
The major issue/or an overlooked issue here is the type of the data. In the /users, you id 12345 will be of type String. But when you fetch the same from /chats, it returns as Int. This downloads the value but never converts it. Always take care while seeding/testing your data.
To fetch the user's credentials just reference that through another query. This is what you can do :
var allUsers = [User]()
var allChats = [Chat]()
func viewDidLoad() {
super.viewDidLoad()
fetchAllChats()
}
func getUser(from userId: String, completion: #escaping (User) -> Void) {
Database.database().reference().child("users").child(userId).observeSingleEvent(of: .value, with: { snapshot in
if let datasnap = snapshot.value as? Dictionary<String, Any> {
let user = User(userid: userId, userData: datasnap)
completion(user)
}
})
}
func fetchAllChats() {
Database.database().reference().child("chats").observeSingleEvent(of: .value, with: { snapshot in
allChat.removeAll()
if let snapshot = snapshot.value as? Dictionary<String, Any> {
for snap in snapshot {
if let chatd = snap.value as? Dictionary<String, Any> {
let chat = Chat(chatid: snap.key, chatData: chatd)
self.allChats.append(chat)
}
}
}
// collectionview.reloadData() <--------- only if required.
})
}
func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let chatData = allChats[indexPath.row]
let cell = collectionView.dequeueReusableCell(withReuseIdentifier: CellId, for: indexPath) as! Cell
getUser(from: chatData.user1) { user in
cell.label.text = user.usernme
}
return cell
}

How do I store values ​by referring to classes in FireStore

I'm creating a chat app using the MessageKit library in Swift 4.2 and FireStore.
My problem is that I can't store data acquired by real time communication using addSnapshotListener in the Message class and confirmed the contents of the class, but I do not think it is wrong.
messageListener is working properly. When handleDocumentChange is executed, it returns nil.
I checked the return value with document.data (), but the value returned.
How do I store values ​​by referring to classes?
guard var message = Message (document: change.document) else {
print ("return Message")
return
}
The data entered for FireStore is as follows
{
"channels": [{
"MOuL1sdbrnh0x1zGuXn7": { // channel id
"name": "Puppies",
"thread": [{
"3a6Fo5rrUcBqhUJcLsP0": { // message id
"content": "Wow, that's so cute!",
"created": "May 12, 2018 at 10:44:11 PM UTC-5",
"senderID": "YCrPJF3shzWSHagmr0Zl2WZFBgT2",
"senderUsername": "naturaln0va",
"recipientProfilePictureURL":"URL",
"recipientID":"ezample",
"recipientUsername" :"A"
"recipientProfilePictureURL":"aaaaa"
},
}]
},
}]
}
That's my message class:
class Message: MessageType {
var id: String?
var sentDate: Date
var kind: MessageKind
lazy var sender: Sender = Sender(id: atcSender.uid ?? "No Id", displayName: atcSender.uid ?? "No Name")
var atcSender: User
var recipient: User
var messageId: String {
return id ?? UUID().uuidString
}
var image: UIImage? = nil
var downloadURL: URL? = nil
let content: String
init(messageId: String, messageKind: MessageKind, createdAt: Date, atcSender: User, recipient: User) {
self.id = messageId
self.kind = messageKind
self.sentDate = createdAt
self.atcSender = atcSender
self.recipient = recipient
switch messageKind {
case .text(let text):
self.content = text
default:
self.content = ""
}
}
init(user: User, image: UIImage) {
self.image = image
content = ""
sentDate = Date()
id = nil
self.kind = MessageKind.text("xxx")
self.atcSender = user
self.recipient = user
}
init?(document: QueryDocumentSnapshot) {
let data = document.data()
guard let sentDate = data["created"] as? Date else {
return nil
}
guard let senderID = data["senderID"] as? String else {
return nil
}
guard let senderUsername = data["senderUsername"] as? String else {
return nil
}
guard let senderProfilePictureURL = data["senderProfilePictureURL"] as? String else {
return nil
}
guard let recipientID = data["recipientID"] as? String else {
return nil
}
guard let recipientUsername = data["recipientUsername"] as? String else {
return nil
}
guard let recipientProfilePictureURL = data["recipientProfilePictureURL"] as? String else {
return nil
}
id = document.documentID
self.sentDate = sentDate
self.atcSender = User(uid: senderID, username: senderUsername, firstname: "", lastname: "", email: "", profileUrl: senderProfilePictureURL)
self.recipient = User(uid: recipientID, username: recipientUsername, firstname: "", lastname: "", email: "", profileUrl: recipientProfilePictureURL)
if let content = data["content"] as? String {
self.content = content
downloadURL = nil
} else if let urlString = data["url"] as? String, let url = URL(string: urlString) {
downloadURL = url
self.content = ""
} else {
return nil
}
self.kind = MessageKind.text(content)
}
required init(jsonDict: [String: Any]) {
fatalError()
}
var description: String {
return self.messageText
}
var messageText: String {
switch kind {
case .text(let text):
return text
default:
return ""
}
}
var channelId: String {
let id1 = (recipient.username ?? "")
let id2 = (atcSender.username ?? "")
return id1 < id2 ? id1 + id2 : id2 + id1
}
}
extension Message: DatabaseRepresentation {
var representation: [String : Any] {
var rep: [String : Any] = [
"created": sentDate,
"senderID": atcSender.uid ?? "",
"senderUsername": atcSender.username ?? "",
"senderProfilePictureURL": atcSender.profileUrl ?? "",
"recipientID": recipient.uid ?? "",
"recipientUsername": recipient.username ?? "",
"recipientProfilePictureURL": recipient.profileUrl ?? "",
]
if let url = downloadURL {
rep["url"] = url.absoluteString
} else {
rep["content"] = content
}
return rep
}}extension Message: Comparable {
static func == (lhs: Message, rhs: Message) -> Bool {
return lhs.id == rhs.id
}
static func < (lhs: Message, rhs: Message) -> Bool {
return lhs.sentDate < rhs.sentDate
}
}
ChatViewController:
import UIKit
import MessageKit
import MessageInputBar
import Firebase
import FirebaseFirestore
import FirebaseAuth
class ChatViewController: MessagesViewController {
private let db = Firestore.firestore()
private var reference: CollectionReference?
private var messages: [Message] = []
private var messageListener: ListenerRegistration?
private let user: User
private let channel: Channel
let uid = Auth.auth().currentUser?.uid
init(user: User, channel: Channel) {
self.user = user
self.channel = channel
super.init(nibName: nil, bundle: nil)
title = channel.name
}
required init?(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
messageListener?.remove()
}
override func viewDidLoad() {
super.viewDidLoad()
guard let id = channel.id else {
navigationController?.popViewController(animated: true)
return
}
reference = db.collection(["channels", id, "thread"].joined(separator: "/"))
// reference?.addSnapshotListener { querySnapshot, error in
// guard let snapshot = querySnapshot else {
// print("Error fetching snapshots: \(error!)")
// return
// }
// snapshot.documentChanges.forEach { diff in
// if (diff.type == .added) {
// print("New city: \(diff.document.data())")
// }
// if (diff.type == .modified) {
// print("Modified city: \(diff.document.data())")
// }
// if (diff.type == .removed) {
// print("Removed city: \(diff.document.data())")
// }
// }
// }
messageListener = reference?.addSnapshotListener { querySnapshot, error in
guard let snapshot = querySnapshot else {
print("Error listening for channel updates: \(error?.localizedDescription ?? "No error")")
return
}
snapshot.documentChanges.forEach { change in
self.handleDocumentChange(change)
print("handleDocumentChange")
}
}
self.navigationItem.title = title
messageInputBar.delegate = self
messagesCollectionView.messagesDataSource = self
messagesCollectionView.messagesLayoutDelegate = self
messagesCollectionView.messagesDisplayDelegate = self
messageInputBar.sendButton.tintColor = UIColor.lightGray
//scrollsToBottomOnKeyboardBeginsEditing = true // default false
//maintainPositionOnKeyboardFrameChanged = true // default false
}
private func save(_ message: Message) {
reference?.addDocument(data: message.representation) { error in
if let e = error {
print("Error sending message: \(e.localizedDescription)")
return
}
self.messagesCollectionView.scrollToBottom()
}
}
private func insertNewMessage(_ message: Message) {
guard !messages.contains(message) else {
return
}
messages.append(message)
messages.sort()
let isLatestMessage = messages.index(of: message) == (messages.count - 1)
let shouldScrollToBottom = messagesCollectionView.isAtBottom && isLatestMessage
messagesCollectionView.reloadData()
if shouldScrollToBottom {
DispatchQueue.main.async {
self.messagesCollectionView.scrollToBottom(animated: true)
}
}
}
private func handleDocumentChange(_ change: DocumentChange) {
guard var message = Message(document: change.document) else {
print("return Message")
return
}
switch change.type {
case .added:
print("add Message")
insertNewMessage(message)
default:
break
}
}
}
Console prints
New city: ["senderUsername": panyayan,
"senderID": RAMIqHAVeoU4TKkm3FDw7XUwgym2,
"created": FIRTimestamp:seconds=1544623185 nanoseconds=412169933>,
"recipientUsername": panyayan,
"content": AAA,
"recipientID": RAMIqHAVeoU4TKkm3FDw7XUwgym2,
"recipientProfilePictureURL": https:,
"senderProfilePictureURL": https:]
return Message
handleDocumentChange
Dont know if you still need it but:
The document.data() field created is a FIRTimestamp.
When you try to init the Message object you use
guard let sentDate = data["created"] as? Date else {
return nil
}
Thats might be a reason, why your object is nil.
Try something like
guard let sentTimestamp = data["created"] as? Timestamp else {
return nil
}
...
self.sentDate = sentTimestamp.dateValue()

How to read correctly the database of Firebase on the viewDidLoad() func?

//Just a struct to save information about the User
var user = AppUser()
override func viewDidLoad() {
super.viewDidLoad()
//Verify if user is logged in
verifyUser()
user.email = "blabla"
print("viewdidload user: \(user)")
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(true)
print("viewdidappear user: \(user)")
}
func verifyUser() {
print("verify user called")
//Log in verification
guard let userID = Auth.auth().currentUser?.uid else {
perform(#selector(handleLogOut), with: nil, afterDelay: 0)
print("nil user")
return
}
ref.child("users").child(userID).observeSingleEvent(of: .value, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
self.user = AppUser(dictionary: dictionary)
print(self.user)
}
}) { (error) in
print(error.localizedDescription)
}
}
Console:
verify user called
viewdidload user: AppUser(id: nil, name: nil, email:
Optional("blabla"), completedRegister: nil, FULLUser: nil)
viewdidappear user: AppUser(id: nil, name: nil, email:
Optional("blabla"), completedRegister: nil, FULLUser: nil)
AppUser(id: nil, name: nil, email: Optional("x#gmail.com"),
completedRegister: Optional(false), FULLUser: Optional(false))
The question is simple. Can someone explain how is it possible that "print("viewDidLoad user:....")" is printed between "verify user called" and "the user" with the database information?
The problem is when I try to get information of the user on the viewDidLoad the function for some reason hasn't get the information so the values are still nil. Is it a question of time?
I tried to put a loop after the function verifyUser() but it never gets
out:
while user.email == nil {
print("Waiting...")
}
So... thats the question
Thanks!
edit for Anas Menhar
This is my struct. Why would it be better to be a NSObject?
I did a Struct because I could do two different inits (one empty) and the NsObject didn't let me for some reason
struct AppUser {
var id: String?
var name: String?
var email: String?
var completedRegister: Bool?
var FULLUser: Bool?
init(dictionary: [String: Any]) {
self.id = dictionary["id"] as? String
self.name = dictionary["name"] as? String
self.email = dictionary["email"] as? String
self.completedRegister = Bool((dictionary["completedRegister"] as? String)!)
self.FULLUser = Bool((dictionary["FULLUser"] as? String)!)
}
init() {
self.id = nil
self.name = nil
self.email = nil
self.completedRegister = nil
self.FULLUser = nil
}
}
edit for Hitesh
if I print the dictionary it prints just at the same time that the user completed with the information. At the end of everything.
The issue is that viewDidLoad() calls verifyUser() before printing "viewdidload user: \(user)". Everything in verifyUser() will finish (with exception to your network call) before the print.
So here is the sequence of events happening for you:
super.viewDidLoad()
verifyUser() is called
print statement that is in verifyUser()
guard statement that is in verifyUser()
print statement in viewDidLoad()
super.viewDidAppear()
print in super.viewDidAppear()
Everything inside the closure for the .observeSingleEventOf will happen some point after 4 -- whenever that call finishes. If you want to do something when the call is finished, put it in the closure.
Like this:
ref.child("users").child(userID).observeSingleEvent(of: .value, with: { (snapshot) in
if let dictionary = snapshot.value as? [String: AnyObject] {
self.user = AppUser(dictionary: dictionary)
//***handle stuff that needs the user here
print(self.user)
} else {
//***handle getting no data for the user or data that is not [String: AnyObject]
}
}) { (error) in
//***handle any errors here
print(error.localizedDescription)
}
The places where I added comments are the places that would potentially be called when that request completes.
Side note: If you are going to use a Struct for AppUser, just make sure you know the differences between structs and classes. You could have a class named AppUser and have two different inits like this:
class AppUser {
var id: String?
var name: String?
var email: String?
var completedRegister: Bool?
var FULLUser: Bool?
///initialization from a dictionary
init(dictionary: [String: Any]) {
id = dictionary["id"] as? String
name = dictionary["name"] as? String
email = dictionary["email"] as? String
if let completed = dictionary["completedRegister"] as? String {
if completed == "true" {
completedRegister = true
} else if completed == "false" {
completedRegister = false
}
}
if let fullUser = dictionary["FULLUser"] as? String {
if fullUser == "true" {
FULLUser = true
} else if fullUser == "false" {
FULLUser = false
}
}
}
///initialization without a dictionary
init() {
//dont need to set any variables because they are optional
}
}
let user1 = AppUser(dictionary: ["id": "12342",
"name": "John Doe",
"email": "email#email.com",
"completedRegister": "true",
"FULLUser": "true"])
///user1 properties:
id: Optional("12342")
- some: "12342"
name: Optional("John Doe")
- some: "John Doe"
email: Optional("email#email.com")
- some: "email#email.com"
completedRegister: Optional(true)
- some: true
FULLUser: Optional(true)
- some: true
let user2 = AppUser()
///user2 properties: none

How to retrieve user mail and name? (PFFacebookUtils.logInInBackgroundWithReadPermissions, swift 1.2)

I have successfully logged my user in, using the following code and plenty of reading on stackoverflow:
#IBAction func FBlogin(sender: UIButton) {
self.activity.startAnimating()
let permissions = ["public_profile", "email"]
PFFacebookUtils.logInInBackgroundWithReadPermissions(permissions) {
(user: PFUser?, error: NSError?) -> Void in
if let user = user {
self.activity.stopAnimating()
if user.isNew {
print(user.email)
print(user.username)
print("User signed up and logged in through Facebook!")
} else {
print(user.email)
print(user.username)
print("User logged in through Facebook!")
}
} else {
self.activity.stopAnimating()
}
}
}
This creates a new user in parse.com BUT does not fill-in email, username ...?
QUESTION: how does one retrieve the email and userName details?
AFTER searching and fiddling more I added this code:
func getUserDataFromFacebookProfile(user: PFUser)
{
var facebookid : String?
var username : String?
var userEmail : String?
var userGender : String?
var userLocation : String?
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: nil)
graphRequest.startWithCompletionHandler({ (connection, result, error) -> Void in
if ((error) != nil)
{
// Process error
print("graphrequest error: \(error)")
}
else
{
//if this works, we can store user name und PFUser and mail in PFuser....
//not working yet
/*
id
name
first_name
last_name
link
gender
locale
timezone
updated_time
verified
*/
facebookid = result.valueForKey("id") as? String
userEmail = result.valueForKey("email") as? String
username = result.valueForKey("name") as? String
let fname = result.valueForKey("first_name") as? String
let lname = result.valueForKey("last_name") as? String
let loc = result.valueForKey("locale") as? String
let gend = result.valueForKey("gender") as? String
//userGender = result.valueForKey("gender") as? String
userLocation = result.valueForKey("location") as? String
print("*****************************************************************************")
print("")
print("graphrequest result \(facebookid), \(username), \(userEmail), \(gend), \(fname), \(lname)")
print("")
print("*****************************************************************************")
}
//persist in our DB for later
let thisUser: PFUser = user
print("updating current user")
if let uName = username {
thisUser.username = uName
}
if let uEmail = userEmail {
thisUser.email = uEmail
}
thisUser.saveInBackground()
})
}
The strange thing is that I can only retrieve id, email and name?!
About email: Search for "Declarative Fields" in the changelog: https://developers.facebook.com/docs/apps/changelog#v2_4
About username: That field is no longer available since v2.0 of the Graph API: https://developers.facebook.com/docs/apps/changelog#v2_0
So here is what worked for me. Appparently FB has again changed a few things. The key is to add the required fields to the parameter list, like so:
let graphRequest : FBSDKGraphRequest = FBSDKGraphRequest(graphPath: "me", parameters: ["fields": "email,first_name,last_name,gender,picture.width(480).height(480)"])