Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 8 years ago.
Improve this question
How to get sessions variables in iOS. For example I had five view controllers starting from login page. When logged in as a user I was able to get user id from my JSON data. I want this user id to be used in all the screens to get it done in a smoother way. What I did is taking a hidden variable and sending it to the next screen which is a tough thing when we have 100 screens. Is there any other way to set the session variables and access those all over the application?
What I had tried is keeping that NSString in Appdelegate but I was not able to uderstand how to do that.
You don't need a fancy class/singleton/NSUserdefaults to do this: Heres Why.
Session variables need to be anything.
The class itself does not need to maintain state
NSUserDefaults are not a session, they are persisted between app launches.
All you really need is a static class which has a NSMutableDictionary in it.
// Session.h
#interface Session : NSObject
+ (NSMutableDictionary *) sessionVariables;
#end
// Session.m
#import "Session.h"
static NSMutableDictionary *_session;
#interface Session
+ (NSMutableDictionary *) sessionVariables {
if (!_session) {
_session = [NSMutableDictionary alloc] init];
}
return _session;
}
So in every class you want session available all you need to do is import Session.h
and use the class like this [Session sessionVariables] setObject:#"something" forKey:#"someKey"]
and to get things out id someValue =[Session sessionVariables][#"something"];`
The one thing I do recommend is please DO NOT store user/password/sensitive data in any of the answers provided (Including mine), use the iOS Keychain for that.
Save your value in NSUserDefaults and also fetch it from that
for save:
[[NSUserDefaults standardUserDefaults] setObject:STRING_OBJECT_OF_YOUR_JSON_LOGIN_ID forKey:#"UserLoginIdSession"];
[[NSUserDefaults standardUserDefaults] synchronize];
for retrive:
NSString *str = [[NSUserDefaults standardUserDefaults] valueForKey:#"UserLoginIdSession"];
and when user log out from your app set this to #""
[[NSUserDefaults standardUserDefaults] setObject:#"" forKey:#"UserLoginIdSession"];
[[NSUserDefaults standardUserDefaults] synchronize];
You have lot of choices.
Save to NSUserDefaults
[[NSUserDefaults standardUserDefaults]setObject:#"12345" forKey:#"SessionKey"]; // Save session id
[[NSUserDefaults standardUserDefaults]objectForKey:#"SessionKey"];//Access in other view controllers
Keep it AppDelegate as a property, you can access AppDelegate in all the view controllers
Create a shared singleton class to share the data across multiple view controllers.
If you want to keep that information in AppDelegate you only have to create properties in that class (AppDelegate) declared in AppDelegate.h. You can access the appDelegate and its properties from any other part of the app:
sessionData = [(MyAppDelegate*)[[UIApplication sharedApplication] delegate] sessionProperty];
I use to define a macro to avoid that verbosity. In AppDelegate.h :
#define myAppDelegate ( (MyAppDelegate*)[[UIApplication sharedApplication] delegate] )
So I can access its methods from anywhere in the app:
sessionData = myAppDelegate.sessionProperty ;
Nevertheless, keeping session data in app delegate is not always a good practice. I much prefer to use a singleton to keep session info. For example:
sessionData = [[SessionManager sharedManager] sessionProperty] ;
To create a singleton, you only have to create a new class and redefine alloc and copy. And, obviously, define the method sharedManager.
First of all: Obviously you are parametrizing every view controller with the userID. Nothing is wrong with doing so and it keeps information encapsulated in a good way. Yes, this can be additional work. Yes, encapsulating data in a good way is additional work.
However, if you want to change it, it is one way to have a property of the application delegate. Keep in mind: This creates a dependency of every class that uses the userID to the application delegate. This reduces the ability of code reuse. Therefore it is a good idea to have an additional class Session. Create one at the login process and store it in the application delegate.
The whole process:
A. Create a class Session
#interface Session : NSObject
#property (readonly, copy) NSString *userID;
#end
#interface Session()
#property (readwrite,copy) NSString *userID;
#end
#implementation Session
- (id)initWithUserID:(NSString*)userID
{
self = [super init];
if (self)
{
self.userID = userID;
}
return self;
}
#end
B. Create an instance and store it in the application delegate
…
// login
NSString *userID = …;
self.session = [[Session alloc] initWithUserID:userID];
…
C. Using the user ID
To use that you have to import the headers of your application delegate class and the session class:
//
#import "AppDelegate.h" // To get the session instance
#import "Session.h" // To get the user ID
…
Session *currentSession = [UIApplication sharedApplication].delegate.session;
NSString* userID = currentSession.userID;
…
You can get rid of the dependency to AppDelegate, if you implement a shared instance of Session. Doing so the code be a little bit more straight forward:
//
#import "Session.h" // To get the user ID
…
Session *currentSession = [Session currentSession];
NSString* userID = currentSession.userID;
…
How to create such a shared instance (called singleton, even it is not) is explained on SO in many, many answers.
Related
I have a couple of objects stored in the user's NSUserDefaults which I have to use more or less in every single ViewController of my app.
Currently, I basically have the same 3 variables declared, and in the viewDidLoad I initialise them like:
if(....){
chosenID = [[[NSUserDefaults standardUserDefaults] valueForKey:#"chosen_xxxx"] integerValue];
chosenName = [[NSUserDefaults standardUserDefaults] valueForKey:#"chosen_name"];
}else{
chosenID = [[[NSUserDefaults standardUserDefaults] valueForKey:#"chosen_xxxx_2nd option"] integerValue];
...
}
I'm looking to clean up my code and optimize my code, and I was wondering what the right way to handle a case such like this was, to avoid having these 10-12 exact same lines of code at the start of every single ViewController.
Write an utility class. And create some class methods.
One method can be like,
+ (NSString *)choosenName {
return [[NSUserDefaults standardUserDefaults] valueForKey:#"chosen_name"];
}
And call the method like,
chosenName = [Your_Utility_Class choosenName];
Yes you can achieve it globally by following simple method.Create NSObject class,please refer my example below.
.h File
//Setting up Session
+(void)SetEmail:(NSString*)value;
+(void)SetFirstName:(NSString*)value;
//Retrieve
+(NSString*)GetEmail;
+(NSString*)GetFirstName;
.m file
+(void)SetEmail:(NSString *)value{
[[NSUserDefaults standardUserDefaults] setObject:value forKey:#"EMAILID"];
}
+(void)SetFirstName:(NSString *)value{
[[NSUserDefaults standardUserDefaults] setObject:value forKey:#"FIRSTNAME"];
}
+(NSString*)GetEmail{
return [[NSUserDefaults standardUserDefaults] stringForKey:#"EMAILID"];
}
+(NSString*)GetFirstName{
return [[NSUserDefaults standardUserDefaults] stringForKey:#"FIRSTNAME"];
}
Now Move to Viewcontroller and access without alloc init as it is in Class method.I am setting up from result.
Setting up in Viewcontroller
[NSDefaultSession SetEmail:#"YourString"];
[NSDefaultSession SetFirstName:#"YourString"];
Now Getting Session from any ViewController
[NSDefaultSession GetEmail]
[NSDefaultSession GetFirstName]
Firstly you should put these values into an object, and secondly use dependency injection.
So first make a class Chosen (for want of a better name) and give it the properties id and name. Now the only thing that needs to worry about where the data is saved and loaded from is the 'Chosen' object, everything else will go through that.
Ok now the dependency injection. You want your VC dependencies to be obvious and clear, don't rely in singletons like NSUserDefaults hidden away inside them. So make .chosen a public property on each of the VC's that needs access to the object.
Init this object in application:application didFinishLaunchingWithOptions: and now inject that into your initial viewController. (ie, set the public property)
Now just pass along the object again by injection to each of the other viewController that needs access to it.
in my option:
create commonClass (sub class of NSObject class)
crate spare methods and use them where u need.
ex:
in ur vc1:
set the values for ur objects
chosenID = [[[NSUserDefaults standardUserDefaults] valueForKey:#"chosen_xxxx"] integerValue];
chosenName = [[NSUserDefaults standardUserDefaults] valueForKey:#"chosen_name"];
in ur common class:
+(NSString *) chosenID{
NSUserDefaults *serverDefults=[NSUserDefaults standardUserDefaults];
NSString * chosenID =[serverDefults objectForKey:#"chosen_xxxx"];
if (chosenID.length==0) {
// do some actions
}
return chosenID;
}
in another VC2:
NSString *id =[commonClass chosenID];
This is the right way to address this issue. Common functionality is what sub-classing is all about - your view controllers are a specific type of view controller that needs access to these variables.
Create class BaseViewController, which is a sub-class of a UIViewController.
Give BaseViewController two public properties called chosenId and chosenName.
Add the init code you have to the viewDidLoad of BaseViewController.
Remove the init code from each of your existing view controllers.
Make any view controller that requires these variables of type BaseViewController intead of UIViewController.
Those variables are now magically (and consistently) available in all of your view controllers without any code duplication.
I want to initialize my singleton object which stores and manages the application settings over the entire class within my app. Also, the singleton instance should be initialized by loading the data from NSUserDefaults upon launch. However, I'm not fully sure where I should initialize the singleton upon launch.
In Cocoa app, I first wrote the singleton initialization code within applicationWillFinishLaunching:, taking parameters from NSUserDefaults. However, later I found that this doesn't work properly if I also write the singleton initialization code (taking no parameter!) within my initial view controller, set in storyboard, because the viewWillLoad:, viewDidLoad: etc. of the class of the view controller are called before the applicationWillFinishLaunching:.
So now I'm sure I should write the singleton initalization code within viewWillLoad: earlier than applicationWillFinishLaunching, but still not sure whether it is appropriate. Specifically, I know the NSApplicationMain is the first method to be called upon launch, but it seems that the next method is not anything within AppDelegate, at least if you use storyboard.
To summary, what I want to ask are the following:
What method from what class will be called after NSApplicationMain, if you use storyboard.
Where should I write my singleton initialization code within my app? I want to initialize it as soon as possible.
Does it differ between iOS and OS X app?
You should initialize it when it's first accessed. Something like this, maybe:
+ (instancetype)sharedInstance {
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
_instance = [[self alloc] init];
});
return _instance;
}
As a side note, if you're literally only using this class as an accessor to NSUserDefaults, you might want to consider using static methods instead.
+ (id)mySpecificDataPoint {
return [[NSUserDefaults standardUserDefaults] objectForKey:#"whatever"];
}
+ (void)setMySpecificDataPoint:(id)data {
[[NSUserDefaults standardUserDefaults] setObject:data forKey:#"whatever"];
}
Or maybe a more well-designed way might be to add a category to NSUserDefaults for this purpose.
#interface NSUserDefaults (MyData)
#property (nonatomic) NSString *someDataPoint;
#property (nonatomic) NSInteger somePrimitiveDataPoint;
#end
#implementation NSUserDefaults (MyData)
- (NSString *)someDataPoint {
return [self objectForKey:#"someDataPoint"];
}
- (void)setSomeDataPoint:(NSString *)someDataPoint {
[self setObject:someDataPoint forKey:#"someDataPoint"];
}
- (NSInteger)somePrimitiveDataPoint {
return [[self objectForKey:#"somePrimitiveDataPoint"] integerValue];
}
- (void)setSomePrimitiveDataPoint:(NSInteger)somePrimitiveDataPoint {
[self setObject:#(somePrimitiveDataPoint) forKey:#"somePrimitiveDataPoint"];
}
#end
You init the singleton when you have to use it. So as Daji Djan said: lazy wins. Just take attention that, you should not do a long-run process in your applicationWillFinishLaunching, it should return as soon as possible.
If the singleton is not mandatory during applicationWillFinishLaunching, you should call it in viewWillAppear of first view controller if you need to initialize it ASAP.
lazy always wins
if you can get away with it: as late as possible :) AND always do the minimum needed (but do as much as is reasonable to keep your code clean!)
Closed. This question does not meet Stack Overflow guidelines. It is not currently accepting answers.
Questions asking for code must demonstrate a minimal understanding of the problem being solved. Include attempted solutions, why they didn't work, and the expected results. See also: Stack Overflow question checklist
Closed 9 years ago.
Improve this question
I need to find a way to make an object which stores patient information "global" to my application in objective c. Example, class A creates the object, (its basically a user name/ password screen). When they close out the application, I would like the appdelegate's applicationdidEnterBackground to read information from this object(which was created in Class A).
You have a few options:
Make your object a property on your App Delegate
Make it a singleton
One of the options is to create a singleton: https://developer.apple.com/library/mac/documentation/general/conceptual/devpedia-cocoacore/Singleton.html
You can also simply write that data inside NSUserDefaults or to a file, a read it afterwards - if you are using this only occasionally it's better idea.
Make this as a public property of your view and read it from appdelegate - however this is not best option if you later change the view to be a subview or you also lose this object when you gets deallocated.
singleton is the way:
you can make class as UserdataSingleton which overrides NSObject. which you can use all over your application to share data globally (for your case array). this code template may help you:
#import <Foundation/Foundation.h>
#interface UserDataSingleton : NSObject
{
#private
NSArray *globalArray;
}
+(UserDataSingleton *) getInstance;
-(void)saveInUserDatasingletonWithArray:(NSArray *)array;
-(NSDictionary *)getGlobalArray;
#end
and implementation file will be some thing like:
#import "UserDataSingleton.h"
#implementation UserDataSingleton
static UserDataSingleton *userDataSingletonInstance;
+(UserDataSingleton *) getInstance
{
if (userDataSingletonInstance == nil) {
userDataSingletonInstance = [[UserDataSingleton alloc] init];
}
return userDataSingletonInstance;
}
-(void)saveInUserDatasingletonWithArray:(NSArray *)array
{
globalArray = array;
}
-(NSDictionary *)getGlobalDictionary
{
return globalArray;
}
#end
usage:
#import "UserDataSingleton.h"
define USERDATASINGLETON (UserDataSingleton *)[UserDataSingleton getInstance]
......................your code...
NSArray *this_IS_Array_Populated_here_For_Global_Access = [NSArray alloc] initWith....];
[USERDATASINGLETON saveInUserDatasingletonWithArray:this_IS_Array_Populated_here_For_Global_Access];//you put your array for global access.
later some where in any other view or view controller you can get that global array for example lets say you have YourViewController class:
NSMutableArray *yourArrayFromWebResponse = [USERDATASINGLETON getGlobalArray];
Class name MyData has 75+ properties that are needed in throughout 7 Scenes.
Currently, I pass the the instance of MyData file with the code below:
in SceneOne:
MyData *myData = [[MyData alloc]init];
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender
{
UIViewController *destinationViewController = segue.destinationViewController;
SceneTwo *sceneTwo = [destinationViewController isKindOfClass:[SceneTwo class]] ? (SceneTwo*)destinationViewController : nil;
sceneTwo.myData = self.myData;
}
This allows me to access any properties as myData.anyProperty
When the 7th Scene is dismissed, I set myData = NULL, and the app returns to SceneOne and a new instance of MyData is initialized.
I'm trying to accomplish above via sharedInstance.
MyData.h
#import <foundation/Foundation.h>
#interface MyData : NSObject {
#property (nonatomic, retain) NSString *someProperty;
// 74 other properties
+ (id)sharedData;
#end
MyData.m
#import "MyData.h"
#implementation MyData
#synthesize someProperty;
+ (id)sharedData {
static Mydata *sharedData = nil;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
sharedData = [[self alloc] init];
});
return sharedData;
}
#end
Question 1: Will the code above work to access the same instance with the code below in any of the Scenes:
MyData *myData = [MyData sharedData];
Question 2: Can I still access the files as myData.anyProperty ?
Question 3: How do I trigger a new instance of MyData and delete the current instance?
Question 4: I'm downloading a myData file off the web that's a duplicate of MyData class but the properties contain data, and I want Singelton to provide a new instance with the data from the downloaded file, what would be the code for that? i.e myData = [newDownloadedFile copy];
Question 5: Is there an advantage of using Singleton Method Vs. my current method?
Yes
Yes
You don't. Why do you believe this is necessary? Can you instead add a reset method to MyData?
You shouldn't mix the singleton pattern with a multiple-instance usage case. If you truly want a singleton, think about adding an additional layer to your data set. For example, you may have a local data and remote data configuration. If this is what you want, you may have to change the interface (header) of MyData to make this possible.
A singleton is a single instance of a class across a process. When you want to access the same collection of data from multiple locations in your code, a singleton object is one way you can accomplish this. Otherwise you need to instantiate an object and pass its address to all interested classes so they are each accessing the same instance. That's an oversimplification but I believe it addresses your question.
Regarding your comment for number 3, if you have a singleton, you don't want to reset the data for the entire app if you simply don't need the data in one place anymore. So consider the impact that would have. If you no longer need the MyData object, simply don't use it anymore. Singleton objects typically persist for the lifetime of an app, so it is not common to release/dispose of the object.
For number 4, say you currently have a property called player with a method declaration like this:
- (Player *)currentPlayer;
If you have multiple configurations available, you would add a parameter to your method interface and implementation like this:
- (Player *)currentPlayerForConfiguration:(NSInteger)configuration;
You could decide to use a number, string, or something else to differentiate between different configurations of your data. If you use a number, 0 could be local, 1 could be remote, 2 could be either (for example, check local data first, and if not there, then check remote). If you only have two options, you could use a BOOL and define your method like this:
- (Player *)currentPlayerUsingLocalData:(BOOL)useLocalData;
Say a user logs in to a certain service in my app. How do I "remember" that the user did this, so it will no longer prompt him/her to do this?
For example, every time the app launches I want it to do something with the user account if they are logged in otherwise, don't do anything. The user logs in from an optional login window they can invoke. Once they login, I'd like to (conceptually at least) set a variable so I can be like if (userIsLoggedIn) { ... } and do things accordingly.
Is this NSUserDefaults? CoreData?
You can easily store it in NSUserDefaults.
// init
NSUserDefaults *defaults = [NSUserDefaults standardUserDefaults];
// set the default value
[defaults setObject:#"YES" forKey:#"loggedIn"];
// read value
NSString *loggedIn = [defaults objectForKey:#"loggedIn"]
Update:
Set with BOOL:
[[NSUserDefaults standardUserDefaults] setBool:YES forKey:#"loggedIn"];
read the value:
if(![[NSUserDefaults standardUserDefaults] boolForKey:#"loggedIn"]) {
// go to login screen
} else {
// go to main screen
}
NSUserDefaults exposes your app to security risks if you are using login credentials.
I would recommend using NSKeyedArchiver.
This site has a fantastic simple explanation on how to implement it!
http://haoxiang.org/2011/11/ios-how-to-save-and-load-a-custom-object/
Apple API Doc just for future reference.
https://developer.apple.com/library/mac/#documentation/Cocoa/Reference/Foundation/Classes/NSKeyedArchiver_Class/Reference/Reference.html
As the other answers have pointed out NSUserDefaults is useful if you are storing simple non private state.
I generally like to wrap this up with a category so that I can interact with user defaults easier and not have to worry about keys etc.
An example would look like this category
NSUserDefaults+PASAuthorisation.h
#interface NSUserDefaults (PASAuthorisation)
#property (nonatomic, assign, getter=pas_isLoggedIn) BOOL pas_loggedIn;
#end
NSUserDefaults+PASAuthorisation.m
#import "NSUserDefaults+PASAuthorisation.h"
static NSString * const PASLogginInKey = #"PASLogginInKey";
#implementation NSUserDefaults (PASAuthorisation)
- (BOOL)pas_isLoggedIn;
{
return [self boolForKey:PASLogginInKey];
}
- (void)setPas_loggedIn:(BOOL)pas_loggedIn;
{
[self setBool:pas_loggedIn forKey:PASLogginInKey];
}
#end
[[NSUserDefaults standardUserDefaults] setObject:#YES forKey:#"isLoggedIn"];
Valid comments: you can also use setBool:forKey with the same result. Calling synchronize right after this assignment also does not hurt (although it is probably not critical).
Core Data isn't really appropriate for small, unique pieces of data like this.
NSUserDefaults is the right place for small things like this, but if those small things are supposed to be secret -- like login details -- the Keychain is better.