Persisting user data across view controllers post-authentication - ios

I'm developing an (my first) iOS application, which utilizes a web service/data API that I've written. Users can create accounts and retrieve information using this API after they've logged in/been authenticated. All users have say a: username, user id, profile picture, biography, etc... I've created an NSObject "user" which is a class that has corresponding properties/attributes. After logging in, in each viewcontroller we need access to this user data, and the solution I've implemented for now is to give each viewcontroller a property "user" that expects my user object. Each time I instantiate a view controller, I am assigning the user class instance to the user property of the viewcontroller, so that I can access the properties of that instantiated user class from within the viewcontroller. I know that this not an optimal solution, but since I'm hacking along trying to learn iOS as I go, this is what I've run with for now.
My question is: What is the best way to persist this user data for use across multiple view controllers in my application?
Other notes:
The amount of data that I need to persist is relatively little and is of types NSString, NSNumber, and UIImage. My user class is being instantiated for the current logged-in user, and also for other users for, for example, when retrieving data for a "friend" and then viewing their profile. Access to data for a user must be only if the user has been authenticated, so I need to be able to destroy the data-store once the session has expired.

Here you go.
I'm not sure exactly how much data you are actually trying to store, but... there is an iOS wrapper to store small amounts of data locally in a plist called NSUserDefaults.
Its really easy to use. Check out the documentation
It uses KVC to set/get the data.
Heres a simple example:
To save the data
[[NSUserDefaults standardUserDefaults] setString:myString forKey:#"username"];
[[NSUserDefaults standardUserDefaults] synchronize];
To get it
_userName = [[NSUserDefaults standardUserDefaults]
stringForKey:#"username"];

If you need the user to be globally available you could store it on your app delegate as a public property. Something like:
In your AppDelegate.h
#interface AppDelegate UIResponder <UIApplicationDelegate>
#property (strong, nonatomic) NSObject *user; // or User *user; if you have an actual user class
then in your view controllers
#import "AppDelegate.h" // at the top
[UIApplication sharedApplication].delegate.user; // where you need access to the user
Another route you could go down is a singleton class that holds your user in a property. For example:
#interface UserManager : NSObject
-(instancetype) sharedHandler;
#property (strong, nonatomic) NSObject *user;
#end
#implementation UserManager
+(instancetype) sharedHandler
{
static instancetype sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[self alloc] init];
});
return sharedInstance;
}
#end
A lot of people consider singletons and global properties as bad practice but I think they have their uses; they've simply been been abused and garnered a bad reputation. Another thing I should add is that this won't persist information between app launches (if the app is quit by the user or terminated by the system); unlike #NewEngland's answer which will. Good thing is you could actually use both ideas together if thats what you wanted.

Related

I'm trying to create a simple login and sign up screen for an application I'm developing. I want to use a dictionary to s

I'm trying to create a simple login and sign up screen in swift for an application I'm developing. I want to use a dictionary to store the multiple usernames and passwords that are later checked by a conditional, to see if the key matches the value, to login. The main issue I'm running into is the placement of the dictionary where it can be used by both login view controller and sign up view controller. Currently, I made the login VC a subclass of the sign up VC; however, the login screen isn't receiving the dictionary from the sign up screen. What can I do to inherit this dictionary from one VC to another, or is there a better way to go about this?
Even though it's a subclass, and it has all the variables that the superclass does, they are not the same variables. You have two different objects of different classes. Making it a subclass doesn't buy you anything in this direction, and makes your code more confusing. There are two general strategies for this: 1) allocate the dictionary in a third object that both objects have a reference to, or 2) when you create your second object, give it a property of the first object type, and write the first object into it.
// rough code follows in obj C
Class signin:
#interface SignIn: UIViewController
#property (nonatomic, strong) NSDictionary * dict;
#end
Class Login:
#class SignIn;
#interface Login: UIViewController
#property (nonatomic, weak) SignIn * mySigninController;
#end
#implementation Signin
...
// create login controller
self.myLoginController = [[Login alloc] init];
// add the reference to myself in login
self.myLoginController.mySigninController = self;
...
in login:
...
my value = self.mySigninController.dict[#"key"];

Creating global variables in Objective C

I have designed a login app. I have entered the account data in sqlite database. However when I logged in with the account username and password i want these to be accessible to all view controllers. Because, I have different controllers which will update the account details. Need implementation details..
Thanks
Use a singleton: http://www.galloway.me.uk/tutorials/singleton-classes/
I have a project with something like this (note I'm using the iOS keychain rather than a database, but I've hidden those details here):
#interface User : NSObject
#property (nonatomic, strong) NSString *username;
+ (User *)currentUser;
#end
#implementation User
+ (User *)currentUser
{
static User *currentUser = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
currentUser = [[User alloc] initFromKeychain];
});
return currentUser;
}
#end
There are A LOT of approaches to doing singletons (depending on how paranoid you want to be about whether someone can create a non-singleton instance of your class or not). Examples here: What should my Objective-C singleton look like?
You could also just access the keychain directly wherever you need it. You should also ask yourself why so much of your code needs access to a username/password. Sounds like a broken design.
Avoid global
variables, rather you can use a Singleton class to store application wide credentials information.
However keep in mind that the best way to store credentials in an app is using Apple Keychain, you can find the API reference on the Apple site.
As it was said - avoid global variables, but if you want them for sure - do it like in c language:
define this variable in one file
in all other files refer to it with extern keyword
Simply: Don't do it!
I recommend to store the credentials in the keychain. But don't use the sample code Apple provides. It doesn't work. I use SSKeychain in my projects.
If you don't need to store the credentials you should, as aleroot suggested, use a singleton or a class with only one instance.
Three answers:
Don't do it.
If you must do it, use a Singleton.
For extremely easy access to username and non confidential (i.e., password) variables, use NSUserDefaults.

iOS Pass data, singleton, extern or via protocols?

Hy all,
I am really stuck at data passing in my iOS app.
First i have a class with TabBarController and inside another two ViewController.
It's a container view with a daily and weekly calendar views.
And i wanted to store the downloaded data in an NSDictionary with dates, so the calendar don't have to reload the data any time (only if the user force refresh).
So, should i store this data in an "extern NSDictionary" in the Container view?
Or should i create a SingletonClass and store the Dictionary there?
But will the SingletonClass released after the container view get released?
Or should i store the NSDictionary in the container view and then use methods via protocols to access the Dic? But how?
I checked a lot of tutorials and examples but i still don't know how to do it properly.
Thank you
You could store it inside the tabBarController and access it inside of the tabBar viewController instances, but I think you might be best off by just storing it inside of the NSUserDefaults..That way you can easily grab it from anywhere in your application without grabbing the tabBar instance.
I'd personally recommend creating a Singleton class that implements your NSUserDefaults like so (although you can just write and read directly to it if you'd like):
//DefaultsSingleton.h
#interface DefaultsSingleton : NSObject
{
}
+(DefaultsSingleton*) sharedDefaultsSingleton;
#property(atomic,assign)NSDictionary *yourDictionary;
//DefaultsSingleton.m
#implementation DefaultsSingleton
SYNTHESIZE_SINGLETON_FOR_CLASS(DefaultsSingleton)
-(NSDictionary *) yourDictionary
{
return[ [NSUserDefaults standardUserDefaults] dictionaryForKey:#"your_dictionary"];
}
-(void) setYourDictionary:(NSDictionary *)yourDictionary
{
[[NSUserDefaults standardUserDefaults] setValuesForKeysWithDictionary:yourDictionary];
}
Then just import this singleton file to wherever you want to access it and you can init new dictionaries with the values of it and create mutable copies, and then override the saved value whereever you'd like. Oh and you should also import Matt Gallagher's awesome SynthesizeSingleton class. http://www.cocoawithlove.com/2008/11/singletons-appdelegates-and-top-level.html

Best approach for storing single BOOL global data value

I need some advice about best practice of developing iOS app design
Here's what I'm dealing with: when iOS device looses internet connection (or doesn't have one and figuring that out) I want my app to go to some sort of offline mode, i.e. firing some event, sending some NSNotifications, m.b. showing some sort of alert etc. Accordingly, when iOS device gets it's connection back I want the oposite thing - move my app to some sort of online mode.
So, what I want is to have ability to access app's mode (i.e. to check whether app is online or offline) from within some of my ViewControllers. I'm thinking of two methods of storing app's state:
1) Have some AppDelegate's property and access it from anywhere via my AppDelegate. AFAIK, that's a wrong approach, because AppDelegate is not supposed to be used as global object for application, but for doing launch initialization and controlling application's state changes.
2) Store this information on Model level. But I have no idea what am I supposed to use on Model level for such purpose. I don't think using Core Data or NSUserDefaults is a good idea, because I don't want this property to be persistent, I need it only during current application running. And apart from Core Data and NSUserDefaults I don't actually know any other Model level techniques.
I don't include any code examples, because it's mostly a theoretical question.
you can use singleton pattern and store the variable as a property
for example
#interface GlobalData : NSObject
#property BOOL connectionAvailable;
+ (GlobalData *)sharedInstance;
#end
#implementation
+ (GlobalData *)sharedInstance {
static GlobalData *sharedInstance;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedInstance = [[GlobalData alloc] init];
});
return sharedInstance;
}
#end
// --- in some method
[GlobalData sharedInstance].connectionAvailable = /* connection state */;
// --- in some other method
BOOL connectionAvailable = [GlobalData sharedInstance].connectionAvailable;
The second approach works best. You surely don't need any persistence a property indicating the internet connection. A good thing to do is having your Model be a singleton class, that exposes a readonly BOOL property for the internet connection and your view controllers can subscribe to its changes via key-value observing. Your model can also implement internally the Reachability class for updating the internet connection status.
I'm assuming you're using Apple's Reachability class. http://developer.apple.com/library/ios/#samplecode/Reachability/Introduction/Intro.html
I created a category on the Reachability class to add a sharedInstance singleton that allows you to check the state of the internet connection. I use this in all my apps.
Here's how you do singletons:
How do I implement an Objective-C singleton that is compatible with ARC?

importing AppDelegate

Often times I initialize my model class variables in my AppDelegate so they can be used by different ViewControllers without passing their instance across class files. However, every time I import AppDelegate into a .m file to access these variable's data I feel like I'm doing some wrong.
Is this the the correct way for accessing these variables or should I be doing something differently?
EDIT:
My problem isn't how to access the variables. I currently use this line of code to get my appDelegate instance:
id appDelegate = [[UIApplication sharedApplication] delegate];
Conceptually, I want to know if this is the accepted way to interact with an application's model classes. It seems, to me, that an application's AppDelegate manages the application overall. So it seems counterintuitive to import this class into other classes further down an application's class chain.
Is this the the correct way for accessing these variables or should I
be doing something differently?
You'll find that different people have different opinions about this. The style that I prefer is to have the app delegate pass the necessary information to the first view controller, and have that view controller pass it on to whatever view controllers it creates, and so on. The main reason for this is that it prevents child view controllers from depending on things that they have no business knowing about.
If you have some detail editor, for example, you want to be able to pass that editor exactly what it needs to do its work. If you give it that information, the editor is completely flexible -- it'll edit any information that you give it. If the editor knows that it should get its data from some external object, like the app delegate, then it loses some degree of flexibility -- it can only get data from the thing that it knows about.
So, it's fine to set up your data model in the app delegate. But when it comes to providing access to the model, think: tell, don't ask. That is, have the app delegate tell the first view controller what model object to use, and have that controller tell the next one, and so on. If you have to ask, you have to know who to ask, and that's where the dependencies start heading in the wrong direction.
every time I import AppDelegate into a .m file to access these
variable's data I feel like I'm doing some wrong.
Trust that instinct. Think about why it feels wrong.
I agree that sometimes it seems like the AppDelegate is the logical place to put things that you only want implemented once but may need from several places. Creating a singleton for each one is fine, if those things are complicated, but it does create a lot of additional files and confusion to the project. I also agree with the majority of answers here, that building dependencies on the AppDelegate is a really poor design.
I think the best solution is to create a Protocol! Then put an IBOutlet to a property to do what you need to have done in each of the controllers that need the function. Protocols are the standard objective-C way to uncouple classes.
So, as an example, maybe I have a database URL that I may need from a bunch of places. Probably the best way would be to set a property with it each step along the way. But in some situations that may be cumbersome because of using a stock controller and not wanting to subclass it. Here is my solution:
Create file: MainDatabasePovider.h
#import <Foundation/Foundation.h>
#protocol MainDatabaseProvider <NSObject>
#required
#property (nonatomic, readonly) NSURL *applicationDocumentsDirectory;
#property (nonatomic, weak) NSURL *mainDatabase;
#end
Now "anyone" (i.e., any class) that says it implements the MainDatabaseProvder protocol is guaranteed to provide the two methods above. This could be the AppDelegate or ANY object.
Now if I want my AppDelegate to provide the information I change the AppDelegate.h file to have:
#import "MainDatabaseProvider.h"
#interface AppDelegate : UIResponder <UIApplicationDelegate, MainDatabaseProvider>
#property (strong, nonatomic) UIWindow *window;
#end
(The only change is to add the MainDatabaseProvider protocol to the #inteface line, again this could be done to ANY class that you want to provide the function).
In my AppDelegate.m file I have to write the two methods...
#implementation AppDelegate
...
#synthesize mainDatabase = _mainDatabase;
...
- (NSURL *) applicationDocumentsDirectory {
return [[[NSFileManager defaultManager] URLsForDirectory: NSDocumentDirectory inDomains: NSUserDomainMask] lastObject];
}
- (void) setMainDatabase: (NSURL *) mainDatabase {
if( _mainDatabase != mainDatabase ) {
_mainDatabase = mainDatabase;
}
}
- (NSURL *) mainDatabase {
if( !_mainDatabase ) {
NSURL *docURL = self.applicationDocumentsDirectory;
self.mainDatabase = [docURL URLByAppendingPathComponent: #"My Great Database"];
}
return _mainDatabase;
}
...
#end
Now in my controllers or other classes that have need of getting the MainDatabase I add the following:
In their .h files:
#import "MainDatabaseProvider.h"
...
#interface myGreatViewController: UIViewController
#property (nonatomic, weak) IBOutlet id <MainDatabaseProvider> mainDatabaseProvider;
...
#end
This property can be set in what was formerly known as InterfaceBuilder by control-dragging or can be set in code in prepareForSegue or I like to provide a custom getter that default it to the AppDelegate in case I am lazy or forgetful and don't do either of the above. In their .m file is would look like:
#implementation myGreatViewController
#synthesize mainDatabaseProvider = _mainDatabaseProvider;
...
- (id <MainDatabaseProvider>) mainDatabaseProvider {
id appDelegate = [[UIApplication sharedApplication] delegate];
if( !_mainDatabaseProvider && [appDelegate conformsToProtocol: #protocol(MainDatabaseProvider)] )
return appDelegate;
return _mainDatabaseProvider;
}
// To get the database URL you would just do something like...
- (void) viewWillAppear: (BOOL) animated {
NSLog( #"In %s the mainDatabaseProvider says the main database is \"%#\"", __func__, self.mainDatabaseProvider.mainDatabase.path );
}
Now the mainDatabaseProvider can be ANY object. I have the ability to set it in InterfaceBuilder or my StoryBoard (although I don't really think of this is a user-interface item so I typically wouldn't but it is pretty typical to do it that way), I can set it in code outside my controller before it gets loaded in prepareForSegue:sender: or tableView:didSelectRowAtIndexPath:. And if I don't set it at all it will default to the AppDelegate if I have configured it properly.
I can even put some safe guards in for when I forget to do things in my old age by changing the getter listed above to help me out with something like:
- (id <MainDatabaseProvider>) mainDatabaseProvider {
if( !_mainDatabaseProvider ) {
id appDelegate = [[UIApplication sharedApplication] delegate];
if( ![appDelegate conformsToProtocol: #protocol(MainDatabaseProvider)] ) {
NSLog( #"Hey!! The mainDatabaseProvider is not set and the AppDelegate does not conform to the MainDatabaseProvider protocol. How do you expect me to figure out where the database is!" );
} else {
return appDelegate;
}
return _mainDatabaseProvider;
}
You should seriously avoid importing the AppDelegate everywhere and you should feel like you are doing something wrong each time you do it (+1 for that). You are essentially creating a Big Ball of Mud and should reconsider your design. If for example you are using CoreData for your models consider a framework such as Magical Panda Active Record to retrieve data. I work on an enterprise application and the AppDelegate.h is only include in AppDelegate.m.
I'm importing them too and use it like this:
AppDelegate *delegate = (AppDelegate*)[[UIApplication sharedApplication] delegate];
[delegate variable];
Another way can be to use a Singleton.
Put this method in your AppDelegate class
+ (AppDelegate *)get {
return (AppDelegate *) [[UIApplication sharedApplication] delegate];
}
And when you need to call your AppDelegate use:
[AppDelegate get];
That is one way of doing it, yes, however it is not very elegant. Singletons are a way too, yes, however not very elegant :) - and reeealy NOT easy to test your code, if you have to mock out all your singletons.
Instead what I probably would do is to have one singleton for a service provider, and ask this service provider for an instance your model provider.
Say, your service provider class is a singleton and you need to access the model for the view user details. I would do it in the following manner:
JMUserDetailModel *myModel = [[[JMServiceProvider sharedInstance] modelProvider] userDetailModel];
This means that you would create a JMServiceProvider class for registering services, and being able to retreive these services. These services act a bit like a singleton, however if you need to unit test your code, then registering a different service that acts in the same way as the original is just a piece of cake.
Hope this answers your question.
EDIT: And read this article: http://martinfowler.com/articles/injection.html - a very good one explaining service oriented architectures as well ...
Look in to singletons. They can be a more elegant way to manage global data.

Resources