HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Singleton With Sub-Singletons

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
withsingletonsubsingletons

Problem

I've found myself wanting an easy way to implement chat into various apps, so I developed a set of classes for the firebase.com backend that make it easy for me to quickly set up the nuts and bolts of a chat system. To implement this, I have three classes:

FSPresenceManager

Current Connection / Other User's Connection

FSChannelManager

Send / Receive Alerts From users

FSChatManager

Create Chat / Send & Receive Messages / Light "Queries" (hoping to improve)

These classes act as sort of a notification center where you can register observers for callbacks and the class distributes the information when it comes in from firebase. For this reason, I used singletons in all cases. I decided to wrap all of these classes into a greater class, which is also a Singleton that I called FireSuite.

This allows me to set things like URL, and CurrentUserId for all managers at once in a few lines.

FireSuite.h

#import "FSChatManager.h"
#import "FSPresenceManager.h"
#import "FSChannelManager.h"

@interface FireSuite : NSObject

+ (FireSuite *) suiteManager; // Singleton

@property (strong, nonatomic) NSString * firebaseURL;
@property (strong, nonatomic) NSString * currentUserId;

@property (strong, nonatomic) FSChatManager * chatManager;
@property (strong, nonatomic) FSPresenceManager * presenceManager;
@property (strong, nonatomic) FSChannelManager * channelManager;

@end


FireSuite.m

```
#import "FireSuite.h"

@implementation FireSuite

@synthesize firebaseURL, currentUserId, presenceManager, chatManager, channelManager;

+ (FireSuite *) suiteManager {
static dispatch_once_t pred;
static FireSuite *shared = nil;

dispatch_once(&pred, ^{
shared = [[FireSuite alloc] init];
});
return shared;
}

#pragma mark SETTERS

  • (void) setFirebaseURL:(NSString *)firebaseURLsetter {



if (![firebaseURLsetter hasSuffix:@"/"]) {
firebaseURLsetter = [NSString stringWithFormat:@"%@/", firebaseURLsetter];
}

// Set Our Tools
[FSChat

Solution

There's very little code here to review, and I made some points in my comment to the question regarding this, however, on what is posted, here are some notes...

First of all, Xcode now autosynthesizes, so you can eliminate this line:

@synthesize firebaseURL, currentUserId, presenceManager, chatManager, channelManager;


This does mean you'll have to refer to firebaseURL as _firebaseURL instead, but this actually makes it more clear that you're referring to a private instance variable and not a method variable. Xcode while highlight it properly for you either way, which helps you distinguish, but when posted outside of Xcode, such as Github or Codereview, the highlighting isn't done, so it's easy to miss.

Second, you can slightly improve performance and save some lines of code by putting your three singletons into a collection. Once you've done that (suppose a NSDictionary, so you can use keys to determine what's what), try out this line of code:

[someArray makeObjectsPerformSelector:@selector(setUrlRefString:) 
                           withObject:firebaseURLsetter];


Third, in your getters for your singletons, you can simply write:

return [FSPresenceManager singleton];


Rather than setting a variable then returning the variable.

Fourth, you should be sure to override the setters for the singletons to be instance variables aren't created for them.

- (void)setPresenceManager {
    return;
}


Fifth, you've put a lot of work into giving yourself convenience methods for synching these three singletons. But what prevents me from doing this:

[FireSuite suiteManager].chatManager.urlRefString = @"SomethingDifferentFromTheOtherTwo";


The answer is, nothing.

You might consider making the inner singletons all private, and giving this suite manager a bunch of method wrappers.

Finally, your singleton's return type is FireSuite. I imagine you've done similar for the three inner singletons. If you ever want to subclass any of these for any reason, you're going to run into trouble. If you keep this return type, you're preventing subclassing, which may be something you want to do (see NSNumber, for example). But I don't see any particular reason for preventing subclassing.

But wait, before you switch the return type to id... hold on. If you switch to id, you can't do the following:

[FireSuite suiteManager].chatManager


Instead, you'll have to do this:

((FireSuite*)[FireSuite suiteManager].chatManager


So instead of a return type of id, we're going to want to use instancetype as our return type.

This is some Xcode magic which will assures the type you're returning is the type that's calling the method, even if you subclass. [FireSuite suiteManager] returns a FireSuite object, and [FireSuiteSubclass suiteManager] returns a FireSuiteSubclass object.

Addendum: Why does the chat manager have a delegate and methods that take block arguments?

Couldn't we have two methods to whatever protocol chat manager delegates conform to, one for success and one for error?

@protocol YourChatManagerDelegate
@required - (void)chatSessionLoadDidFailWithError:(NSError*)error;
@required - (void)chatSessionDidFinishLoadWithMessages:(NSArray*)messages;


Now our delegate simply implements these methods, and the chat manager calls these methods rather than executing the completion block which will no longer be passed.

Code Snippets

@synthesize firebaseURL, currentUserId, presenceManager, chatManager, channelManager;
[someArray makeObjectsPerformSelector:@selector(setUrlRefString:) 
                           withObject:firebaseURLsetter];
return [FSPresenceManager singleton];
- (void)setPresenceManager {
    return;
}
[FireSuite suiteManager].chatManager.urlRefString = @"SomethingDifferentFromTheOtherTwo";

Context

StackExchange Code Review Q#44391, answer score: 6

Revisions (0)

No revisions yet.