Can´t receive AppMessage from Pebble on iOS - ios

On the Watch i send an AppMessage like this
DictionaryIterator *iter;
app_message_outbox_begin(&iter);
Tuplet value = TupletInteger(MESSAGE_TYPE, MESSAGETYPE_REFRESH);
dict_write_tuplet(iter, &value);
app_message_outbox_send();
I set the background modes and protocols for my app as described in the tutorial.
In iOS i set the listeners like this:
[PBPebbleCentral defaultCentral].delegate = self;
self.watch = [PBPebbleCentral defaultCentral].lastConnectedWatch;
NSLog(#"Pebble name: %#", _watch.name);
NSLog(#"Pebble serial number: %#", _watch.serialNumber);
[_watch appMessagesAddReceiveUpdateHandler:^BOOL(PBWatch *watch, NSDictionary *update) {
NSLog(#"Update received!");
return YES;
}];
[_watch appMessagesAddReceiveAllUpdatesHandler:^BOOL(PBWatch *watch, NSUUID *uuid, NSDictionary *update) {
NSLog(#"AllUpdate received!");
return YES;
}];
[_watch appMessagesAddAppLifecycleUpdateHandler:^(PBWatch *watch, NSUUID *uuid, PBAppState newAppState) {
NSLog(#"AppLifecycleUpdate received!");
}];
I already did send messages from the phone to the watch. So that way it works. But the listeners for incoming messages on the phone wont get called.
On the clock i get APP_MSG_SEND_TIMEOUT as error code. What did i wrong?

Do you have src/js/pebble-js-app.js in your watch app? I had the same problem and when i removed this generated file it started working.

Check to make sure you are using
appMessagesAddReceiveUpdateHandler:withUUID:
instead of
appMessagesAddReceiveUpdateHandler:

Pay attention to where you put the listener. For example if you are using the WeatherDemo app (provided by Pebble) you should do that after setting the the app UUID.
// Test if the Pebble's firmware supports AppMessages / Weather:
[watch appMessagesGetIsSupported:^(PBWatch *watch, BOOL isAppMessagesSupported) {
if (isAppMessagesSupported) {
...
[_targetWatch appMessagesAddReceiveUpdateHandler:^BOOL(PBWatch *watch, NSDictionary *update) {
NSLog(#"Received message: %#", update);
return YES;
}];
} else {
....
Another thing to pay attention is not to put it under
- (void)pebbleCentral:(PBPebbleCentral*)central watchDidConnect:(PBWatch*)watch isNew:(BOOL)isNew
because this function isn't called if the device is already connected.

Related

How can I disable HealthKit integration in your app?

I have an application to requestAuthorization to use HealthKit data into a tableView.
I use this code to ask permission to get HealthKit data:
- (IBAction)healthIntegrationButtonSwitched:(UISwitch *)sender {
if (sender.isOn) {
[[GSHealthKitManager sharedManager] requestAuthorization];
}
}
- (void)requestAuthorization {
if ([HKHealthStore isHealthDataAvailable] == NO) {
// If our device doesn't support HealthKit -> return.
return;
}
NSArray *readTypes = #[[HKObjectType characteristicTypeForIdentifier:HKCharacteristicTypeIdentifierDateOfBirth]];
NSArray *writeTypes = #[[HKObjectType quantityTypeForIdentifier:HKQuantityTypeIdentifierBodyMass],
[HKObjectType workoutType]];
[self.healthStore requestAuthorizationToShareTypes:[NSSet setWithArray:writeTypes]
readTypes:[NSSet setWithArray:readTypes] completion:nil];
}
Could I add to my healthIntegrationButtonSwitched function the other case when "if (sender.isOff) {" to cancel the permission accepted of HealthKit?
thanks!
There's no way to de-authorize HealthKit access programmatically, but there's also no need to. When the switch is disabled you should just make sure to not query HealthKit anymore. Also the user can de-authorize your app in the Sources panel of the Health app if they have a privacy concern.

iOS and Google Nearby API: how to publish and subscribe in the right way?

I've done as the guide says
This is the message manager
[GNSMessageManager setDebugLoggingEnabled:YES];
messageManager = [[GNSMessageManager alloc] initWithAPIKey:API_KEY paramsBlock:^(GNSMessageManagerParams *params) {
params.bluetoothPowerErrorHandler = ^(BOOL hasError) {
// Update the UI for Bluetooth power
};
params.bluetoothPermissionErrorHandler = ^(BOOL hasError) {
// Update the UI for Bluetooth permission
};
}];
These are my methods to publish and subscribe with the Nearby API.
- (IBAction)onPublish:(id)sender {
NSLog(#"publish");
NSString* str = #"hello world";
NSData* data = [str dataUsingEncoding:NSUTF8StringEncoding];
GNSMessage* message = [GNSMessage messageWithContent:data];
id<GNSPublication> publication = [messageManager publicationWithMessage:message paramsBlock:^(GNSPublicationParams *publicationParams) {
publicationParams.strategy = [GNSStrategy strategyWithParamsBlock:^(GNSStrategyParams * strategyParams) {
strategyParams.allowInBackground = YES;
strategyParams.discoveryMediums = kGNSDiscoveryMediumsBLE;
strategyParams.discoveryMode = kGNSDiscoveryModeDefault;
}];;
}];
}
- (IBAction)onSubscribe:(id)sender {
NSLog(#"subscribe");
id<GNSSubscription> subscription = [messageManager subscriptionWithMessageFoundHandler:^(GNSMessage *msg) {
// Add the name to a list for display
NSLog(#"message found %#", [msg description]);
} messageLostHandler:^(GNSMessage *msg) {
// Add the name to a list for display
NSLog(#"message lost %#", [msg description]);
} paramsBlock:^(GNSSubscriptionParams *subscriptionParams) {
subscriptionParams.strategy = [GNSStrategy strategyWithParamsBlock:^(GNSStrategyParams * strategyParams) {
strategyParams.allowInBackground = YES;
strategyParams.discoveryMediums = kGNSDiscoveryMediumsBLE;
strategyParams.discoveryMode = kGNSDiscoveryModeDefault;
}];;
}];
}
Both Bletooth central and peripheral background capabilities are enabled, and the permission string for the peripheral is set.
Finally I subscribe on an iOS device and publish from another one but nothing happens.
Be sure to retain the publication and subscription objects. They stop publishing/subscribing when they're deallocated. The usual way is to store them as properties/ivars in one of your classes.
The developer docs are misleading on this point, and I apologize. We'll improve the docs in the next release.

GKMatchmaker findMatchForRequest invite never received

I'm trying to invite nearby players to a match, but the invite is either never sent or never received.
GKMatchMaker startBrowsingForNearbyPlayersWithHandler works and returns nearby players that are on same wifi, but then I use findMatchForRequest and it returns a match without any players, and the players I try to invite never receive an invite notification. Here is my code.
I start by authenticating the local player:
GKLocalPlayer.localPlayer.authenticateHandler= ^(UIViewController *controller, NSError *error)
{
if (error)
{
NSLog(#"%s:: Error authenticating: %#", __PRETTY_FUNCTION__, error.localizedDescription);
return;
}
if(controller)
{
// User has not yet authenticated
[pViewController presentViewController:controller animated:YES completion:^(void)
{
[self lookForNearbyPlayers];
}];
return;
}
[self lookForNearbyPlayers];
};
-(void)lookForNearbyPlayers
{
if(!GKLocalPlayer.localPlayer.authenticated)
{
NSLog(#"%s:: User not authenticated", __PRETTY_FUNCTION__);
return;
}
I register my view controller as a delegate of GKLocalPlayerListener:
[GKLocalPlayer.localPlayer registerListener:self]; // self is a view controller.
// This works. My test local player which is a second device and appleID I setup shows up when this handler is called.
[GKMatchmaker.sharedMatchmaker startBrowsingForNearbyPlayersWithHandler:^(GKPlayer *player, BOOL reachable)
{
NSArray * paPlayers= [NSArray arrayWithObject:player];
_pMatchRequest= [[GKMatchRequest alloc] init];
_pMatchRequest.minPlayers= 2;
_pMatchRequest.maxPlayers= 4;
_pMatchRequest.recipients = paPlayers;
_pMatchRequest.inviteMessage = #"Join our match!";
_pMatchRequest.recipientResponseHandler = ^(GKPlayer *player, GKInviteeResponse response)
{
// This is never called.
NSLog((response == GKInviteeResponseAccepted) ? #"Player %# Accepted" : #"Player %# Declined", player.alias);
};
// This returns with a match without any players.
[GKMatchmaker.sharedMatchmaker findMatchForRequest:_pMatchRequest withCompletionHandler:^(GKMatch *match, NSError *error)
{
if(error)
{
NSLog(#"%s:: %#", __PRETTY_FUNCTION__, error.localizedDescription);
return;
}
else if(match != nil)
{
_pMatch= match;
match.delegate = self;
NSLog(#"players count= %lu", (unsigned long)_pMatch.players.count); // Always returns 0
}
}];
}
}
I have delegate methods for GKLocalPlayerListener setup, but they are never called:
- (void)player:(GKPlayer *)player didRequestMatchWithRecipients:(NSArray<GKPlayer *> *)recipientPlayers
{
NSLog(#"%s", __PRETTY_FUNCTION__);
}
- (void)player:(GKPlayer *)player didAcceptInvite:(GKInvite *)invite
{
NSLog(#"%s", __PRETTY_FUNCTION__);
}
Does anyone know how to get this to work without GKMatchmakerViewController and for iOS9? The only examples I can find have the deprecated -inviteHandler method.
This code is working in Swift if you know how you can convert it to Objective-C and to try it.
GKMatchmaker.sharedMatchmaker().findMatchForRequest(
request,
withCompletionHandler: {(match : GKMatch!, error: NSError!) -> Void in
NSLog("This works")
})
Based on multiple questions here on SO, Game Center seems to be getting stuck from time to time. In the best case, it returns "Game not recognized" errors. In the worst case, it just cheerfully returns nil to GC calls. Sometimes it resumes working on it's own, sometimes it doesn't. But it seems you can kickstart it again by logging into iTunesConnect and do any of the following:
Add a leaderboard
Change the default leaderboard
Add an achievement
I've added this to my debugging routine. If some aspect of GC stops working, or returns nil, I try making one of the above changes in iTunesConnect before proceeding. In my case, I get the "game not recognized" several times per week, but several others have noted the "nil return values."
I know this an older post, but I ran across it when trying to establish a connection between several app instances over the internet. I believe the part you're missing is that after registering for the listener, you need to receive the connected status with
- (void)match:(GKMatch *)match
player:(GKPlayer *)player
didChangeConnectionState:(GKPlayerConnectionState)state
{
NSLog(#">>> did change state");
if (state == GKPlayerStateConnected)
{
NSLog(#">>>> match:%# did change to Connected for player %# ",match, player.displayName);
}
else if (state == GKPlayerStateDisconnected)
{
NSLog(#">>>> match:%# disconnected for player %# ",match, player.displayName);
}
I find the match has 0 players when the completionHandler is called from findMatchForRequest:, but that I can successfully use the GKMatch and GKPlayer as returned in didChangeConnectionState:
Hope that helps someone who reads this long after the OP.

CKFetchNotificationChangesOperation returning old notifications

I'm working on a CloudKit-based app that uses CKSubscription notifications to keep track of changes to a public database. Whenever the app receives a push notification I check the notification queue with CKFetchNotificationChangesOperation and mark each notification read after processing it:
__block NSMutableArray *notificationIds = [NSMutableArray new];
CKFetchNotificationChangesOperation *operation = [[CKFetchNotificationChangesOperation alloc] initWithPreviousServerChangeToken:self.serverChangeToken];
operation.notificationChangedBlock = ^(CKNotification *notification) {
[notificationIds addObject:notification.notificationID];
[self processRemoteNotification:notification withCompletionHandler:completionHandler];
};
__weak CKFetchNotificationChangesOperation *operationLocal = operation;
operation.fetchNotificationChangesCompletionBlock = ^(CKServerChangeToken *serverChangeToken, NSError *operationError) {
if (operationError) {
NSLog(#"Unable to fetch queued notifications: %#", operationError);
}
else {
self.serverChangeToken = serverChangeToken;
completionHandler(UIBackgroundFetchResultNewData);
// Mark the processed notifications as read so they're not delivered again if the token gets reset.
CKMarkNotificationsReadOperation *markReadOperation = [[CKMarkNotificationsReadOperation alloc] initWithNotificationIDsToMarkRead:[notificationIds copy]];
[notificationIds removeAllObjects];
markReadOperation.markNotificationsReadCompletionBlock = ^(NSArray *notificationIDsMarkedRead, NSError *operationError) {
if (operationError) {
NSLog(#"Unable to mark notifications read: %#", operationError);
}
else {
NSLog(#"%lu notifications marked read.", (unsigned long)[notificationIDsMarkedRead count]);
}
};
[[CKContainer defaultContainer] addOperation:markReadOperation];
if (operationLocal.moreComing) {
NSLog(#"Fetching more");
[self checkNotificationQueueWithCompletionHandler:completionHandler];
}
}
};
[[CKContainer defaultContainer] addOperation:operation];
As I understand it marking a notification read will keep it from showing up in future queue fetches, even if the server change token is reset to nil. Instead I'm getting a lot of old notifications in every fetch with a non-nil change token when there should only be 1 or 2 new ones. I can detect the old ones from the notificationType flag, but I'm concerned that they're showing up at all. Am I missing a step somewhere?
I know this is a bit old, but I was running into the same issue. I think I figured it out (at least for my case).
In my code, I was doing the same as you: that is, adding all the notificationIDs to an array and using that in my CKMarkNotificationsReadOperation, and was also getting all the notifications returned each time (although, as you noted, with a type of "ReadNotification").
I changed my code so that I was only adding "new" notifications to my array, and not the "ReadNotification" items, and sending those. That fixed it.
It seems that sending a notification back to the server to be marked as read, even if it already has been marked as such, will cause it to be returned again as "ReadNotification."
I hope this helps someone.
The documentation isn't very clear it should say: "Marking a notification as read prevents it from being returned by subsequent fetch operations"...as a query notification type. Further clarification it should say the notifications will instead be returned as read type.
If it wasn't returned at all then other devices that missed the push wouldn't know that something has changed!

How to distinguish between PNMessage sent by current user versus those from others?

I am currently using PubNub to build chat-like functionality into an app. Following the example from the demo project, I added the following to my viewDidLoad to listen for messages received my channel:
[[PNObservationCenter defaultCenter] addMessageReceiveObserver:self withBlock:^(PNMessage *message) {
NSLog(#"message %#", message);
[self DisplayInLog: [NSString stringWithFormat:#"[%#]: %#",message.channel.name, message.message]];
[self showReceivedMessage:message];
}];
My problem is, when the current user sends a message using [PubNub sendMessage:text toChannel:self.currentChannel], the listener picks up on the message (as expected), but I am having trouble distinguishing from the PNMessage that which is sent by the current user and which is sent by someone else and picked up by the receiver. How should I go about approaching this without getting too hacky (for example, comparing the contents of the messages, when they were sent, etc).
Thanks!
#daspianist
You have to add field which will store identifier of the user and on client side check who is sender of the message by retrieving value from dictionary which will be send as message.
For example:
[[PNObservationCenter defaultCenter] addMessageReceiveObserver:self withBlock:^(PNMessage *message) {
NSLog(#"message %#", message);
if (![[message.message valueForKey:#"sender"] isEqualToString:#"Bob"]) {
[self DisplayInLog: [NSString stringWithFormat:#"[%#]: %#",message.channel.name, message.message]];
[self showReceivedMessage:message];
}
}];
[PubNub sendMessage:#{#"message":#"This is actual message which we want to send. It can be any Objective-C type.", #"sender":#"Bob"} toChannel:self.currentChannel];
In example above, it shouldn't process message if it has been sent from "Bob"
SEND MESSAGE IN DICTIONARY
NSDictionary *message = [[NSDictionary alloc] initWithObjectsAndKeys:#"HELLLLOO",#"text",#"Kiran",#"sender", nil];
[PubNub sendMessage:message toChannel:[PNDataManager sharedInstance].currentChannel
withCompletionBlock:^(PNMessageState state, id object) {
}

Resources