Messages sent with PebbleKit iOS are not received on the watch - ios

In my iOS App i want to send an message to the watch like this:
NSMutableDictionary *message = #{#(1): #(1),
#(2): #"The String"}
[_watch appMessagesPushUpdate:message onSent:^(PBWatch *watch, NSDictionary *update, NSError *error) {
if (error != nil)
NSLog(#"Error sending message: %#", error);
}];
If i am sending it like this it works. But if my string is longer or if i add more than 3 or 4 keys to the dictionary the message will not be delivered. Error is "the app did not acknowledge the message in time".
In my pebble App i do the following:
static void message_handler(DictionaryIterator *iter, void *context) {
APP_LOG(APP_LOG_LEVEL_DEBUG, "Received message.");
Tuple *msg_type_tuple = dict_find(iter, PebbleMessageKeyType);
Tuple *msg_value_tuple = dict_find(iter, PebbleMessageKeyValue);
write_line_on_screen(msg_value_tuple->value->cstring);
}
...
// Set sniff interval.
app_comm_set_sniff_interval(SNIFF_INTERVAL_NORMAL);
// Register message handlers
app_message_register_inbox_received(message_handler);
app_message_register_inbox_dropped(message_dropped);
// Init buffers
app_message_open(app_message_inbox_size_maximum(), app_message_outbox_size_maximum());
APP_LOG(APP_LOG_LEVEL_DEBUG, "App Message set up.");
My thought is that it has something to do with the message size. But i can´t imagine that messages can only be so small?
In the ToDo list example i saw that they used app_message_open with values 64 for inbox and 16 for outbox parameter. What units are meant with this? 64 characters? How do i know on the iOS side how big my message will be when it arrives on the pebble?

First thing you should do when debugging this type of problem is adding a message_dropped handler and printing the failure reason:
void init() {
// ...
app_message_register_inbox_dropped(appmsg_in_dropped);
app_message_open(...);
// ...
}
static void appmsg_in_dropped(AppMessageResult reason, void *context) {
APP_LOG(APP_LOG_LEVEL_DEBUG, "In dropped: %i", reason);
}
You will find a list of reason code in the documentation.
The two most common problems are:
APP_MSG_BUFFER_OVERFLOW: The message is too big (see below)
APP_MSG_BUSY: You are sending messages too fast. You cannot send a new message until the previous one has been acknowledged.
The size of the message is equal to the size of the dictionary. The documentation of dict_calc_buffer_size() explains how to calculate it:
1 byte + 7 bytes for each key + the sum of the sizes of the values
Finally, the values passed to app_message_open() are the buffer sizes in bytes.

Related

QBChatDelegate -chatDidReceiveMessage: getting called but dialogID is NULL

I'm utilising QB for awhile now and everything was working perfectly.
Something came up today and I'm not being able to receive QBChatMessages with a proper dialogID anymore.
I send the message with:
[self.dialog sendMessage:message completionBlock:^(NSError * _Nullable error) {
if (error != nil) {
NSLog(#"error! %#", error);
} else {
NSLog(#"success!");
/*locally adding message to tableview*/
}
}
And the callback always results a success, the other end receives the message through:
- (void)chatDidReceiveMessage:(QBChatMessage *)message
{
NSLog(#"chatDidReceiveMessage %#", message.dialogID);
}
But the dialogID I'm getting is (null), which doesn't make any sense. The variable self.dialog.ID is not null upon sending the message, the message gets to its destination with all the text I sent, but the dialogID is (null). I verified in the Chat admin panel inside my account and the dialog does not show the new messages. I'm using v2.6.0.1 btw.
Okay, I found my problem.
Seems when I changed some sending messages methods I commented out the important part of
message.customParameters[#"save_to_history"] = #"1";
Never forget to set save_to_history custom parameter or your message will get to its recipient without a dialogID...

Creating a working UIPrinter object from Url for dialogue-free printing

Scenario: Guided-Mode locked app accepts some user input (name, etc) and prints them out a ticket. The user cannot be able choose a printer.
My planned solution was to save the URL of the printer, which I have and is in the form of:
ipp://<hostname>.local.:5001/<printername>
To build the UIPrinter object from this stored string:
var printerURL = NSUrl.FromString(SettingsService.OptPrinterUrl);
var printer = UIPrinter.FromUrl(printerURL);
I then call:
printer.ContactPrinter((available) => {
if (available) {
//
}
});
OR
var printInterface = UIPrintInteractionController.SharedPrintController;
printInterface.PrintToPrinter(printer, (handler, completed, error) =>
{
if (!completed && error != null) {
UIAlertView alert = new UIAlertView("Guest Check-In App", "Print Error", null, "Ok", null);
alert.Show();
}
});
Without positive result.
However, when using a UIPrinter object returned from UIPrinterPickerController (as shown below) it all works.
var printerPicker = UIPrinterPickerController.FromPrinter(null);
printerPicker.PresentFromRect(new CGRect(0,0,100,100),this.View,true, delegate(UIPrinterPickerController handler, bool completed, NSError error) {
printer = printerPicker.SelectedPrinter;
});
I have even tried getting the UIPrinter object from PrinterPicker, and trying to build a new UIPrinter with UIPrinter.FromUrl using the url from the UIPrinter object taken from PrinterPicker.
I was only able to create a working UIPrinter object without directly using the one from UIPrinterPickerController like so:
var printerPicker = UIPrinterPickerController.FromPrinter(null);
printerPicker.PresentFromRect(new CGRect(0,0,100,100),this.View,true, delegate(UIPrinterPickerController handler, bool completed, NSError error) {
printerPickerPrinter = printerPicker.SelectedPrinter;
});
var printer = (UIPrinter)UIPrinter.FromObject(printerPickerPrinter);
Summary
What I need is a way to 'remember' a printer, and use that printer to print automatically in a xamarin-built iOS app.
This is an iOS bug. I reported to Apple Bug Reporter (47180997) and they gave me a lame response:
Shared printers on Linux will not work with this contactPrinter method. It requires that the printer attributes are returned successfully. We often still send a print job even if the other parts fail.
For these methods to work properly, the server has to be a licensed and correctly implemented AirPrint server. CUPS running somewhere isn’t enough.

Message was repeat when read data from socket IOS

I have build app use socket to transfer data between two device. But i have a problem when i try to read data from socket.
This is code used to read message from socket.
while (1) {
UInt8 bufr[802400];
int bytesRead = CFReadStreamRead(_readStream, bufr, sizeof(bufr));
if(bytesRead >0 ){
NSLog(#"Read: %d", bytesRead);
NSString *jsonStr = [NSString stringWithUTF8String:(char*)bufr];
NSLog(#"Message: %#", jsonStr);
}
}
}
But my message was repeat:
Example i have two meesage: message A and message B.
Message A i have received from socket at first time.
At second time i recived a part of message A and message B.
Please tell me how to fix that.
By the way please tell me how to limit byte of message when i read data. Because if my buffer is two small, when i read data jsonStr always null.
Thanks.

Fetching Gmails Via Mailcore 2: Thread ID vs Message ID vs UID

I have an iPad application that allows users to access their Gmail accounts using Mailcore2. I thought I had an understanding of the difference between Gmail's Thread Id, Message ID and UID until I looked closely at what Mailcore2 is returning to me when I perform a message fetch operation. I am hoping someone can clarify my confusion.
Here is what I think I know from the Gmail docs:
1) A thread ID groups together messages (which have their own message IDs and UIDs) that are part of the same conversation
2) A UID is specific to a message and is unique only to the folder which contains it
3) A message ID is specific to a message and is unique across all folders of an account
I am also making the following assumptions:
1) A thread has a thread ID and is a collection of messages. A thread does not have a message ID or a UID.
2) A message has a message ID, UID and thread ID (even if it is the only message in a thread)
3) Fetching messages by UID fetches MESSAGES which have a UID that falls in the requested range of UIDs.
4) Messages that belong to the same thread will have different UIDs and message IDs but the same Thread ID.
Ok, so assuming that the above is correct, I would think that during a typical fetch of messages in Mailcore2 by UID, I would receive an array of emails, and from those emails I could look at their thread ID, for example and reconstruct threads on the client side. However, I seem to get back threads rather than emails. Additionally, each thread I get does not necessarily contain all its 'child' messages.
So for example, if I have two threads in my inbox, each containing five messages, Mailcore returns to me an array of 2 "emails" in the form of MCOIMAPMessages. And each "email" has one thread ID, one message ID and one UID. So I am not sure how to access contained emails on these two threads. I see that there is a references array... but inspecting this object doesn't reveal anything useful. When I log the contents of each thread, I get only part of the contents - say 4 out of the 5 messages on the thread. Not sure if this is Mailcore or an error in my saving process due to my incomplete understanding of how this is all working.
Here is my code to fetch messages:
//create fetch operation to get first (10) messages in folder (first fetch is done by sequence number, subsequent fetches are done by UID
uint64_t location = MAX([info messageCount] - DefaultPageSize + 1, 1);
uint64_t size = serverMessageCount < DefaultPageSize ? serverMessageCount - 1 : DefaultPageSize - 1;
MCOIndexSet *numbers = [MCOIndexSet indexSetWithRange:MCORangeMake(location, size)];
MCOIMAPMessagesRequestKind kind = MCOIMAPMessagesRequestKindUid |
MCOIMAPMessagesRequestKindFullHeaders |
MCOIMAPMessagesRequestKindFlags |
MCOIMAPMessagesRequestKindHeaders |
MCOIMAPMessagesRequestKindInternalDate;
if ([capabilities containsIndex:MCOIMAPCapabilityGmail]) {
kind |= MCOIMAPMessagesRequestKindGmailLabels | MCOIMAPMessagesRequestKindGmailThreadID | MCOIMAPMessagesRequestKindGmailMessageID;
self.gmailCapability = YES;
}
fetchLatestEmails ([self.imapSession fetchMessagesByNumberOperationWithFolder:folder.folderId requestKind:kind numbers:numbers]);
//perform fetch
void (^fetchLatestEmails)(MCOIMAPFetchMessagesOperation *) = ^(MCOIMAPFetchMessagesOperation *fetchOperation) {
[fetchOperation start:^(NSError *error, NSArray *emails, MCOIndexSet *vanishedMessages) {
if (nil != error) {
failure(error);
NSLog(#"the fetch error is %#", error);
return;
}
[self.dataManager performBatchedChanges:^{
if ([emails count] !=0) {
MCOIndexSet *savedThreadIds = [[MCOIndexSet alloc]init];
for (MCOIMAPMessage *email in emails) {
//do stuff with emails
Thread *thread = [self.dataManager fetchOrInsertNewThreadForFolder:folder threadId:email.gmailThreadID ?: email.gmailMessageID ?: email.uid error:nil];
if (nil != thread) {
[savedThreadIds addIndex:thread.threadId];
[self.dataManager updateOrInsertNewEmailForThread:thread uid:email.uid messageId:email.gmailMessageID date:email.header.receivedDate subject:email.header.subject from:email.header.from.mailbox to:[email.header.to valueForKey:#"mailbox"] cc:[email.header.cc valueForKey:#"mailbox"] labels:labels flags:flags error:nil];
}
if (nil != error) {
failure(error);
return;
}
}
[savedThreadIds enumerateIndexes:^(uint64_t threadId) {
[self.dataManager updateFlagsForThreadWithThreadId:threadId inFolder:folder];
}];
}
NSError *folderUpdateError;
[self.dataManager updateFolder:folder withMessageCount:serverMessageCount error:&folderUpdateError];
} error:&error];
if (nil == error) {
[self refreshFolder:folder success:^{
success();
}failure:^(NSError *error) {
}];
} else {
failure(error);
}
}];
};
Clearly something is amiss here in terms of my understanding of either Gmail or Mailcore2. If anyone can point out my misunderstanding I would appreciate it.
After opening an issue with Mailcore and a bit of research I have found the answers to my own questions.
First, my above assumptions about UID, gmailMessageID and gmailThreadID are correct. The confusion was in my looking at the Gmail conversation view of my email account on the web, and expecting my Mailcore fetch to match it. This does not happen because the conversation view, as the name implies, stitches together ALL messages of a conversation, even those that were replies to an incoming message in the inbox - ordinarily such messages would be found in the 'sent' or 'all mail' folder. In other words, what is fetched from Mailcore looks incomplete only because it is fetching what is ACTUALLY in your inbox (or whatever folder you are fetching messages from). This does NOT include replies to incoming messages. In order to include such messages (i.e. to re-create Gmail's conversation view) I understand that one must fetch sent messages from 'all mail' (or I suppose 'sent' mail) by doing a search operation using the gmailThreadID of the message of interest.
Switching the conversation view to 'off' for my Gmail account on the web while testing my Mailcore fetch operations makes testing much clearer.
Found the answer
for getting gmailThreadID we need to add MCOIMAPMessagesRequestKindGmailThreadID. in Fetch request like below example.
MCOIMAPMessagesRequestKind requestKind = (MCOIMAPMessagesRequestKind)
(MCOIMAPMessagesRequestKindHeaders | MCOIMAPMessagesRequestKindStructure |
MCOIMAPMessagesRequestKindInternalDate | MCOIMAPMessagesRequestKindHeaderSubject |
MCOIMAPMessagesRequestKindFlags | MCOIMAPMessagesRequestKindGmailThreadID);

Why am I not getting an error when sending data through the Game Center without internet?

-(BOOL)sendMessage:(NSMutableDictionary *)_message {
//*** Process _message and convert it to NSData here ***//
NSError *error;
BOOL success = [currentMatch sendDataToAllPlayers:data withDataMode:GKMatchSendDataReliable error:&error];
if (error != nil) {
NSLog(#"Sending data ERROR");
}
return success;
}
I started a match (stored in currentMatch) with another player and continuously sent data using the above method. Then I turned off the wifi.
However, I am not getting the "Sending data ERROR" log message at all.
Why? I turned off the internet connection, so why is there no error here? What could possibly lead to this scenario?
I've also confirmed that the dictionary I am sending is properly encoded into an NSData object, and success is returning YES.
As per the documentation
Return Value
YES if the data was successfully queued for transmission; NO if the match was unable to queue the data.
The method only enqueues the data for transmission, which happens asynchronously.
If you want to monitor the state of the transmission, implement the proper GKMatchDelegate delegate methods, such as match:didFailWithError:.
However, as stated in the documentation:
The match queues the data and transmits it when the network becomes available.
so if you try to perform the method with no network, the transfer just won't happen until the network is back, meaning that you won't see it failing.
By the way you should check the return value, instead of the error, since the error might be nil despite the operation being unsuccessful.
NSError *error;
BOOL success = [currentMatch sendDataToAllPlayers:data withDataMode:GKMatchSendDataReliable error:&error];
if (!success) {
NSLog(#"Sending data ERROR\n%#", error);
}
return success;
You need to check the return value of the method to know whether or not an error occurred. You cannot test the error parameter directly.
if (!success) {
NSLog(#"Sending data ERROR -- %#", error);
}
return success;
As to why you don't get an error, that send method is asynchronous. It simply enqueues the data for transmission and immediately returns. You have to catch the error through some other means (I'm not steeped in GameKit to know what that other means might be).

Resources