Sending text chat msg to other player with Game Center? - ios

Is it possible to send text chat msgs to other players via Game Center? is their any dedicated way to do that with the GameKit API? or would it just have to be put into the turn data that is sent between players?

You have to write your own, there is no method in game center to allow for chatting.

You can just send the text as normal data during the game. In order to do that
Method to prepare data to send
-(void)sendText:(NSString *) text {
NSString * text2Send = [NSString stringWithFormat:#"%#", text];
[self sendData:[text2Send dataUsingEncoding:NSUTF8StringEncoding]];
}
The send data method will be the normal data sending method of Game center as
- (BOOL)sendDataToAllPlayers:(NSData *)data withDataMode:(GKMatchSendDataMode)mode error:(NSError **)error;
and in order to resolve the received data
NSString * rawText = [[NSString alloc] initWithBytes:[data bytes] length:[data length] encoding:NSUTF8StringEncoding];
if(rawText.length > 0)
{
//Do what ever you want with the text
}

Like already proposed, you can use GKTurnBasedMatch's matchData for that. If your game is turn-based, you can have two game data message types: one for sending game data at the end of each player's turn, and the other for sending chat messages. Or you can combine them so that a message is sent when each player ends her turn. However if you only use matchData to send chat messages, make sure it doesn't end player's turn, otherwise you will have game synchronization issues. For that use saveCurrentTurnWithMatchData:completionHandler: method for sending your message; on the other side GameKit will call your turn callback as it receives the message - you should read the updated matchData and see whether it is a message or a game state update - that's relatively easy if you use JSON or XML or NSDictionary serialization for sending data back and forth - you can introduce something like dataType property there that would let you distinguish between text message and game state update.
There's also an option for changing the match.message contents, however if you put your short message there, the opponent(s) will only see it when they receive 'your turn' notification from the GameCenter.

Related

Reading Bluetooth LE CBCharacteristic returns a smaller value on iOS 7 [duplicate]

I have a characteristic value which contains the data for an image. In the peripheral I setup the value like this:
_photoUUID = [CBUUID UUIDWithString:bPhotoCharacteristicUUID];
_photoCharacteristic = [[CBMutableCharacteristic alloc] initWithType:_photoUUID
properties:CBCharacteristicPropertyRead
value:Nil
permissions:CBAttributePermissionsReadable];
My understanding is that when this value is requested, the didReceiveReadRequest callback will be called:
-(void) peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {
if ([request.characteristic.UUID isEqual:_photoUUID]) {
if (request.offset > request.characteristic.value.length) {
[_peripheralManager respondToRequest:request withResult:CBATTErrorInvalidOffset];
return;
}
else {
// Get the photos
if (request.offset == 0) {
_photoData = [NSKeyedArchiver archivedDataWithRootObject:_myProfile.photosImmutable];
}
request.value = [_photoData subdataWithRange:NSMakeRange(request.offset, request.characteristic.value.length - request.offset)];
[_peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
}
}
}
This comes pretty much from Apple's documentation. On the Central side in the didDiscoverCharacteristic callback I have the following code:
if ([characteristic.UUID isEqual:_photoUUID]) {
_photoCharacteristic = characteristic;
[peripheral readValueForCharacteristic:characteristic];
}
Which in turn calls the didUpdateValueForCharacteristic callback:
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
NSLog(#"updated value for characteristic");
if ([characteristic.UUID isEqual:_photoUUID]) {
NSArray * photos = [NSKeyedUnarchiver unarchiveObjectWithData:characteristic.value];
}
}
All of the callbacks are called but when I try to re-construct the array, it's corrupted because not all of the data is transferred correctly. I would expect the didRecieveReadRequest callback to be called multiple times with a different offset each time. However it's only called once.
I was wondering if anyone knew what I'm doing wrong?
I'm guessing you're bumping up against the 512 byte limit on characteristic length. You'll need to move to subscriptions to characteristics and processing of updates to get around this:
On the central:
Subscribe to the characteristic by calling -[CBPeripheral setNotifyValue:forCharacteristic] (with YES as the notify value).
In -peripheral:didUpdateValueForCharacteristic:error, every update will either be data to append, or something you choose to use on the peripheral side to indicate end-of-data (I use an empty NSData for this). Update your -peripheral:didUpdateValueForCharacteristic:error code so that:
If you're starting to read a value, initialize a sink for the incoming bytes (e.g. an NSMutableData).
If you're in the middle of reading a value, you append to the sink.
If you see the EOD marker, you consider the transfer complete. You may wish to unsubscribe from the characteristic at this state, by calling -[CBPeripheral setNotifyValue:forCharacteristic] with a notify value of NO.
-peripheral:didUpdateNotificationStateForCharacteristic:error: is a good spot to manage the initialization and later use of the sink into which you read chunks. If characteristic.isNotifying is updated to YES, you have a new subscription; if it's updated to NO then you're done reading. At this point, you can use NSKeyedUnarchiver to unarchive the data.
On the peripheral:
In -[CBMutableCharacteristic initWithType:properties:value:permissions], make sure the properties value includes CBCharacteristicPropertyNotify.
Use -peripheralManager:central:didSubscribeToCharacteristic: to kick off the chunking send of your data, rather than -peripheral:didReceiveReadRequest:result:.
When chunking your data, make sure your chunk size is no larger than central.maximumUpdateValueLength. On iOS7, between an iPad 3 and iPhone 5, I've typically seen 132 bytes. If you're sending to multiple centrals, use the least common value.
You'll want to check the return code of -updateValue:forCharacteristic:onSubscribedCentrals; if underlying queue backs up, this will return NO, and you'll have to wait for a callback on -peripheralManagerIsReadyToUpdateSubscribers: before continuing (this is one of the burrs in an otherwise smooth API, I think). Depending upon how you handle this, you could paint yourself into a corner because:
If you're constructing and sending your chunks on the same queue that the peripheral is using for its operations, AND doing the right thing and checking the return value from -updateValue:forCharacteristic:onSubscribedCentrals:, it's easy to back yourself into a non-obvious deadlock. You'll either want to make sure that you yield the queue after each call to -updateValue:forCharacteristic:onSubscribedCentrals:, perform your chunking loop on a different queue than the peripheral's queue (-updateValue:forCharacteristic:onSubscribedCentrals: will make sure its work is done in the right place). Or you could get fancier; just be mindful of this.
To see this in action, the WWDC 2012 Advanced Core Bluetooth video contains an example (sharing VCards) that covers most of this. It doesn't however, check the return value on the update, so they avoid the pitfalls in #4 altogether.
Hope that helps.
I tried the approach described by Cora Middleton, but couldn't get it to work. If I understand her approach correctly, she would send all partial data through the update notifications. The problem for me seemed to be that there was no guarantee each update would be read by the central if the values in these notifications would change often in short succession.
So because that approach didn't work, I did the following:
There's some characteristic that I use to keep track of the state of the peripheral. This characteristic would only contain some flags and would send out notifications if one or more flags change. Interactions by the user on the peripheral would change the state and there's one action on the peripheral that the user can perform to trigger a download from a connected central.
The data to be downloaded from the central is added to a stack on the peripheral. The last item on the stack is a terminator indicator (an empty NSData object)
The central registers to receive notifications of the aforementioned state characteristic. If some flag is set, a download is triggered.
On the peripheral side, every time I receive a read request for a certain characteristic, I remove 1 item from the stack and return this item.
On the central side I add all data that is returned from the read requests. If the empty data value is retrieved, then I create an object from the returned data (in my case it's a JSON string).
On the peripheral side I also know the download is finished after returning the empty NSData object, so afterwards I can change the state once again for the peripheral.

Problems using notifications as a callback?

I am trying to update a view when something happens in another class, and after some looking, it appeared that the most common way to do this was to use either delegates or blocks to create a callback. However, I was able to accomplish this task using notifications. What I want to know is: Is there a problem using notifications to trigger methods calls? Are there any risks I'm not aware of? Is there a reason I'd want to use blocks/delegates over notifications?
I'm new to Objective-C, so I'm not sure if the approach I'm taking is correct.
As an example, I'm trying to set the battery level of a BLE device on the ViewController. I have a BluetoothLEManager, which discovers the peripheral, its services/characteristics, etc. But to do this, I need to initiate the "connection" in the detailViewController, then update the battery level once I find it.
Here is some example code of what I'm doing:
DetailViewController.m
- (void)viewDidLoad {
[super viewDidLoad];
// Do any additional setup after loading the view.
NSLog(#"Selected tag UUID: %#", [selectedTag.tagUUID UUIDString]);
tagName.text = selectedTag.mtagName;
if(selectedTag.batteryLevel != nil){
batteryLife.text = selectedTag.batteryLevel;
}
uuidLabel.text = [selectedTag.tagUUID UUIDString];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(setBatteryLevel:) name:#"SetBatteryLevel" object:nil];
}
...
-(void)setBatteryLevel:(NSNotification*)notif{
NSMutableString* batLevel = [[NSMutableString alloc]initWithString:[NSString stringWithFormat:#"%#", selectedTag.batteryLevel]];
[batLevel appendString:#" %"];
selectedTag.batteryLevel = batLevel;
batteryLife.text = selectedTag.batteryLevel;
}
BluetoothLEManager.m:
...
-(void) getBatteryLevel:(CBCharacteristic *)characteristic error:(NSError *)error fetchTag:(FetchTag *)fetchTag
{
NSLog(#"Getting battery Level...");
NSData* data = characteristic.value;
const uint8_t* reportData = [data bytes];
uint16_t batteryLevel = reportData[0];
selectedTag.batteryLevel = [NSString stringWithFormat:#"%i", batteryLevel];
NSLog(#"Battery Level is %#", [NSString stringWithFormat:#"%i", batteryLevel]);
[[NSNotificationCenter defaultCenter] postNotificationName:#"SetBatteryLevel" object:nil];
}
...
Let me know if you need any other code, but this is the basics of it all.
Each approach has different strengths and weaknesses.
Delegates and protocols require a defined interface between the object and it's delegate, a one-to-one relationship, and that the object have specific knowledge of the delegate object it's going to call.
Methods with completion blocks involve a similar one-to-one relationship between an object and the object that invokes the method. However since blocks inherit the scope in which they're defined, you have more flexibility as to the context that's available in the completion block. Blocks also allow the caller to define the completion code in same place that the call takes place, making you code more self-documenting.
In both cases, the object that is notifying the delegate or invoking the completion block has to know who it's talking to, or what code is being executed.
A delegate call is like an auto shop calling you back to let you know your car is done. The service manager has to have your phone number and know that you want a call.
A block is more like a recipe you give to a chef. Give the chef a different recipe and he/she performs a different task for you.
Notifications are much less tightly coupled. It's like a town crier, yelling announcements in a crowded public square. The crier doesn't need to know who's listening, or how many people are listening.
Likewise, when you send a notification, you don't know who, if anybody, is listening, or how many listeners there are. You don't need to know. If 10 objects care about the message you are broadcasting, they can all listen for it, and they'll all be notified. The message sender doesn't have to know or care who's listening.
Sometimes you want tighter coupling, and sometimes you want looser coupling. It depends on the problem you're trying to solve.

Reading long characteristic values using CoreBluetooth

I have a characteristic value which contains the data for an image. In the peripheral I setup the value like this:
_photoUUID = [CBUUID UUIDWithString:bPhotoCharacteristicUUID];
_photoCharacteristic = [[CBMutableCharacteristic alloc] initWithType:_photoUUID
properties:CBCharacteristicPropertyRead
value:Nil
permissions:CBAttributePermissionsReadable];
My understanding is that when this value is requested, the didReceiveReadRequest callback will be called:
-(void) peripheralManager:(CBPeripheralManager *)peripheral didReceiveReadRequest:(CBATTRequest *)request {
if ([request.characteristic.UUID isEqual:_photoUUID]) {
if (request.offset > request.characteristic.value.length) {
[_peripheralManager respondToRequest:request withResult:CBATTErrorInvalidOffset];
return;
}
else {
// Get the photos
if (request.offset == 0) {
_photoData = [NSKeyedArchiver archivedDataWithRootObject:_myProfile.photosImmutable];
}
request.value = [_photoData subdataWithRange:NSMakeRange(request.offset, request.characteristic.value.length - request.offset)];
[_peripheralManager respondToRequest:request withResult:CBATTErrorSuccess];
}
}
}
This comes pretty much from Apple's documentation. On the Central side in the didDiscoverCharacteristic callback I have the following code:
if ([characteristic.UUID isEqual:_photoUUID]) {
_photoCharacteristic = characteristic;
[peripheral readValueForCharacteristic:characteristic];
}
Which in turn calls the didUpdateValueForCharacteristic callback:
- (void)peripheral:(CBPeripheral *)peripheral didUpdateValueForCharacteristic:(CBCharacteristic *)characteristic error:(NSError *)error {
NSLog(#"updated value for characteristic");
if ([characteristic.UUID isEqual:_photoUUID]) {
NSArray * photos = [NSKeyedUnarchiver unarchiveObjectWithData:characteristic.value];
}
}
All of the callbacks are called but when I try to re-construct the array, it's corrupted because not all of the data is transferred correctly. I would expect the didRecieveReadRequest callback to be called multiple times with a different offset each time. However it's only called once.
I was wondering if anyone knew what I'm doing wrong?
I'm guessing you're bumping up against the 512 byte limit on characteristic length. You'll need to move to subscriptions to characteristics and processing of updates to get around this:
On the central:
Subscribe to the characteristic by calling -[CBPeripheral setNotifyValue:forCharacteristic] (with YES as the notify value).
In -peripheral:didUpdateValueForCharacteristic:error, every update will either be data to append, or something you choose to use on the peripheral side to indicate end-of-data (I use an empty NSData for this). Update your -peripheral:didUpdateValueForCharacteristic:error code so that:
If you're starting to read a value, initialize a sink for the incoming bytes (e.g. an NSMutableData).
If you're in the middle of reading a value, you append to the sink.
If you see the EOD marker, you consider the transfer complete. You may wish to unsubscribe from the characteristic at this state, by calling -[CBPeripheral setNotifyValue:forCharacteristic] with a notify value of NO.
-peripheral:didUpdateNotificationStateForCharacteristic:error: is a good spot to manage the initialization and later use of the sink into which you read chunks. If characteristic.isNotifying is updated to YES, you have a new subscription; if it's updated to NO then you're done reading. At this point, you can use NSKeyedUnarchiver to unarchive the data.
On the peripheral:
In -[CBMutableCharacteristic initWithType:properties:value:permissions], make sure the properties value includes CBCharacteristicPropertyNotify.
Use -peripheralManager:central:didSubscribeToCharacteristic: to kick off the chunking send of your data, rather than -peripheral:didReceiveReadRequest:result:.
When chunking your data, make sure your chunk size is no larger than central.maximumUpdateValueLength. On iOS7, between an iPad 3 and iPhone 5, I've typically seen 132 bytes. If you're sending to multiple centrals, use the least common value.
You'll want to check the return code of -updateValue:forCharacteristic:onSubscribedCentrals; if underlying queue backs up, this will return NO, and you'll have to wait for a callback on -peripheralManagerIsReadyToUpdateSubscribers: before continuing (this is one of the burrs in an otherwise smooth API, I think). Depending upon how you handle this, you could paint yourself into a corner because:
If you're constructing and sending your chunks on the same queue that the peripheral is using for its operations, AND doing the right thing and checking the return value from -updateValue:forCharacteristic:onSubscribedCentrals:, it's easy to back yourself into a non-obvious deadlock. You'll either want to make sure that you yield the queue after each call to -updateValue:forCharacteristic:onSubscribedCentrals:, perform your chunking loop on a different queue than the peripheral's queue (-updateValue:forCharacteristic:onSubscribedCentrals: will make sure its work is done in the right place). Or you could get fancier; just be mindful of this.
To see this in action, the WWDC 2012 Advanced Core Bluetooth video contains an example (sharing VCards) that covers most of this. It doesn't however, check the return value on the update, so they avoid the pitfalls in #4 altogether.
Hope that helps.
I tried the approach described by Cora Middleton, but couldn't get it to work. If I understand her approach correctly, she would send all partial data through the update notifications. The problem for me seemed to be that there was no guarantee each update would be read by the central if the values in these notifications would change often in short succession.
So because that approach didn't work, I did the following:
There's some characteristic that I use to keep track of the state of the peripheral. This characteristic would only contain some flags and would send out notifications if one or more flags change. Interactions by the user on the peripheral would change the state and there's one action on the peripheral that the user can perform to trigger a download from a connected central.
The data to be downloaded from the central is added to a stack on the peripheral. The last item on the stack is a terminator indicator (an empty NSData object)
The central registers to receive notifications of the aforementioned state characteristic. If some flag is set, a download is triggered.
On the peripheral side, every time I receive a read request for a certain characteristic, I remove 1 item from the stack and return this item.
On the central side I add all data that is returned from the read requests. If the empty data value is retrieved, then I create an object from the returned data (in my case it's a JSON string).
On the peripheral side I also know the download is finished after returning the empty NSData object, so afterwards I can change the state once again for the peripheral.

iOS - intercept outgoing SMS messages

is it possible to intercept outgoing SMS messages on a jailbroken iOS 6 device? What I want to do is reroute SMS messages via a free service, but still "reuse" the entire Messages.app UI.
I have seen some info about intercepting SMS on iOS, but they all talk about incoming messages, not outgoing messages.
First, you need to inject your code in MobileSMS application.
Now you can hook -send: method in CKTranscriptController. This method is called when you press "Send" button in MobileSMS. It's argument doesn't contain any information about message being sent so we need to find it manually:
1) Text and subject. CKTranscriptController has instance variable CKMessageEntryView *_entryView.
CKContentEntryView *contentEntryView = [_entryView entryField];
CKMessageStandaloneComposition *composition = [contentEntryView messageComposition];
NSString* subject = [composition subject];
NSString* text = [composition textString];
2) Recipients. CKTranscriptController has instance variable CKRecipientSelectionView *_recipientSelectionView. It's -recipients method returns array of MFComposeRecipient objects. Call MFComposeRecipient instance method -rawAddress to get address string.
Most of the classes can be found in private ChatKit.framework. MFComposeRecipient located in private 'MessageUI.framework'.

RedPark Serial Cable partial data

I have an RFID scanner attached to a RedPark serial cable connected to an iPad app. When people scan their RFID cards, I get a callback with -readBytesAvailable:. However, sometimes it doesn't give me the entire RFID in one call. Sometimes it send it in two calls.
How can I determine if I've received everything? When my code takes the first callback's data and tries to use it, I get an error, because let's say the RFID was "123456789" sometimes I'll get one call with #"12" and a second call with #"3456789". So I try to process #"12" and get a user not found error, then I try to process #"3456789" and get a user not found error.
How can I tell if I'm done reading data? The lengths of the RFIDs can vary from vendor to vendor, so I can't just assume I need to read a certain number of digits.
This is the method I use to receive the data from the scanner through the RedPark:
- (void) readBytesAvailable:(UInt32)length {
NSLog(#"readBytesAvailable: %lu", length);
UInt8 rxLoopBuff[LOOPBACK_TEST_LEN];
[self.rfidManager read:rxLoopBuff Length:length];
NSString *rfid = [[NSString alloc] initWithBytes:rxLoopBuff length:length encoding:NSUTF8StringEncoding];
NSLog(#"rfid=%#", rfid);
[self receivedScanOfRFID:rfid];
}
Serial port gives you no control over packetization. Data is just a stream of bytes with no way to predict which bytes appear in each read call. You have to parse the data stream itself to interpret the contents and understand start/end of your messages. You either need to look for a reliable terminating character or potentially use a timeout approach where you do multiple reads until you get no more data for some period of time. I don't recommend the timeout approach.

Resources