Best practice to store CurrentUser after login - swift

I'm implementing my login-logic using Firebase with just Facebook as provider.
How can I save my CurrentUser after the login in order to use personal data during the app experience later?
At the moment I'm using a singleton with an instance of User. Something like this:
CurrentUser.swift
class CurrentUser {
static let i: CurrentUser = CurrentUser()
var cUser: User?
private init() {
}
func setCurrentUser(u: User) {
cUser = u
}
func getCurrentUser() -> User {
return cUser!
}
func clearUser() {
cUser = nil
}
func userIsLogged() -> Bool {
return cUser != nil
}
}
And I'm using that singleton this way:
LoginViewController.swift
class LoginViewController: UIViewController {
...
func createCurrentUser(authData: FAuthData) {
let u = User(uid: authData.uid, displayName: authData.providerData["displayName"] as! String, email: authData.providerData["email"] as! String)
u.wrapperFromFacebookData(authData.providerData)
ref.childByAppendingPath("users").childByAppendingPath(u.uid).setValue(u.toDict())
CurrentUser.i.setCurrentUser(u)
}
...
}
I don't think this is the best practice. Before Firebase I'm used to deal with Parse builtin user logic, that was pretty easier.

I am facing the exact problem and this link helped a lot: http://totallyswift.com/ios-app-development-part-2/
What he did was create a singleton (currentUser) that conforms with User class.
class var currentUser: User
{
struct Static
{
static var instance: User?
}
if Static.instance == nil
{
if let load: AnyObject = NSUserDefaults.standardUserDefaults().objectForKey(kUserDataKey)
{
Static.instance = User(data: load as [String: AnyObject])
}
else
{
Static.instance = User()
}
}
return Static.instance!
}

Related

inverse a core data boolean value

In my swift code below the goal would be to inverse whatever the boolean sign is. Right now the code just deletes the current boolean value which is at user1. So say user1 is true when it is inserted in into the function I would like to reverse to false instead of deleting it in core data how do I do that?
class ViewController: UIViewController {
override func viewDidLo
if CoredataHandler.deleteObject(user: user![1]) {
user = CoredataHandler.fetchObject()
print("after single")
for i in user! {
print(i.username!)
}
}
}
}
class CoredataHandler : NSManagedObject {
private class func getContext() -> NSManagedObjectContext {
let appDelegate = UIApplication.shared.delegate as! AppDelegate
return appDelegate.persistentContainer.viewContext
}
class func fetchObject() -> [User]?
{
let context = getContext()
var user : [User]? = nil
do {
user = try context.fetch(User.fetchRequest())
return user
} catch {
return user
}
}
class func deleteObject(user: User) -> Bool
{
let context = getContext()
let delete = NSBatchDeleteRequest(fetchRequest: User.fetchRequest())
do {
try context.execute(delete)
return true
} catch {
return false
}
}
}

Boolean returns nil and unable to access value from response on view controller

I have a usermodel that checks the backend if the email exists - then I drill back into a viewcontroller and set a boolean value that should trigger a function run. However the value is unchanged and I am trying to change this value from the usermodel but it is not accessible. I understand why it does not work.. but do not know how to resolve the issue.
static func sendEmailWithResetLink(email: String) {
let params : Parameters = [
PARAM_EMAIL : email
]
request(URL_RESET_PASSWORD as String, method: .post, parameters: params, headers: nil).responseJSON {
(response: DataResponse<Any>) in
hideProgress()
print("this is response \(response)")
switch(response.result)
{
case .success(_):
print("it did not fail")
let passwordResetVC = PasswordResetViewController()
passwordResetVC.hasFailed = false
break
case .failure(_):
print("it failed")
let passwordResetVC = PasswordResetViewController()
//here boolean is set that I am trying to access in viewcontroller
passwordResetVC.hasFailed = true
break
}
}
}
Here's what I would suggest. You probably have some of these in place already:
Create an PasswordResetViewController object has an #IBAction func resetButtonClicked triggered by a button or whatever, which kicks off the password reset process.
Create a UserManager class. This class is responsible for all profile management activies in your app. Among other things, it has the ability to reset user passwords. This UserManager would probably be a singleton, that' sprobably good enough for now.
Create a new UserManagerDelegate protocol. Add to it all capabilities that are required by the UserManager to inform them of whatever happened. For example: var passwordResetHasFailed: Bool { get set }.
Extend your PasswordResetViewController conform to this protocol.
Your VC gets a reference to the singleton UserManager object, stores it in an instance variable, and uses that to access the shared object from then on.
Make your PasswordResetViewController register itself as the delegate to the user manager, with userManager.delegate = self
The #IBAction func resetButtonClicked will just call userManager.resetPassword()
Your UserManager does whatever it needs to do to reset the user's password.
When it's done, it'll call self.delegate?.passwordResetHasFailed = true/false.
Since your PasswordResetViewController registered itself as the delegate of the UserManager, when the operation is done, its passwordResetHasFailed property will be changed, giving it a chance to respond (by updating some UI or whatever).
There are some limitations to this approach, but it's a decent way to get started. Some thing to note:
This lets you unit test your PasswordResetViewController. You can create a MockUserManager, and set tesPasswordResetViewController.userManager = MockUserManager(), allowing you to separate out the user manager, and test PasswordResetViewController in isolation.
You'll run into issues if you need multiple objects to subscribe to receive delegate call backs (since there can only be 1 delegate object). At that point, you can switch to using something like Promises, RxSwift or Combine. But that's a problem for a later time, and the migration would be easy.
Going off of #Alexander - Reinstate Monica and what I assume what the code to look like to approach your problem.
Using MVC:
In Models folder (data/ logic part)
public class User {
private var name: String!
private var userEmail: String!
public var hasFailed: Bool?
init() {
name = ""
userEmail = ""
hasFailed = nil
}
public func setName(name: String) { self.name = name }
public func getName() -> String { return name }
public func setEmail(email: String) { userEmail = email }
public func getEmail() ->String { return userEmail }
public static func sendEmailWithRestLing(email: String) {
// your other code
switch response.result {
case .success(_):
//your code
hasFailed = false
break
case .failuare(_):
// your code
hasFailed = true
break
}
}
}
User Manager class applying singleton design
final class UserManager {
private var user = User()
static let instance = UserManager()
private init(){}
public func userName(name: String) {
if (name.count > 3) {
user.setName(name: name)
}
else { print("user name is too short") }
}
public func userEmail(email: String) {
if (email.count > 3) {
user.setEmail(email: email)
}
else { print("user email is too short") }
}
public func getUserName() -> String {
let name = user.getName()
if (name.isEmpty) { return "user name is Empty" }
return name
}
public func getUserEmail() -> String {
let email = user.getEmail()
if (email.isEmpty) { return "user email is Empty" }
return email
}
public func doKatieTask(link: String) -> Int {
guard let myValue = user.hasFailed else {
return -1
}
if (myValue) { return 1}
return 0
}
}
So, Now in the Controllers folder and since we a one-to-one relation we will use delegate design pattern. If had had one-to-many with the view controller. Use observers.
class ViewController: UIViewController {
#IBOutlet weak var nameTextField: UITextField!
#IBOutlet weak var emailTextField: UITextField!
var _hasFail: Bool!
override func viewDidLoad() {
super.viewDidLoad()
// Do any additional setup after loading the view.
}
#IBAction func doTask() {
UserManager.instance.userName(name: nameTextField.text!)
UserManager.instance.userEmail(email: emailTextField.text!)
switch UserManager.instance.doKatieTask(link: emailTextField.text!) {
case 0:
_hasFail = false
break
case 1:
_hasFail = true
break
default:
print("hasFailed is nil")
break
}
if let vc = storyboard?.instantiateViewController(identifier: "passwordVC") as? PasswordResetViewController {
vc.modalPresentationStyle = .fullScreen
vc.delegate = self
self.present(vc, animated: true, completion: nil)
}
}
}
extension ViewController: KatieDelegate {
var hasFailed: Bool {
get {
return _hasFail
}
set {
_hasFail = newValue
}
}
}
In PasswordReset UIViewController
protocol KatieDelegate {
var hasFailed: Bool { get set }
}
class PasswordResetViewController: UIViewController {
#IBOutlet weak var nameLabel: UILabel!
#IBOutlet weak var emailLabel: UILabel!
var delegate: KatieDelegate?
override func viewDidLoad() {
super.viewDidLoad()
nameLabel.text = UserManger.instance.getUserName()
emailLabel.text = UserManger.instance.getUserEmail()
if let delegate = delegate {
print("The value for has failed is: .....\(delegate.hasFailed)!")
}
else { print("error with delegate") }
}
}

clear singleton data and everything when user logout

I have an application that when user login I get data from the server with singleton class but the problem is when user logout(exit from login not exit from application) I can't renew singleton class, is there a way that when user log out clear all data and class?
My singleton class is as follows
import Foundation
class GetloginData{
static let instance = GetloginData()
private let personData : PersonData = generateLoginData()
func getLoginData () -> PersonData {
return personData
}
}
func generateLoginData() -> PersonData {
name = service.getname
balance = service.balance
return PersonData(name: name, balance: balance)
}
service is soap webservice
You can simply change the personData to optional and add a method to resetLoginData
import Foundation
class GetloginData {
static let instance = GetloginData()
private var personData : PersonData? = generateLoginData()
func getLoginData () -> PersonData? {
return personData
}
func resetLoginData() {
personData = nil
}
}
And on your logout button put it like
GetloginData.instance.resetLoginData()
Also, use guard else to check for the logged in or not and handle it accordingly.
guard let loggedInPerson = GetloginData.instance.getLoginData() else {
// HANDLE CODE TO RETURN TO LOGIN SCREEN.
return
}
Hope it helps

Specifying A Dynamic Type In Swift Generics

I created a small wrapper class for NSNotification registrations.
public final class EventRegistration<E: AppEvent> {
private(set) var event: E?
private var token: AnyObject?
public init() {}
public init(event: E, token: AnyObject) {
self.event = event
self.token = token
}
public func unregister() {
if let token = self.token,
let event = self.event {
event.unregister(token)
}
token = nil
event = nil
}
public var isRegistered: Bool {
return token != nil
}
deinit {
unregister()
}
}
It is a simple enough class. Now I would like to return it from a method on the base class of all my other events. Here is the signature:
public class AppEvent : CustomStringConvertible {
public func register(notification: (NSNotification! -> Void))->EventRegistration {
let eventName = self.dynamicType.eventName()
let token = NSNotificationCenter.defaultCenter().addObserverForName(eventName, object: nil, queue: self.dynamicType.eventQueue(), usingBlock: notification)
let registration = EventRegistration(event: self, token: token)
return registration
}
The question, is what can I put for the return type of register such that I get the correct AppEvent subclass when this method is called on subclass.
ie.
class SomeSubClass: AppEvent {}
I would want result.event to be of type SomeSubClass

Swift - How to set a singleton to nil

I am writing an app in swift and use a singleton to share a class object, User, across the app.
I want to be able to set this singleton to 'nil' when the user logs out, so that when they log back in the old properties no longer exists (i.e. name, username, etc).
I am hoping there is an easy way to just set the singleton back to nil, instead of having to set each property to nil.
Here is my User class that is used in the app as User.activeUser:
class User: NSObject
{
class var activeUser : User? {
struct Static {
static let instance : User = User()
}
return Static.instance
}
}
How can I change this so that the below code does not give me a warning and actually nils out the singleton object:
User.activeUser = nil
This should work:
private var _SingletonSharedInstance:MyClass! = MyClass()
class MyClass {
let prop = "test"
class var sharedInstance : MyClass {
return _SingletonSharedInstance
}
init () {}
func destroy() {
_SingletonSharedInstance = nil
}
}
But then the references to the object are still kept, so you need to do some additional things to invalidate method calls in the class.
Your activeUser is set up as a read-only computed property. Every time you call User.activeUser it is going to recompute the activeUser for you. In order to set it to nil you'll have to add some logic to determine whether or not a user is logged in outside of the computed property. Something like this would work:
class User: NSObject
{
private struct userStatus { static var isLoggedIn: Bool = true }
class var activeUser : User? {
get {
if userStatus.isLoggedIn {
struct Static {
static let instance : User = User()
}
return Static.instance
} else {
return nil
}
}
set(newUser) {
if newUser != nil {
userStatus.isLoggedIn = true
} else {
userStatus.isLoggedIn = false
}
}
}
}
Here has a solution I used in java, but also works in swift:
class Manager {
private static var manager: Manager?
static func instance() -> Manager {
if Manager.manager == nil {
Manager.manager = Manager()
}
return Manager.manager!
}
func destroy() {
Manager.manager = nil
}
}