I was recently tasked with architecting a user/profile object in Objective-C and wanted the ability to access a static instance from the class level, similar in style to the way Parse manages their current user (a static variable on a class, not a singleton). With Parse, I can call a class method to return the current user object if a user is logged in, or nil
if there is no current user session with [PFUser currentUser]
.
I architected my class like so:
User.h
@interface User : NSObject
/// Returns the current user if logged in, or nil if logged out
+ (User *)currentUser;
/// Sets the current user, call this on login
+ (void)setCurrentUser:(NSDictionary *)userInfo;
/// Removes the current user, call this on logout
+ (void)removeCurrentUser;
// Getters
/// Returns the user's name if logged in, nil if logged out
@property (nonatomic, readonly, strong) NSString *name;
@end
User.m
@interface User ()
@property (nonatomic, readwrite, strong) NSString *name;
@property (nonatomic, strong) NSString *address;
@end
@implementation User
#pragma mark - Static Variables
static User *currentUser;
#pragma mark - Static Object Setters and Getters
+ (User *)currentUser
{
return currentUser;
}
+ (void)setCurrentUser:(NSDictionary *)userInfo
{
currentUser = [[User alloc] initWithDictionary:userInfo];
}
+ (void)removeCurrentUser
{
currentUser = nil;
}
#pragma init
- (instancetype)initWithDictionary:(NSDictionary *)userInfo
{
self = [super init];
if (self) {
NSString *name = userInfo[@"name"];
NSString *address = userInfo[@"address"];
if (!name || !address ) { return nil; }
_name = name;
_address = address;
}
return self;
}
@end
This works well. I have a single static User
instance which can be non-nil if a user is logged in, and nil
if the user is logged out. I can access this object from anywhere with [User currentUser]
without a singleton.
Now as an additional challenge I'm trying to translate this type of pattern into Swift. Here is my fist pass:
public class User {
// Private Properties
private var name: String?
private var address: String?
// Private instance of User
private static var user: User?
// Class Functions
public class func currentUser() -> User? {
return self.user
}
public class func setCurrentUser(userInfo: [String : AnyObject]) {
self.user = User(dictionary: userInfo)
}
public class func removeCurrentUser() {
self.user = nil
}
// Instance methods
public func getName() -> String? {
return self.name
}
// Private
private convenience init?(dictionary: [String : AnyObject]) {
self.init()
guard let name = dictionary["name"] as? String else { return nil }
guard let address = dictionary["address"] as? String else { return nil }
self.name = name
self.address = address
}
}
This works but doesn't feel "Swifty" enough.
Questions:
Is there a way to expose a public getter on a private property (sort of like a property redeclaration through a class extension in Objective-C)? Or do I need to write an additional accessor.
Does declaring my private User
object as private static
ensure that it can only be accessed on the class level and not on the instance level? Is this variable really static
like it would be in C or Objective-C?
Can a User
setter and getter be added/modified to remove the need for setCurrentUser
and removeCurrentUser
?
I am open to optimizations in both my Swift and Objective-C code.