Have you ever wanted to alert other classes of your app of some event from another one?
Sure, you could have your class poll the other one and keep asking if something has changed, but that seems to be wasteful, since in many cases, there usually won’t be. You could even not bother with polling, and just read it back in all the time, but if there has been no change, that again is wasteful of processing time, power, etc. To avoid these issues, I have found that NSNotification Center is a great answer to this problem. You can just have your class subscribe to the Notification, and then when it alerts your class that there has actually been an event you’ve been waiting for, you THEN can call that class and read in what is necessary, or react appropriately.
Now if you have been reading this blog for a bit, you might think, “Yeah yeah, we know about NSNotification center, you wrote about it in a couple of your earlier posts: Supporting Dynamic Type for iOS7 Apps, and Updating an app when returning from background in iOS.” You would be right, but in both of those, I talked about subscribing to them, but not generating them yourself.
In the case of the app I am working on, I needed this to be implemented in a class that handled the storing of settings, so during this post, I will refer to the class that will send notifications as the SettingsModel.
Creating an NSNotificationCenter string constant
If you look in my post about updating an app when returning from background, it has to subscribe to a notification by name, in that case UIApplicationWillEnterForegroundNotification. We need to get read to do the same, so in your SettingsModel.h, you should add the line:
extern NSString* const SettingsModelChangedNotification;
Then of course you have to actually set it up in your implementation file, so in SettingsModel.m, you add:
NSString* const SettingsModelChangedNotification = @"MyApp-SettingsModelChanged";
It does not really matter what you write as the text in the NSString, other than that it should be unique, at least among the Notification keys, so that people can just subscribe to the specific notification that they want.
I actually have not used extern much myself, so I had to look up a bit about it and do some testing to accurately explain it. I found a lot of the information I needed from this article: Understanding “extern” keyword in C, it even has examples!
When I deleted the “extern” from my class, it showed an error, “Cannot declare variable inside @interface or @protocol”. Going to that article I linked to, this has to do with the difference between declaration and definition. In a header file, you can only declare a variable. You can make a readonly property, but in this case, that seems like a lot of overhead for a simple string constant. The extern here declares that there WILL be an NSString named SettingsModelChangedNotification, and allows anything that includes SettingsModel.h to see it, but it will be defined elsewhere. The logical place is simply in SettingsModel.m, and that’s what we do. You declare its name in the header file, which you are allowed to do, but you don’t give it a value and memory to reside in until you define it, in your implementation file.
Posting an NSNotification
Okay, here comes the hard part. Now that we have given it a name, now we have to actually send out this notification to the ether, and hope some other class hears it. We will do this in your SettingsModel.m implementation file:
[[NSNotificationCenter defaultCenter] postNotificationName:SettingsModelChangedNotification object:self];
Phew, I’m glad that’s over. I know it was arduous, but we got through it.
Kidding aside, that’s pretty much all there is to it. There seems to be more fuss about actually subscribing to it than actually making it. All we are doing it first getting the NotificationCenter singleton provided by Apple, and then sending it a message to post the notification. The “object” parameter is referring to the object that posted the notification.
Now, I don’t know about you, but I kind of find that statement kind of messy. I mean, isn’t the object posting the notification ALWAYS going to be self? Maybe not, but in my app, it is, so I made a little helper method to make this a bit prettier.
- (void)postChangedNotification:(NSString *)notificationName
{
[[NSNotificationCenter defaultCenter] postNotificationName:notificationName object:self];
}
So now, wherever I want to post a notification I can just use this one small line:
[self postChangedNotification:SettingsModelChangedNotification];
Still a little long, but now I don’t have to keep asking explicitly for the NSNotificationCenter singleton, nor repeatedly say that it is self that posted it.
Why the extra Notification Parameter?
In the first version of my app, i actually had it even shorter. It didn’t take ANY parameters, and just sent out the SettingsModelChangedNotification. Now if you want all classes to be aware that something changed, that’s fine. However, what if you had say, 3 properties (myInt1, myString2, and myObject3) and 2 classes (myClassA and myClassB). Lets say myClassA wants to know if myInt1 or myString2 has changed, but doesn’t care about myObject3. Additionally, you myClassB only cares about myObject3.
You could post a general SettingsModelChangedNotification, but if myObject3 changed, and myClassA hears about it, what should it do? It will just waste time doing what it needs to do for myInt1 and myString2, even though they may not have changed. You could make multiple notifications, and we just announce SettingsModelFirstGroupChangedNotification for myInt1 and myString2 changes, and then have SettingsModelOtherGroupChangedNotification for myObject3. Then myClassA only subscribes to SettingsModelFirstGroupChangedNotification, and myClassB only subscribes to SettingsModelOtherGroupChangedNotification.
Conclusion
Since I have repeated it twice, I probably shouldn’t a third time, so if you want to see how to subscribe and use NSNotificationCenter properly, see my previous posts: Supporting Dynamic Type for iOS7 Apps, and Updating an app when returning from background in iOS. This a very nice way to tell other classes when some event has occurred in your SettingsModel, or whatever class you want. If you are just watching for variable changes, there is something else called Key-Value-Observing, but I still have not learned this, but will probably do so in the future, especially
Thank you very much to Matthijs Hollemans and his post Making Your Classes Talk to Each Other, Part 3. I learned a lot about how to do this from his posts.
I hope this was helpful for using NSNotificationCenter in your apps. If you did, don’t hesitate to share this post on twitter or your social media of choice. The blog is still pretty new, and every share helps. Of course, if you have any questions, don’t hesitate to contact me on twitter @CodingExplorer, and I’ll see what I can do.
Thanks!