I'm trying to figure out the smartest, most reliable way to make my retrieved PubNub history accessible for several view controllers. As i see there are multiple ways to do that, but after a bunch of questions and articles i've read i can't decide which solution would be the best. Actually i think dependency injection would be the right idea in my case, but i'm not sure because i saw different solutions in the sample PubNub apps and overall i never heard about that so i would avoid it, if it's possible.
The possible ways:
1. Using AppDelegate
It could be the easiest way, but a lot of developers said, that AppDelegate is really not the best place for storing global data. So i wouldn't like to do this way.
2. Using Singleton
As an instance,i saw this solution in the PubNub's iPad demo app (PNDataManager file). I was sure this is the best way, but after i read Stephen Poletto's article on objc.io about singletons i've changed my mind, because he pointed to some issues, that can be substantial in my case.
It's a really useful article, that's worth reading, but actually i will grab only the thoughts that i found important in my situation.
"suppose we’re building an app where users can see a list of their friends. Each of their friends has a profile picture, and we want the app to be able to download and cache those images on the device. With the dispatch_once snippet handy, we might find ourselves writing an SPThumbnailCache singleton"
Suppose we want to cache the message
array instead of images, that we retrieved with this method [PubNub requestFullHistoryForChannel: withCompletionBlock:^(NSArray *message, PNChannel *channel, PNDate *fromDate, PNDate *toDate, PNError *error)}];
in our root view controller called ViewController1
.
"We continue building out the app, and all seems well in the world, until one day, when we decide it’s time to implement the ‘log out’ functionality, so users can switch accounts inside the app. Suddenly, we have a nasty problem on our hands: user-specific state is stored in a global singleton. When the user signs out of the app, we want to be able to clean up all persistent states on disk. Otherwise, we’ll leave behind orphaned data on the user’s device, wasting precious disk space. In case the user signs out and then signs into a new account, we also want to be able to have a new SPThumbnailCache for the new user. The problem here is that singletons, by definition, are assumed to be “create once, live forever” instances. You could imagine a few solutions to the problem outlined above. Perhaps we could tear down the singleton instance when the user signs out
The problem here is that singletons, by definition, are assumed to be “create once, live forever” instances. You could imagine a few solutions to the problem outlined above. Perhaps we could tear down the singleton instance when the user signs out..We could certainly make this solution work, but the cost is far too great. For one, we’ve lost the simplicity of the dispatch_once solution, a solution which guarantees thread safety and that all code calling [SPThumbnailCache sharedThumbnailCache] only ever gets the same instance. We now need to be extremely careful about the order of code execution for code that utilizes the thumbnail cache.... Since there’s no distinct owner for the singleton instance (i.e. the singleton manages its own lifecycle), it becomes very difficult to ever ‘shut down’ a singleton. The lesson here is that singletons should be preserved only for state that is global, and not tied to any scope. If state is scoped to any session shorter than “a complete lifecycle of my app,” that state should not be managed by a singleton.A singleton that’s managing user-specific state is a code smell, and you should critically reevaluate the design of your object graph."
When the user signs in to a new account, we should be able to construct and interact with a brand new SPThumbnailCache, with no attention paid to the destruction of the old thumbnail cache. The old view controllers and old thumbnail cache should be cleaned up lazily in the background on their own accord, based on the typical rules of object management. In short, we should isolate the state associated with user A from the state associated with user B.
I know one NSArray
with PNMessage
objects, that doesn't contain images is not the same and it's easier to handle them, but i'm really confused how should i deal with them.
Originally i would pass the message
variable inside the requestFullHistoryForChannel: withCompletionBlock:
to an instance variable called self.messageGlobal
(that i can use with a singleton in every views) and reset it when the user logs out.
//inside the viewWillAppear
[PubNub requestFullHistoryForChannel: withCompletionBlock:^(NSArray *message, PNChannel *channel, PNDate *fromDate, PNDate *toDate, PNError *error)
// self.messageGlobal should be managed by a singleton or appdelegate or use the dependency injection??
self.messageGlobal = message;
}];
- (IBAction)logOutButton:(id)sender {
//disconnect from the PubNub network
[PubNub unsubscribeFromChannel:currentUserChannel];
//prepape ivar for the new message history
[self.messageGlobal removeAllObjects];
// log out user (i'm using parse.com for managing users)
[PFUser logOut]
}
After Stephen's article i feel i'm lost, it would be amazing if somebody could explain the right direction.
My plan is to create a method in the ViewController1
, that handles the history.
-(void)downloadPubNubHistory {
[PubNub requestFullHistoryForChannel: withCompletionBlock:^(NSArray *message, PNChannel *channel, PNDate *fromDate, PNDate *toDate, PNError *error)
// self.messageGlobal should be managed by a singleton or appdelegate or use the dependency injection??
self.messageGlobal = message;
}];
}
In the AppDelegate
or ViewController1
i would place an observer to get notified when the current users channel recieved a new message, it's important because i would like the call downloadPubNubHistory
everytime he or she receives a new message and update the current view with the new content.
[[PNObservationCenter defaultCenter] addMessageReceiveObserver:currentUserChannel withBlock:^(PNMessage * msg) {
[ViewController1 downloadPubNubHistory];
} }];
For example in ViewController3
i list every message that was sent by sampleUserB to the channel of sampleUserA (current user), if sampleUserA's channel receives a new message from sampleUserB on her/his channel i need to reload downloadPubNubHistory
, and also reload ViewController3
, because i'm filtering the self.messageGlobal
in the ViewController3
to get messages only from sampleUserB
.
Any opinion and suggestion welcomed, it's possible that i misunderstood the whole concept and i could basically use singleton and reset it everytime when the user log out without any problems, but i would like to utilize the most lightweight reliable solution. If there is any other recommended technique i would love to hear it too.
Original source of the article