I would like to create a GCD element in a shared static object (say, called Manager) that will, every few seconds, fetch some data from an external repository (e.g. URL) and change a UIElement in the application ViewController.
My thinking is to:
initialize the Manager object in the AppDelegate initialization methods (as soon as the App gets lunched)
In the initialization method start an operation queue that fetches the data and, after every fetch, verifies if the new content is different than the previous one (that would be locally stored in a variable inside the Manager class.)
If the content is different change the UIElement (e.g. if it is a string then the UILabel would change, if it is an Image URL resource then the UIIMage will change).
I have no idea on how to access to the UILabel element from the Manager class. Am I going in the wrong direction or is there some other way/pattern to do this? (I suspect that I need to create a static Logic class that can be accessed by the callback methods function inside from the Manager class and that have reference to the ViewController that contains the UIElements)
Any simple but good tutorial would be of much help.
You can achieve this through delegation. Define a protocol for the Manager class called ManagerDelegate. Then when the manager does its work it can send the delegate information about what it has done or will do.
Some sample delegate methods could be:
- (void)managerBeganNewFetchRequest:(Manager *)manager;
- (void)manager:(Manager *)manager foundNewData:(id)data;
- (void)manager:(Manager *)manager didReceiveDuplicateData:(id)data;
Here's some info on delegation from the Apple's documentation: https://developer.apple.com/library/ios/documentation/general/conceptual/DevPedia-CocoaCore/Delegation.html
Post a notification.
In your ViewController with the UILabel you want to change, in viewDidLoad, do something like this:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateLabel) name:#"ManagerUpdated" object:nil];
Then in your manager class whenever you want to update the label:
[[NSNotificationCenter defaultCenter] postNotification:#"ManagerUpdated" object:nil];
You can den send data along with the object: parameter, instead of being nil it can be whatever you want.
Related
I have three Xibs (A, B, C). From A I am going to B. So If I have to pass data to A and get back to A, I have written a delegate and I am dismissing the Controller. This is fine.
But now my requirements are that I have to go from A to B and from B to C. Now from C I have to pass data to A and come back to A.
How to do this?
Note: I am not using StoryBoard or Navigation Controller. And Controller A is not root View Controller
Add a notification observer with a name in controller A, Then from where you want to send data (in your case B, C), From there post a notification with the name you are observing in controller A.
You can use NSNotificationCenter for passing data from viewcontroller to another view controllers.
You can try NSNotificationCenter as shown below,
Example:
In your ViewControllerA.m
-(void)viewDidLoad
{
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(dataReceived:) name:#"passData" object:nil];
}
-(void)dataReceived:(NSNotification *)noti
{
NSLog(#"dataReceived :%#", noti.object);
}
In your ViewControllerC.m
-(void)gotoHome
{
[[NSNotificationCenter defaultCenter] postNotificationName:#"passData" object:[NSDictionary dictionaryWithObject:#"Sample Data" forKey:#"dataDic"]];
[self.navigationController popToRootViewControllerAnimated:YES];
}
Check this example in below link:
https://stackoverflow.com/a/22501709/5349267
I extend the answer of Sommm as it is my wish to underline how important
MVC, the model-view-controller pattern,
as a design pattern in iOS as well as macOS applications really is and how useful it is if one obeys to it.
Good solution: Use NSNotificationCenter in order to let parts of your application communicate that don't know about each other usually. You register controllers that are waiting for information as observers for a specific message type and then post from other parts of your code what should be sent to the observers (using the specific message type the observers are waiting for).
Much better solution: Don't use NSNotificationCenter and instead design your application better and obey to the MVC design pattern.
The OP's initial question somehow shows (at least to my mind) a common misunderstanding: Don't try to create a specific UI flow and write your code accordingly but think of your application in a more general way first. As you don't give enough information on the question what you really want to do, I will assume you are generating an instance of a class User in your three XIBs called A, B and C.
Model: Write a class User that has the needed properties like firstname, surname and email
Controller: Write a controller UserController that holds an array of every instance of the class User that exists in your application.
View: Now (and only now!) think about your View. First question: Do you really need three XIBs or could you use one with three UIViews? The views could be IBOutlets of a class say UserViewController. Make this class the delegate of your single XIB file and connect the IBOutlets. Your UserViewController could then have a property user of the instance of User it represents and set the properties of user according to what has been inserted in the UITextFields for example.
If this is too complicated to you, you can use the NSNotificationCenter in order to send messages (with content if needed) between different parts of your application. If you find yourself using it very often: increased usage of this technique usually is a sign of a badly constructed application that needs some refactoring.
I'm trying to think through the ramifications of passing an NSNotification object as a var to a method.
It seems like it's difficult to trace what happens to a the flow of logic with a notification anyway (without some great commenting and method/object naming) so passing the notification to another method/class/lib/project would just make it even less fun to debug or respond intelligently to an error.
But it just feels like it's the sort of thing that has a lot of hidden gotchas potentially.
And I know there are still a lot of people out there who love notifications.
So does anyone have some pros/cons/better-practices about passing the actual NSNotification object as a var between methods?
Just for clarity's sake here's some pseudo code:
[[NSNotificationCenter defaultCenter] postNotificationName:kSuperImportantNotification object:self userInfo:soMetaDict];
... Later in the code:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(importantThingHappened:) name:kSuperImportantNotification object:nil];
... Later in the code:
- (void) importantThingHappened:(NSNotification *)notification {
[respondingInterestedClass executeSomethingWith:notification];
}
... In "respondingInterestedClass":
- (void)executeServicesDiscoveredBlock:(NSNotification *)notification {
// ... do something with the NSNotificaiton object.
}
Thanks
It depends in what you are doing, usually this is when to use what:
One-to-One relationship and loose coupling: Delegate
One-to-One relationship and tight coupling: Direct method calls
One-to-Many relationship and loose coupling: Notifications
One-to-Many relationship and tight coupling: KVO
So you should not use Notifications for all kind of communication, lot of developers use them heavily even when its not needed.
Saying this, it is totally fine to pass the notification object to the method handler, but don't pass it anywhere else.
Putting aside how and when notifications should be used and focusing purely on the ephemeral set of uses that are totally the right choice....
Once received, the notification object is a state container that contains some set of state in a format that is convenient to the system. You can shoehorn whatever you want into it by shoving key/value pairs into a dictionary and passing it as the userInfo, if you want, but it is still the system state.
In a well designed body of code, there are hard fought barriers through which the format of the data and the encapsulated messaging are well defined. In a Model-View-Controller system, for example, a Model class won't be a subclass of UIView or handle user events directly.
And Notifications should be treated the same. When the notification is received, unwrap it into whatever internal-and-specific-to-your-architecture representation you want and pass that around.
Or, consider:
- (void) somethingChangedNotification:(NSNotification *)changed
{
[myModelGoop applyChange:...];
}
#implementation MyModelLayer
- (void)applyChange:(....)change
{
}
In the above, you could certainly pass the notification object into the model layer. But that's just pushing a system specific implementation detail into your model.
What happens the moment you have changes that aren't triggered via an NSNotification? Your either left to encapsulate your change into a fake NSNotification instance (because posting a notification may have side effects depending on who is observing) or you have to refactor the above to no longer take a notification in the model layer.
Better to get rid of the dependencies on system encapsulation early.
To that end, the NSNotification instance should never be passed out of the method that handles the notification.
In my view controller I am calling a method to request data to populate my tableView and handle any notifications at viewWillAppear and also with a notification observer for UIApplicationDidBecomeActiveNotification.
This appears to cause problems when I am initially launching the app (not from the background) because my loadJSON method gets called twice, causing cellForRowAtIndexPath to crash as my data is changing.
Anyone have a suggestion on how this is typically handled?
You can test you loadJSON task is or not is executing before call it.
Or you can cancel privious loadJSON task before perform it.
I use global object to manage data, that I should download from different places.
My object (for example, named DataManager) has notifications, block-callbacks or delegate to notify listeners about data updating.
Also it has method to check his state, for example: isDownloading. If my DataManager more complex class, it has enum for states or many methods for any aspect.
Now I do not like to use Singleton for implementation of DataManager, I prefer to creating a property in AppDelegate to store instance of the manager inside.
I have a shared singleton that contains all relevant information on the current user and its session, through the object [IWSession session] and [IWSession session].currentUser.
The current user (which actually refers to the one logged in the application) might have some of its properties updated frequently through webservice calls (triggered by iBeacon, triggered by a change in its location, etc).
This implies to update the GUI accordingly at different places in the app, let's say 5 or 6 class instances.
What's the proper way to update information displayed in the app as soon as any property is updated ?
I thought about
1) Adding a KVO on the [IWSession session].currentUser on himself and for all properties regarding the following link
Key Value Observing - how to observe all the properties of an object?
2) The KVO would then trigger a
[[NSNotificationCenter defaultCenter] postNotificationName:#"userUpdated" object:nil];
and all classes which need their layout to be updated would listen to that notification.
Is it a good approach ?
Any other suggestion ?
If you use Notifications then memory will not be released for all the objects in which notification is posted. Because if you use notifications then reference to the object is stored in the heap. So I don't this it would be a good idea to use Notifications.
I recently have been using a weak-observer list using hashtables
mObservers = [NSHashTable weakObjectsHashTable];
with semi-delegation messages, for example:
#protocol UserSessionObserver <NSObject>
- (void) userSession:(id)session didUpdateUser:(id)userProfile;
#end
So, any object that would be interested in changes to userProfile or userSession can simply add itself as an ad-hoc observer to the shared userSession. Because it is a weak entry, the object will be removed automatically from the observer table on dealloc.
Trick is to create the correct addObserver message:
- (void) addObserver:(__weak id<UserSessionObserver>)observer;
While this requires you to write your own observer logic, it also means:
1. No memory management issues with strong retain cycles,
2. Simplified and to the point observer messaging (the system notifications and KVO implementation is so generic, it is bordering on incomprehensible imo, but also adds several layers of logic that merely slows down processing).
An example implementation: Weak Observer example
I want to remove a notification observer and I am using the method:
[[NSNotificationCenter defaultCenter] removeObserver: name:#"myNotification" object:nil];
for this. Now there are many observers who are listening to this notification and I want to remove all of them in one shot from a centralised place. Can I pass 'nil' in first parameter and it will remove all observers who are listening to myNotification?
You can remove an object from the notification center all together which means no notifications will get triggered. For example, when I have a view controller that has registered for notifications, I include this line in my dealloc.
[[NSNotificationCenter defaultCenter] removeObserver:self];
This is at the object level...so it will unregister for many notifications. It won't unregister for one notification in many objects.
Hope I understood your question correctly.
In case of Swift, you doing it like this:
NSNotificationCenter.defaultCenter().removeObserver(self)
And in Swift 3:
NotificationCenter.default.removeObserver(self)
Unfortunately, there is no way to remove all observers of a specific notification in one place. While there are certainly cases where this would be nice, it would be a dangerous thing to do as generally, the object doing the observing should be responsible for adding and removing itself as an observer of a particular notification. This ensures no unpredictable behavior b/c observers can come and go so they configure and clean up after themselves.
If an object that generates notifications goes away, it won't matter to the observer as the observer doesn't know about that object anyway. It just means that object won't be generating any more notifications.
[EDIT: RESPONSE TO YOUR COMMENT RE CLASS B STOPPING CLASS A FROM OBSERVING]
I just saw your comment. There are different ways to accomplish this, particularly if class B knows about class A. As you reference classes it sounds like you want to affect all instances of a class vs a particular instance. If you have some condition you can check when handling the notification, that's how I would approach this. In the notification handler something like:
if ([self shouldRespondToNotificationNamed:notification.name]) {
[self performNotificationAction];
}
If you don't have a condition you can check, then create one either in the class in question as an iVar or in a place where you can access it globally for all class instances. I generally use a singleton to store global app state that doesn't persist. If it persists, then use whatever method you're using for other state.