How to send a string along with NSNotification? - objective-c

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
}
}

Related

libPusher not receiving pushes (obj c)

I am trying to implement a OS X (10.10) client for the real time bitcoin exchange rate from bitstamp.com. They offer a great api, unfortunately I can't get the websocket api (https://www.bitstamp.net/websocket/) to work in an OS X Objective C app.
Everyone is pointing me to the libPusher Github project (https://github.com/lukeredpath/libPusher) but I tried literally everything I can think of, I can't get it to receive any pushes on 10.10.
Following the wiki page I downloaded the pre-compiled static library and dragged all files into my projects (I also made sure the "copy" checkbox was checked) and my sample applications compiles. Unfortunately, I never see either the "event through block" or "event through delegate" on the console. However I know that trades happened in the mean time, so that can't be the issue. Any ideas?
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification {
// Insert code here to initialize your application
PTPusher* client = [PTPusher pusherWithKey:#"de504dc5763aeef9ff52" delegate:self encrypted:YES];
[client connect];
PTPusherChannel *channel = [client subscribeToChannelNamed:#"live_trades"];
[channel bindToEventNamed:#"trade" handleWithBlock:^(PTPusherEvent *channelEvent) {
// channelEvent.data is a NSDictionary of the JSON object received
NSLog(#"event through block"); // <-- never called
}];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(didReceiveEventNotification:)
name:PTPusherEventReceivedNotification
object:client];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(didReceiveChannelEventNotification:)
name:PTPusherEventReceivedNotification
object:channel];
}
- (void)didReceiveEventNotification:(NSNotification *)notification // <-- never called
{
NSLog(#"event through delegate");
PTPusherEvent *event = [notification.userInfo objectForKey:PTPusherEventUserInfoKey];
}
After talking to the developer I found the problem:
In my code, the PTPusher instance is a local variable defined in the .m file.
However, it needs to be a strong variable defined in the .h file:
#property (strong) PTPusher* client;
Then in the .m file, you can easily use it (don't forget to synthesize it!):
self.client = [PTPusher pusherWithKey:#"de504dc5763aeef9ff52" delegate:self encrypted:YES];
[self.client connect];
[[NSNotificationCenter defaultCenter]
addObserver:self
selector:#selector(didReceiveEventNotification:)
name:PTPusherEventReceivedNotification
object:self.client];
PTPusherChannel *channel = [client subscribeToChannelNamed:#"live_trades"];

Why doesn't the block gets called with NSNotificationCenter?

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.

Class method and instance method with the same name in Objective-C

I have a solution for a notification problem which works well, but I'm afraid might be a bad idea.
I have a notification that needs to be handled by each instance of a class and by the class itself. To handle this, I'm registering for a notification by both the class and instances of the class. Because it's the exact same notification, I've named the class and instance method the same. This follows the standard we've set for how notification handlers are named.
Is this a bad idea? Is there some hidden got'ca that I'm missing. Will I be confusing the heck out of future developers?
+ (void)initialize
{
if (self == [SICOHTTPClient class]) {
[[self notificationCenter] addObserver:self
selector:#selector(authorizationDidChangeNotification:)
name:SICOJSONRequestOperationAuthorizationDidChangeNotification
object:nil];
}
}
- (id)initWithBaseURL:(NSURL *)url
{
self = [super initWithBaseURL:url];
if (self) {
self.parameterEncoding = AFJSONParameterEncoding;
[self registerHTTPOperationClass:[SICOJSONRequestOperation class]];
[self setDefaultHeader:#"Accept" value:#"application/json"];
if ([[self class] defaultAuthorization])
[self setDefaultHeader:#"Authorization" value:[[self class] defaultAuthorization]];
[[[self class] notificationCenter] addObserver:self
selector:#selector(authorizationDidChangeNotification:)
name:SICOJSONRequestOperationAuthorizationDidChangeNotification
object:nil];
}
return self;
}
- (void)dealloc
{
[[[self class] notificationCenter] removeObserver:self
name:SICOJSONRequestOperationAuthorizationDidChangeNotification
object:nil];
}
#pragma mark Notifications
- (void)authorizationDidChangeNotification:(NSNotification *)notification
{
NSString *authorization = notification.userInfo[SICOJSONRequestOperationAuthorizationKey];
if ([authorization isKindOfClass:[NSString class]]) {
[self setDefaultHeader:#"Authorization" value:authorization];
} else {
[self clearAuthorizationHeader];
}
}
+ (void)authorizationDidChangeNotification:(NSNotification *)notification
{
NSString *authorization = notification.userInfo[SICOJSONRequestOperationAuthorizationKey];
if ([authorization isKindOfClass:[NSString class]]) {
[self setDefaultAuthorization:authorization];
} else {
[self setDefaultAuthorization:nil];
}
}
This is what code comments are for :)
There's no problem in Objective C with a class method and instance method having the same name.
I would suggest either:
amend your notification method name spec to handle this (and then handle the class notification with a different appropriately named method), or
add appropriate comment to explain what's happening for benefit of future potentially confused developers
The language itself and the runtime will see no ambiguity in what you're doing. So you're safe on that front.
In terms of confusing future maintainers I guess you needn't be too concerned with silly autocomplete mistakes because it's not a method you intend to make manual calls to.
That said, I'd be tempted to move the class stuff into an artificial category. That'll not only give separation on the page but make it explicit that the class intends to respond as a separate tranche of functionality from the instance responses.

Notifications and sending object values - beginners help please

Ive set up a notification centre with the following code in a class called ConnectionController
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(aChangeCalendarMethod:) name:#"updateCalendar" object:nil];
And have a sender in another class (MonthFieldValidator)
[[NSNotificationCenter defaultCenter] postNotificationName:#"updateCalendar" object:self];
MonthFieldValidator has a property of NSString type called month which I want to pass the value of back to connection controller.
All I seem to be able to do is get this
Printing description of testObject:
(NSObject *) testObject = 0x4055c00000000000
And the object doesn't have the month property (or at least that I can access).
Presumably I will have to useruser info?
Do this:
[[NSNotificationCenter defaultCenter] postNotificationName:#"updateCalendar" object:yourMonthStringHere];
Method that will be called is:
-(void)aChangeCalendarMethod:(NSNotification*)notification
{
NSString *strMonth = (NSString *)[notification object]; //retrieve passed string like this
//continue other code
}

Objective-C Mac OS X Distributed notifications iTunes

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.