libPusher not receiving pushes (obj c) - objective-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"];

Related

How to send a string along with NSNotification?

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

Automatic chatroomDidLeave after chatDidFail. Why? Oh why? [Quickblox]

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.

how to resume downloads using MKNetworkKit in the event of network failure

MKNetworkKit allegedly supports resuming of interrupted downloads, but it's not clear how one would go about this. In another thread, it's developer says it works if the server sends Range headers.
How to cancel or pause download operation of MKNetworkKit iOS?
However, it's my understanding that it is the client that sends Range headers. I would expect the library to see how much has been downloaded, and then request the appropriate range. I don't see any place in the code that does this.
The method
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
does indeed check if a Range has been specified, but there seems to be no code actually doing the specifying.
Has anyone gotten MKNetworkKit to resume a download after network failure?
Outside of the freezable option, which as I understand doesn't work for GET, there isn't a way in the standard MKNetworkKit to resume a download. However, a slight modification does allow you to resume a download that has either been stopped by
beginBackgroundTaskWithExpirationHandler
or by
operationFailedWithError
it requires saving downloadedDataSize to another property say resumeDownloadedDataSize and making this available to the calling object via a notification such as
NSDictionary *resumeDownloadedDataSizeDetails = [[NSDictionary alloc] initWithObjectsAndKeys:[NSNumber numberWithLong:self.resumeDownloadedDataSize], #"resumeDownloadedDataSize", nil];
[[NSNotificationCenter defaultCenter] postNotificationName:kMKNetworkOperationEndBackgroundTaskWithExpirationHandler object:self userInfo:resumeDownloadedDataSizeDetails];
(define kMKNetworkOperationEndBackgroundTaskWithExpirationHandler in MkNetworkKit.h)
Set resumeDownloadedDataSize to 0 where the operation completes without error or interruption and make it equal to downloadDataSize when you want to resume.
Then add
if (self.resumeDownloadedDataSize !=0) {
NSString* range = #"bytes=";
range = [range stringByAppendingString:[[NSNumber numberWithLong:self.resumeDownloadedDataSize] stringValue]];
range = [range stringByAppendingString:#"-"];
[[self request] setValue:range forHTTPHeaderField:#"Range"];
}
to
-(void) start
This code at the start of
- (void)connection:(NSURLConnection *)connection didReceiveData:(NSData *)data {
if (self.downloadedDataSize == 0) {
// This is the first batch of data
// Check for a range header and make changes as neccesary
NSString *rangeString = [[self request] valueForHTTPHeaderField:#"Range"];
if ([rangeString hasPrefix:#"bytes="] && [rangeString hasSuffix:#"-"]) {
NSString *bytesText = [rangeString substringWithRange:NSMakeRange(6, [rangeString length] - 7)];
self.startPosition = [bytesText integerValue];
self.downloadedDataSize = self.startPosition;
}
}
Now works for my added offset as the client has requested a range.
I also added
#property (assign, nonatomic) NSUInteger resumeDownloadedDataSize;
to the header file so I could set it from other views.
Now in your calling object you can listen for the notification
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(didMKNetworkOperationEndBackgroundTaskWithExpirationHandler:) name:kMKNetworkOperationEndBackgroundTaskWithExpirationHandler object:nil];
check it's for you and restart the operation setting a range.
- (void) didMKNetworkOperationEndBackgroundTaskWithExpirationHandler: (NSNotification*) notification
{
if ([[notification object] isEqual:downloadOperation]) {
resumeDownloadedDataSize = [notification.userInfo objectForKey:#"resumeDownloadedDataSize"];
bDownloadOperationCancelled=YES;
}
}
As I am using this for a large file download, the expiration handler expires and I restart the operation when I come back into foreground.
if (bDownloadOperationCancelled) {
NSLogDebug(#"DownloadOperationCancelled restarted");
[self doFileDownload];
bDownloadOperationCancelled=NO;
}
where
- (void) doFileDownload
{
downloadOperation = [ApplicationDelegate.downloadEngine downloadVideoFile:authDownloadURLString toFile:downloadPath withOffset:resumeDownloadedDataSize];
(Your code for handling completion blocks etc)
…
}
The final piece is to make sure in your engine you set append to YES
[op addDownloadStream:[NSOutputStream outputStreamToFileAtPath:filePath append:YES]];
So a resume adds to the file.
Just to also be clear I am using AWS CloudFront and this supports the use of range headers when data is stored in S3.
This appears to work for me. I am sure there are more elegant ways of doing it.

How to verify network connectivity in objective-c

I was looking over the Reachability sample project on developer.apple.com and found that it was a large project just to verify you had network connectivity.
First part of the question is "What is the minimum code required to find out if the device can reach a 3G or wifi network?"
And next should this be done inside the appDelegate (on start) or inside the first View Controller that is launched?
Thank you in advance
It's not large, it really does what you want. If it is too big for you, you can extract what you need only like reachabilityForLocalWiFi. But I'm afraid that it will not be much smaller.
Yes, you can use reachability in your application delegate or inside the first view controller.
Reachability notification registration ...
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(networkReachabilityDidChange:)
name:kReachabilityChangedNotification
object:nil];
__reachability = [[Reachability reachabilityWithHostName:#"www.google.com"] retain];
[__reachability startNotifier];
... callback method example ...
- (void)networkReachabilityDidChange:(NSNotification *)notification {
Reachability *reachability = ( Reachability * )[notification object];
if ( reachability.currentReachabilityStatus != NotReachable ) {
// Network is available, ie. www.google.com
} else {
// Network is not available, ie. www.google.com
}
}
... do not forget to stop notifications, remove observer and release rechability object.

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.