Creating global variables in Objective C - ios

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.

Related

Objective-C dynamic implementation

Description + sample + explanation: (You can skip to the question section)
I'd like to make an object instance, which can be implemented by different implementations, depend on a condition (the internet status).
Simple declaration
#interface LoginController : NSObject
/** The currently logged-in User. Nil if not logged-in yet. */
#property (strong, nonatomic) User *currentUser;
// Singleton object
+ (instancetype)shareInstance;
/** Abstract methods, will do nothing if call directly. Use inheritance implements (Online/Offline) instead. */
- (User *)loginByEmail:(NSString *)email password:(NSString *)pwd;
#end
#interface LoginControllerOnline : LoginController
// Login will call request to server.
#end
#interface LoginControllerOffline : LoginController
// Login will check data in coredata.
#end
The LoginController's login method actually do nothing (return nil). Instead, the inherited class (Online/Offline) overwrite the parent login's method, with different implementations (as in comments)
And then, I have a manager to define which class should be in use:
#implement InternetManager
+ (LoginController *)loginController
{
return [self hasInternet] ? [LoginControllerOnline shareInstance] : [LoginControllerOffline shareInstance];
}
+ (BOOL)hasInternet
{
// Check with Reachability.
}
#end
This work. But it's not the mechanism I'd like to achieve.
This mean I have 2 instances of inherited LoginController instead of 1.
When internetStatus change from offline to online, I'd like to re-login online (to get session/oauthToken...). But, I'll have to do many things (copy user, change instance, check retained...) before I can actually call from login online
QUESTION:
Is there a way for me to create only one instance of LoginController, which hold the same properties (User), but can has different (dynamic) implementations (Online/Offline)?
Update question:
Quote from Apple's Dynamic typing:
The isa Pointer:
Every object has an isa instance variable that
identifies the object's class. The runtime uses this pointer to
determine the actual class of the object when it needs to.
So, is there a way for me to change this isa pointer of an object instance?
It sounds like the real problem is that you've given these things direct primary ownership of state that you actually don't want them to own — factor it out. There's no copying, just give each an instance of the thing that marshals sate at -init and allow them to talk to it.
Then just do the normal programming thing when you want to do either one thing or another based on a condition: use an if statement.
So, I don't think use of the dynamic runtime is appropriate. However, academically, supposing an interest:
If you really must, use object_setClass, which "[s]ets the class of an object", answering your actual question. Obviously you need the storage to be compatible, so probably your subclasses shouldn't declare any properties or instance variables.
A commonly-discussed alternative for this general area is not changing the class of an existing instance but changing the methods that are a member of the class. So you'd have two alternative implementations of -loginByEmail:password: and set which was the one that actually responded to that selector dynamically. But there's really no advantage over just using an if if you have access to the source code and a bunch of disadvantages around its generally indirect, opaque nature. The whole thing is usually known as swizzling. class_replaceMethod is the key component but just search for swizzling.

Persisting user data across view controllers post-authentication

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.

the best way to get access

I've got one variable and I need this variable during the whole programm. Now code is the next:
.h file
extern RequestParams* newUser;
.m file
RequestParams* newUser;
But it works bad. Information doesn't get to this variable.
What is the best way to solve this problem?
Thank you.
You will have to use NSUserDefault or Singleton to handle the values of varaiables accross various Controllers.
These both are used in such a scenario where you need to maintain and access the variable values across multiple View Controllers. You can opt for either one based on your choice.
NSUserDefault can store multiple key-value pairs that are accessible globally throughout the app. Singleton helps you create a object / variable which is static and hence no other instance of it is created afterwards. Only a single instance is retained throughout the app.
The following links might help you.
Singleton Tutorial
Another Singleton Guide
NSUserDefault Tutorial
Another NSUserDefault Tutorial
Hope this helps !
If you required only in you program then you can create it as singleton
// constants.h
+ (RequestParams*) newUser;
// constants.m
+ (RequestParams*) newUser{
static RequestParams* instance = nil;
if (instance == nil) {
// initiate your instance here;
}
return instance;
}
// you can Use it where you required
[constants newUser];
If you you want to keep to when application is closed then you need to use NSUserDefault to save it.
If this help you then you can accept it as solution.

Should I avoid a Singleton in this case?

I've been thinking about using a singleton on my data class in my current app. I've also been thinking of reasons not to use the pattern and currently I am stuck in the middle.
The reason I am for the singleton in this case is due to accessibility to methods and properties easily that I need to use more than once throughout the app. I could use the appDelegate for this but that muddles up the area of concern as these methods / variables have nothing to do with the app state - more to do with user input. The data will be persisted with eventually with NSUserDefaults - which is a singleton already.
The reason I am against it is because its another singleton to an app that already bass one (The appDelegate)
My question:
Would using another Singleton to access the data model be the correct way / acceptable way of doing it - or should I look at another approach?
I personally think there would be nothing wrong with a singleton as a data model - using the app delegate to instantiate it when the app starts and then access its various methods / properties when I need them throughout the app. This would be the only other singleton in the app (Maybe another one for user management - e.g.; logging in / out / profile / credentials, etc? )
Any thoughts?
Thanks all!
There is no problem having multiple singleton classes within ios, in fact in many of my projects most of the classes are singletons. Normally I have the data access logic and control within a singleton and then actual object classes as instances.
As you have already identified you really do not want to be putting something into AppDelegate that does not apply to the whole app.
From my point of view do not instanciate the class from AppDelegate, simply have the class instanciate itself on first access.
Whenever I am explaining singleton Objective-C to people I always direct them to this site, it may be worth a view for you:
http://www.galloway.me.uk/tutorials/singleton-classes/
It explains only what you need in a really easy to understand way. The bit that does the 'self-instanciating' is this bit:
+ (id)sharedManager {
static MyManager *sharedMyManager = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedMyManager = [[self alloc] init];
});
return sharedMyManager;
}
You could use singleton in these case. Don't use delegate to hold values for your app. You may use same singleton for user management also.
Note : Don't use globals unless you need to do so. Singletons and top-level data should be used only when the data they contain truly belongs at the top level.

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?

Resources