I've recently started working with OCMock framework and trying to figure out how to use the notification observer. So in my source code I'm observing some notification:
typedef enum {
OperationStatusCompleted,
// Some other statuses
} SomeOperationStatus;
- (void)addObserverForSomeNotification {
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(someOperationStatusDidChange:)
name:#"someOperationStatusDidChange"
object:nil];
- (void)backgroundOperationStatusDidChange:(NSNotification *)notification {
id<SomeOperationProtocol> operation = [notification object];
if (operation.status == OperationStatusCompleted) {
// Do something.
}
}
Now I want to add the expectation for that notification in my test:
- (void)testNotification {
id observerMock = OCMObserverMock();
[[NSNotificationCenter defaultCenter] addMockObserver:observerMock name:#"someOperationStatusChanged" object:nil];
[[observerMock expect] notificationWithName:#"someOperationStatusChanged" object:[OCMArg any]];
// run the test ...
}
That by itself works fine and I'm receiving the notification in the test but what I actually want to check is the status of the object of the notification (meaning that I've received a notification with specific object with specific status). Is it possible?
I finally figured out how to do it:
[[observerMock expect] notificationWithName:#"someOperationStatusChanged" object:[OCMArg checkWithBlock:^BOOL(id<IMBBackgroundOperation> param) {
return param.status == OperationStatusCompleted;
}]];
Related
I'm trying to write a plugin for phonegap/cordova that allows audio to resume when interrupted by a phone call. I'm using AVAudioSesionInterruptionNotification and it is working well. However, I need to be able to send a string to my event handler, but I can't figure out how.
I'm setting up an event listener here and calling it from the javascript layer:
- (void) startListeningForAudioSessionEvent:(CDVInvokedUrlCommand*)command{
NSString* myImportantString = [command.arguments objectAtIndex:0];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(onAudioSessionEvent:) name:AVAudioSessionInterruptionNotification object:nil];
}
and I'm handling the event here:
- (void) onAudioSessionEvent:(NSNotification *)notification
{
if([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeEnded]]){
//do stuff using myImportantString
}
}
can't figure out how to pass myImportantString over to onAudioSessionEvent. I know very little Objective-C (hence my use of cordova), so please respond as if you're talking to a child. Thanks!
By the way, I'm simply trying to add a couple of methods on top of cordova's media plugin found here: https://github.com/apache/cordova-plugin-media/tree/master/src/ios
so the code above is the entirety of my .m file minus this part
#implementation CDVSound (extendedCDVSound)
It's probably easier to use the block-based API for adding an observer:
- (void) startListeningForAudioSessionEvent:(CDVInvokedUrlCommand*)command{
NSString* myImportantString = [command.arguments objectAtIndex:0];
id observer = [[NSNotificationCenter defaultCenter] addObserverForName:AVAudioSessionInterruptionNotification
object:nil
queue:nil
usingBlock:^(NSNotification *notification){
if([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeEnded]]){
//do stuff using myImportantString; you could even skip the use of that
//temporary variable and directly use [command.arguments objectAtIndex:0],
//assuming that command and command.arguments are immutable so that you
//can rely on them still being the same
}
}];
}
By using the block-based API, you can directly reference any variables that are in scope at the time the observer is added in the code which is invoked when the notification is posted.
When you're done observing, you need to remove observer as an observer of the notification.
Like this:
// near the top of MyController.m (above #implementation)
#interface MyController ()
#property NSString *myImportantString;
#end
// inside the implementation
- (void) startListeningForAudioSessionEvent:(CDVInvokedUrlCommand*)command{
self.myImportantString = [command.arguments objectAtIndex:0];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(onAudioSessionEvent:) name:AVAudioSessionInterruptionNotification object:nil];
}
- (void) onAudioSessionEvent:(NSNotification *)notification
{
if([[notification.userInfo valueForKey:AVAudioSessionInterruptionTypeKey] isEqualToNumber:[NSNumber numberWithInt:AVAudioSessionInterruptionTypeEnded]]){
//do stuff using self.myImportantString
}
}
Every time we try to reconnect after a chatDidFail event, we:
login in chat
enter room
automatically get kicked out of the the room (chatRoomDidLeave event)
when we try to re-join the room, we get kicked out again (chatRoomDidLeave)
and it loops
why, oh why?
Why does Quickblox kicks us out of any room after we try to reconnect following a chatDidFail event?
All we do is:
-(void)chatDidFailWithError:(int)code
{
[[QBChat instance] loginWithUser:currentUser];
}
- (void)chatRoomDidLeave:(NSString *)roomName
{
[[QBChat instance] createOrJoinRoomWithName:#"roomName" membersOnly:NO persistent:YES];
}
We're running out of ideas on this one...
I managed to resolve this. It seems that Quickblox chat implementation isn't able to recover on it's own, when the application goes background and you don't leave the chat room.
This can be solved by having the ChatManager (your own singleton class used to manage chat connections) to store the currently shown room (self.currentRoom) and automatically leave the room, and log out when application resigns active. And when the application will enter foreground, you log in and rejoin the room.
Add these to the singleton class (id)init:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleApplicationWillResignActiveNotification:) name:UIApplicationWillResignActiveNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleApplicationEnterForegroundNotification:) name:UIApplicationWillEnterForegroundNotification object:nil];
And the property implementation for the current room:
#property (nonatomic, strong) QBChatRoom *currentRoom;
And then implement the corresponding handlers (you can also invalidate the presence timer here):
- (void)handleApplicationWillResignActiveNotification:(NSNotification *)notification
{
lg(#"Application resigning active");
if (self.currentRoom) {
if (self.presenceTimer) {
[self.presenceTimer invalidate];
self.presenceTimer = nil;
}
[self leaveRoom:self.currentRoom clearRoom:NO];
[[QBChat instance] logout];
}
}
- (void)handleApplicationEnterForegroundNotification:(NSNotification *)notification
{
lg(#"Application entering foreground.");
if (![[QBChat instance] loginWithUser:self.currentUser]) {
lg(#"Logging in failed for some reason.");
}
}
Then in chatDidLogin you do the following:
- (void)chatDidLogin
{
...
if (self.currentRoom) {
lg(#"Auto-joining current room.");
[self joinRoom:self.currentRoom completionBlock:nil];
}
...
}
Store the room reference when you enter a room:
- (void)chatRoomDidEnter:(QBChatRoom *)room
{
...
self.currentRoom = room;
...
}
Make sure that you don't clear self.currentRoom when you leave the room on UIApplicationWillResignActiveNotification, so that there's a reference for the current room when the application comes foreground.
I just came across the NSNotificationCenter method [NSNotificationCenter defaultCenter] addObserverForName: object: queue: usingBlock: method and since I'm beginning to be comfortable using blocks I decided to try it out since I results in better code readability.
But for some reason I can't get it to work. For what reason doesn't this work
[[NSNotificationCenter defaultCenter] addObserverForName:#"SomeNotificationName"
object:self
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note) {
NSLog(#"This doesn't work");
// ... want to do awesome stuff here...
}];
where as this works just fine
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(aMethod)
name:#"SomeNotificationName"
object:nil];
//...
//....
- (void)aMethod {
NSLog(#"This works");
// ... doing awesome stuff here...
}
END NOTE
Thanks, for future reference this is my final solution
// declared instance variable id _myObserver;
//...
_myObserver = [[NSNotificationCenter defaultCenter] addObserverForName:#"SomeNotificationName"
object:nil
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note) {
NSLog(#"It's working! It's working!!");
// ... again doing awesome stuff here...
}];
And then finally, (when I'm done with the object)
- (void)dealloc {
[[NSNotificationCenter defaultCenter] removeObserver:_myObserver];
}
Read the doc more carefully :) In the block version, you have to store the return "token" object, for instance in an ivar.
id obj = [[NSNotificationCenter defaultCenter]
addObserverForName:#"SomeNotificationName"
object:self
queue:[NSOperationQueue mainQueue]
usingBlock:^(NSNotification *note) {
NSLog(#"This doesn't work");
// ... want to do awesome stuff here...
}];
//you need to retain obj somehow (e.g. using ivar)
//otherwise it will get released on its own under ARC
Also (as mentioned in the docs too), don't forget to remove the observer when you're done with it. In my case, this usually happens in the dealloc method of my window/view controller. In your case it may be at a different place. (Of course, if you want the observer until your app quits, you don't need to do this.)
- (void)dealloc {
//assuming you stored it in _obj ivar
[[NSNotificationCenter defaultCenter] removeObserver:_obj];
//no super dealloc under ARC
}
The object parameter in both methods is the sender of the notification.
In the block based one you are passing self, in the selector based one you are passing nil. That means the block based observer will listen only to notifications generated by self.
So my goal is to deliver a notification to another class with using NSNotificationCenter, I also want to pass object with the notification to the other class, how should I do this?
You must first register a notification name
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(startLocating:) name:#"ForceUpdateLocation" object:nil]; // don't forget the ":"
And then post a notification with a dictionary of parameters
[[NSNotificationCenter defaultCenter] postNotificationName:#"ForceUpdateLocation" object:self userInfo:[NSDictionary dictionaryWithObject:#"1,2,3,4,5" forKey:#"categories_ids"]];
and the method will be
- (void)startLocating:(NSNotification *)notification {
NSDictionary *dict = [notification userInfo];
}
Just call any method for posting notifications as described here, for instance :
to post a notification :
-(void)postNotificationName:(NSString *)notificationName
object:(id)notificationSender
userInfo:(NSDictionary *)userInfo;
where userInfo is a dictionary containing useful objects.
On the other side to register for notifications :
-(void)addObserver:(id)notificationObserver
selector:(SEL)notificationSelector
name:(NSString *)notificationName
object:(id)notificationSender;
You could also check Apple's Notification Programming Topics.
i need a little help, i currently have a method; updateTrackInfo in my Mac OS X application which gets the artist name, the track name and duration of the track currently being played in iTunes
However i want the app to listen for the distributed iTunes notification; com.apple.iTunes.playerInfo then call the method updateTrackInfo when ever the notification is distributed by iTunes. Please could someone help me, on what i would need to write in both the header and implementation file.
Thanks, Sami.
You're looking for -[NSDistributedNotificationCenter addObserver:selector:name:object:]:
NSDistributedNotificationCenter *dnc = [NSDistributedNotificationCenter defaultCenter];
[dnc addObserver:self selector:#selector(updateTrackInfo:) name:#"com.apple.iTunes.playerInfo" object:nil];
Elsewhere in the same class...
- (void) updateTrackInfo:(NSNotification *)notification {
NSDictionary *information = [notification userInfo];
NSLog(#"track information: %#", information);
}
It even gives you a whole bunch of track information in the notification. Isn't that nice?
Thanks for your help, you helped me correct my code, I had this written up:
- (id) init {
self = [super init];
if (!self) return nil;
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(receiveNotification:)
name:#"com.apple.iTunes.playerInfo"
object:nil];
return self;}
- (void) receiveNotification:(NSNotification *) notification {
if ([#"com.apple.iTunes.playerInfo" isEqualToString:#"com.apple.iTunes.playerInfo"]) {
NSLog (#"Successfully received the test notification!");
}}
But it used NSNotificationCenter instead of NSDistributedNotificationCenter. Which is where I was going wrong.
Thanks, Sami.