patternMinor
Singleton With Sub-Singletons
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
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
if (![firebaseURLsetter hasSuffix:@"/"]) {
firebaseURLsetter = [NSString stringWithFormat:@"%@/", firebaseURLsetter];
}
// Set Our Tools
[FSChat
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;
@endFireSuite.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:
This does mean you'll have to refer to
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:
Third, in your getters for your singletons, you can simply write:
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.
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:
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
But wait, before you switch the return type to
Instead, you'll have to do this:
So instead of a return type of
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.
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?
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.
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].chatManagerInstead, you'll have to do this:
((FireSuite*)[FireSuite suiteManager].chatManagerSo 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.