I am trying to create a singleton User class in my app, here’s the code:
#import "User.h"
#import "Login.h"
#import "SFHFKeychainUtils.h"
// Constants
static NSString* const kDBUserCurrentUserIDDefaultsKey = @"kDBUserCurrentUserIDDefaultsKey";
// Current User singleton
static User* currentUser = nil;
@implementation User
@synthesize username = _username;
@synthesize password = _password;
@synthesize delegate = _delegate;
- (id)init
{
self = [super init];
if (self) {
// Initialization code here.
}
return self;
}
+ (NSString*)primaryKeyProperty {
return @"username";
}
+ (User*)currentUser {
if (nil == currentUser) {
id username = [[NSUserDefaults standardUserDefaults] objectForKey:@"kApplicationUserNameKey"];
if (!username) {
currentUser = [self new];
} else{
NSLog(@"CURRENT USER");
return self;
}
[currentUser retain];
}
return currentUser;
}
+ (void)setCurrentUser:(User*)user {
[user retain];
[currentUser release];
currentUser = user;
}
/**
* Implementation of a RESTful login pattern. We construct an object loader addressed to
* the /login resource path and POST the credentials. The target of the object loader is
* set so that the login response gets mapped back into this object, populating the
* properties according to the mappings declared in elementToPropertyMappings.
*/
- (void)loginWithUsername:(NSString*)username andPassword:(NSString*)password delegate:(NSObject<DBUserAuthenticationDelegate>*)delegate {
_delegate = delegate;
//[RKObjectManager sharedManager].client.username = username;
//[RKObjectManager sharedManager].client.password = password;
self.username = username;
self.password = password;
RKObjectMapping * userMapping = [[RKObjectManager sharedManager].mappingProvider objectMappingForKeyPath:@"LoginViewController"];
[[RKObjectManager sharedManager] loadObjectsAtResourcePath:@"/account/verify.json" objectMapping:userMapping delegate:self];
}
/**
* Implementation of a RESTful logout pattern. We POST an object loader to
* the /logout resource path. This destroys the remote session
*/
- (void)logout/*:(NSObject<DBUserAuthenticationDelegate>*)delegate */{
NSError * error = nil;
[[NSUserDefaults standardUserDefaults] setValue:nil forKey:@"kApplicationUserNameKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
[SFHFKeychainUtils deleteItemForUsername:self.username andServiceName:@"convore" error:&error];
NSLog(@"LOGGING OUT");
if ([self.delegate respondsToSelector:@selector(userDidLogout:)]) {
[self.delegate userDidLogout:self];
}
[[NSNotificationCenter defaultCenter] postNotificationName:@"DBUserDidLogoutNotification" object:nil];
}
- (void)loginWasSuccessful {
// Upon login, we become the current user
[User setCurrentUser:self];
NSError * error = nil;
// Persist the username for recovery later
[[NSUserDefaults standardUserDefaults] setValue:self.username forKey:@"kApplicationUserNameKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
[SFHFKeychainUtils storeUsername:self.username andPassword:self.password forServiceName:@"convore" updateExisting:TRUE error:&error];
// Inform the delegate
if ([self.delegate respondsToSelector:@selector(userDidLogin:)]) {
[self.delegate userDidLogin:self];
}
[[NSNotificationCenter defaultCenter] postNotificationName:@"DBUserDidLoginNotification" object:self];
}
- (void)request:(RKRequest*)request didLoadResponse:(RKResponse*)response
{
NSLog(@"Loaded payload: %@", [response bodyAsString]);
}
- (void)objectLoader:(RKObjectLoader*)objectLoader didLoadObject:(id)object
{
if ([objectLoader wasSentToResourcePath:@"/account/verify.json"]) {
Login * login = (Login *) object;
if ([login.username length] > 0)
[self loginWasSuccessful];
}
}
- (void)objectLoader:(RKObjectLoader *)objectLoader didFailWithError:(NSError*)error {
if ([objectLoader wasSentToResourcePath:@"/account/verify.json"]) {
NSLog(@"Encountered an error: %@", error);
// Login failed
if ([self.delegate respondsToSelector:@selector(user:didFailLoginWithError:)]) {
[self.delegate user:self didFailLoginWithError:error];
}
}
}
- (BOOL)isLoggedIn {
return self.username != nil;
//return self.singleAccessToken != nil;
}
- (void)dealloc {
_delegate = nil;
[_password release];
[_passwordConfirmation release];
[super dealloc];
}
@end
The issue is that whenever I tried to access currentUser it always breaks down. I first called the loginWithUsernameandPassword and then tried calling the currentUser, but when I call the currentUser on logout, it gives me an error:
calling this:
if ([[User currentUser] isLoggedIn])
gives me:
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '+[User isLoggedIn]: unrecognized selector sent to class 0x1a671c'
seems that currentUser is nil, why is this?
Quick Singleton 101 (I wish I had this when I started, lol. Everyone just pointed me to the docs which didn’t help much). The name of the singleton is going to be “Singleton”
And now the .m
And to set/get, first import the singleton in whatever class you need:
#import "Singleton.h", then grab the singleton withSingleton *singletonManager = [Singleton sharedSingleton];and then you can do whatever you need to as necessary. i.e. to get the description of the NSDictionary you would call[[singletonManager randomDictionary] description];Now this is using ARC, so if you are not you’d just have to make sure you manage your memory correctly. Enjoy.