2

I am a beginner in programming and in iOS development, I am trying to make an app that use Tab Bar Controller and it has 5 Tab bars.

I make a User class that has email, uid, name, and other user data like their domicile etc as the properties. This class is initialized in the view controller of first tab bar in the tab bar controller (index:0), let say we call it HomeVC. the instance of the User class is initialized using the data that comes from the server.

the instance of this User class, will be used several time in my app. let say when the user creating a post in the PostVC that located in the bottom / last VC in the navigation stack of the third tab bar (index:2). and also when the user see their own profile in profileVC.

I think making request repeatedly to get user data in some place in my app is not a good practise, since currently I am using Firestore database, more read the database means it needs more cost. so I want just making one request to the server to get the user data and then use that user data for my entire app.

at this time I make a super global variable ( a variable that located outside the class), so I can access it in everywhere in my app. but I just read that using super global variable is a bad programming practise.

so what should I do? do I have to pass the user data by using prepareForSegue ? is there any better option to solve this problem?

sarah
  • 3,819
  • 4
  • 38
  • 80

5 Answers5

2

As you are already using Cloud Firestore you could sign your user in using various methods in the Firebase Authentication dashboard (FirebaseAuth). Once a user is signed in you can get the current User from anywhere in your app, like this:

    import Firebase        

    if let user = Auth.auth().currentUser {
        let userID = user.uid
        let email = user.email
        let displayName = user.displayName
    }

Once the App has a valid User Token is does not keep calling back to the server, unless the Token needs refreshing (such as during a change to the User Account settings).

https://firebase.google.com/docs/auth/ios/start

rbaldwin
  • 4,581
  • 27
  • 38
1

I think you should avoid singletons. One of the best solutions I learned is to create protocols for your datamanagers (like sessions, shopping cart...). After that you should push your dependencies as far as you can from your class. Which in this case is AppDelegate. So you create properties for these classes to be held there. If you have many of these put them together in a class or something.

Little example:

@objc protocol SessionManagerProtocol {
  func persistUser()
}

Then in your AppDelegate: (make it thread safe)

var sessionManager: SessionManagerProtocol!
func getSessionManager() -> SessionManagerProtocol {
    if let manager = sessionManager {
        return manager
    }
    sessionManager = SessionManager()
    return sessionManager
}

With this solution you can also inject dependencies if you want to make it testable.

If your app goes to background mode you save the session to userdefaults or your own database.

If you just want to pass between viewcontrollers check this link: Passing Data between View Controllers

1

I would recommend avoiding singletons here as well. The primary motivation is to make things testable. Here's a possible approach.

You have a User something like this:

struct User {
    let userid: String
    let email: String
}

Declare a protocol for managing your user. Using a protocol makes testing easier since you can create a mock that conforms to it.

protocol UserManagerProtocol {
    func getSignedInUser() -> User?
    func signIn(userid: String, password: String) -> User?
}

Declare your actual class that implements the protocol:

class UserManager: UserManagerProtocol {
    fileprivate var signedInUser: User?
    func getSignedInUser() -> User? {
        return signedInUser
    }
    func signIn(userid: String, password: String) -> User? {
        //call your server to sign in user
        // if sign in fails, return nil
        // if sign in works, create a User object
        signedInUser = User(userid: userid, email: "email retrieved from server")
        return signedInUser
    }
}

Add a reference in your view controllers, but declare it using the protocol, not the actual class. Again, this allows for mocking.

class HomeVC: UIViewController {
    var userManager: UserManagerProtocol?
}
class PostVC: UIViewController {
    var userManager: UserManagerProtocol?
}

Finally, in your AppDelegate, when you launch the app, create a single instance of your UserManager, store it in the AppDelegate, and pass it to all your view controllers. In this example, I've obviously left out a lot of stuff because I don't know what your AppDelegate looks like.

class AppDelegate: NSObject, UIApplicationDelegate {
    var userManager: UserManagerProtocol?

    func applicationDidFinishLaunching(_ application: UIApplication) {
        userManager = UserManager()

        //when setting up your tab bar controllers, pass the userManager instance to each of them
    }
}
Mike Taverne
  • 9,156
  • 2
  • 42
  • 58
0

People hate singletons despite

URLSession.shared

UIApplication.shared

NotificationCenter.default

and many more , nearly every Apple framework has a singleton class , it's not a bad idea as long as it's documented well so other developers can read your code properly

Shehata Gamal
  • 98,760
  • 8
  • 65
  • 87
  • There's a huge difference between a singleton for a utility class and a singleton for a data model. People don't hate singletons. Many people dislike the misuse/overuse of singletons where they shouldn't be used. – rmaddy May 03 '18 at 15:57
  • Aren't object & userInfo properties of Notification.are examples of data model ?? – Shehata Gamal May 03 '18 at 17:47
  • `object` and `userInfo` are not singletons (nor properties). They are simple arguments to method calls. And a comment here is not the place to write a lengthy discussion about the proper way to pass data around an app. – rmaddy May 03 '18 at 17:52
  • they are properties inside Notification sended by notificationCenter which means that a singleton shares data , plus for example firebase , parse and many others do the same sharing with singletons , i told you that as there is no workaround and it will have to write and read the data with is insufficient regarding performance which for sure compared with app-live existence of a singleton puls the ability to hand it over to the next class which is HORRIBLE – Shehata Gamal May 03 '18 at 18:18
0

I think for your case you can store data in serialized format in some file.txt or CoreData DB. You can get data from these places at any time from any part of your app.

Serhii Didanov
  • 2,200
  • 1
  • 16
  • 31