How many messages in Quickblox chat rooms? - quickblox

I know that I'll receive the last 50 messages in the room history. But how to know how many messages I will receive if the chat room history has less than 50 messages?

After join room
[[QBChat instance] joinRoom:testRoom];
you will receive all messages in delegate and count this way
static int count = 0;
- (void)chatRoomDidReceiveMessage:(QBChatMessage *)message fromRoom:(NSString *)roomName{
NSLog(#"Did receive message: %#, from room %#", message, roomName);
++count;
[NSObject cancelPreviousPerformRequestsWithTarget:self selector:#selector(receivedAllMessages) object:nil];
[self performSelector:#selector(receivedAllMessages) withObject:nil afterDelay:1.0];
}
- (void)receivedAllMessages{
NSLog(#"%d", count);
}

Quickblox provide a separate request to get a number of chat messages for particular dialog:Like in javascript :
var params = {chat_dialog_id: dialogId, count: 1};
QB.chat.message.list(params, function(err, messagesCount) {
if (messagesCount) {
}else{
console.log(err);
}
});

Related

Fast Enumeration..How do I know when it's finished? Completion block?

I have a BLEPeripheral that holds about thirty CBCHaracteristics. I'm building an app that interfaces with a custom BLE device. When the view loads I need to write, read, and subscribe to Characteristics on that device. The problem I'm having is I'm not sure when all my Characteristics have been assigned to the data model and if I try to load my view before thats finished my app will crash. I've confirmed this by adding a count integer and incrementing the count after it discovers each characteristic, then when my count int == 30, I load the UI. This feel extremely clunky and I feel like theres a better way to do this.
I'm trigger the loading of the data on the UI view with an observer notification. To give a little more information, I'm using a splitview, user selects the ble object that want to connect to. Once it's connected it hides the splitview and shows the detailview.
- (void)peripheral:(CBPeripheral *)peripheral didDiscoverCharacteristicsForService:(CBService *)service error:(NSError *)error {
for (CBCharacteristic *characteristic in service.characteristics) {
if(_itemCount == 30) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"STOPINDICATOR" object:self];
}
// Serial Number
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:SERIAL_NUMBER]]) {
_serialNumber = characteristic;
_itemCount++;
}
// MFG Name
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:MFG_NAME]]) {
_mfgName = characteristic;
_itemCount++;
}
// Device Type
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:DEVICE_TYPE]]) {
_deviceType = characteristic;
_itemCount++;
}
// Model Number
if ([characteristic.UUID isEqual:[CBUUID UUIDWithString:MODEL_NUMBER]]) {
_modelNumber = characteristic;
_itemCount++;
}
}
}

Synchronize Asynchronous tasks in Objective-C

I'm trying to send some images file (almost 100MB) to my iDevice clients using GCDAsyncSocket.
I want to Synchronously send packets to the clients. I mean after sending 100MB of data to first client iterating to the next client.but because of Asynchronous nature of GCDAsyncSocket I don't know how can I serialize these packet sending.
I can't use semaphore because before sending images I negotiate with each client to know what images I should send then try to send those images. and I can't find a neat way to wait and signal the semaphore.
- (void)sendImagesToTheClients:clients
{
...
//negotiating with client to know which images should sent
...
for(Client* client in clients)
{
packet = [packet addImages: images];
[self sendPacket:packet toClient:client];
}
}
- (void)sendPacket:packet toClient:client
{
// Initialize Buffer
NSMutableData *buffer = [[NSMutableData alloc] init];
NSData *bufferData = [NSKeyedArchiver archivedDataWithRootObject:packet];
uint64_t headerLength = [bufferData length];
[buffer appendBytes:&headerLength length:sizeof(uint64_t)];
[buffer appendBytes:[bufferData bytes] length:[bufferData length]];
// Write Buffer
[client.socket writeData:buffer withTimeout:-1.0 tag:0];
}
this is how AsyncSocket writing data works:
- (void)writeData:(NSData *)data withTimeout:(NSTimeInterval)timeout tag:(long)tag
{
if ([data length] == 0) return;
GCDAsyncWritePacket *packet = [[GCDAsyncWritePacket alloc] initWithData:data timeout:timeout tag:tag];
dispatch_async(socketQueue, ^{ #autoreleasepool {
LogTrace();
if ((flags & kSocketStarted) && !(flags & kForbidReadsWrites))
{
[writeQueue addObject:packet];
[self maybeDequeueWrite];
}
}});
// Do not rely on the block being run in order to release the packet,
// as the queue might get released without the block completing.
}
so how can I synchronize this task?
UPDATE
for socket connection I use GCDAsyncSocket which heavily uses delegation for event notification.(GCDAsyncSocket.h and GCDAsyncSocket.m) (no method with completionHandler).
I have written a class named TCPClient which handles socket connection and packet sending and set it as the delegate of initialized socket.
after writing a packet, the delegate method - (void)socket:(GCDAsyncSocket *)sock didWriteDataWithTag:(long)tag gets called. which only informs me some data has been written. here I can't decide based of written data to call dispatch_group_leave. so relying delegate method is useless.
I have modified [client.socket writeData:buffer withTimeout:-1.0 tag:0] in GCDAsyncSocket.h and .m files to accept a completionBlock: [client.socket writeData:buffer withTimeout:-1.0 tag:0 completionBlock:completionBlock]
using this approach helps me to solve synchronizing async tasks.
// perform your async task
dispatch_async(self.socketQueue, ^{
[self sendPacket:packet toClient:client withCompletion:^(BOOL finished, NSError *error)
{
if (finished) {
NSLog(#"images file sending finished");
//leave the group when you're done
dispatch_group_leave(group);
}
else if (!finished && error)
{
NSLog(#"images file sending FAILED");
}
}];
but the problem is after updating GCDAsyncsocket, my code may break.
here I'm looking for neat way to add completion handler to GCDAsyncsocket without modifying it directly. like creating a wrapper around it or using features of objective-c runtime.
do you have any idea?
You can accomplish this with dispatch groups. For a async task with a completion block:
//create & enter the group
dispatch_group_t group = dispatch_group_create();
dispatch_group_enter(group);
// perform your async task
dispatch_async(socketQueue, ^{
//leave the group when you're done
dispatch_group_leave(group);
});
// wait for the group to complete
// (you can use DISPATCH_TIME_FOREVER to wait forever)
long status = dispatch_group_wait(group,
dispatch_time(DISPATCH_TIME_NOW,NSEC_PER_SEC * COMMAND_TIMEOUT));
// check to see if it timed out, or completed
if (status != 0) {
// timed out
}
Alternatively for a task with a delegate:
#property (nonatomic) dispatch_group_t group;
-(BOOL)doWorkSynchronously {
self.group = dispatch_group_create();
dispatch_group_enter(self.group);
[object doAsyncWorkWithDelegate:self];
long status = dispatch_group_wait(self.group,
dispatch_time(DISPATCH_TIME_NOW,NSEC_PER_SEC * COMMAND_TIMEOUT));
// A
if (status != 0) {
// timed out
return NO
}
return YES;
}
-(void)asyncWorkCompleted {}
// after this call, control should jump back to point A in the doWorkSynchronously method
dispatch_group_leave(self.group);
}

RMStore Receipt Verification Failure Flow

I am using RMStore to verify receipts. As a note, I am not using RMStore for the actual purchase portion. The process is successfully dealing with success and failure in terms of throwing errors and not delivering content if the receipt is invalid. I purposefully changed the bundle to force a failure as a test. My question though is with the failure process and the confirmation Apple sends.
The issue is that while this process does detect the failure to verify and therefore does prevent the content from being sent to the user, Apple still afterwards comes back with a dialog box about the purchase being successful. The good news is that the purchase isn't successful and the content isn't delivered, but I would prefer that this dialog box from Apple not show as it will create confusion.
Here is my implementation of the check. For now I am just testing the failure scenario before doing more within the failure block.
- (void)completeTransaction:(SKPaymentTransaction *)transaction {
NSLog(#"completeTransaction...");
RMStoreAppReceiptVerificator *verifyReceipt = [[RMStoreAppReceiptVerificator alloc]init];
[verifyReceipt verifyTransaction:transaction success:^{
[self provideContentForProductIdentifier:transaction.payment.productIdentifier];
[[SKPaymentQueue defaultQueue] finishTransaction:transaction];
}failure:^(NSError *error){
NSLog(#"failure to verify: %#",error.description);
}];
}
Is there a way within the failure block to halt the process at Apple that creates their success dialog or do I need to perform this check at an earlier stage?
Update:
In looking further at this, the method above is being called by the state SKPaymentTransactionStatePurchased The definition of that state per Apple is:
"The App Store successfully processed payment. Your application should provide the content the user purchased."
This tells me that it is likely too late to prevent the dialog. There is an earlier state, however, I would think the receipt verification has to come after purchase but before delivery of content (otherwise there would not be a purchase to verify). So is this just a matter of having to deal with the conflicting message or am I missing something?
Update 2: Adding some more methods per the request in comments
#interface IAPHelper () <SKProductsRequestDelegate, SKPaymentTransactionObserver>
#end
#implementation IAPHelper
{
SKProductsRequest * _productsRequest;
RequestProductsCompletionHandler _completionHandler;
NSSet * _productIdentifiers;
NSMutableSet * _purchasedProductIdentifiers;
NSDictionary *_mappingDict;
}
- (id)initWithProductIdentifiers:(NSSet *)productIdentifiers andMappings:(NSDictionary *)mappingDict
{
if ((self = [super init])) {
// Store product identifiers & mappings
_productIdentifiers = productIdentifiers;
_mappingDict = mappingDict;
// Add self as transaction observer
[[SKPaymentQueue defaultQueue] addTransactionObserver:self];
}
return self;
}
- (void)requestProductsWithCompletionHandler:(RequestProductsCompletionHandler)completionHandler {
// 1
_completionHandler = [completionHandler copy];
// 2
_productsRequest = [[SKProductsRequest alloc] initWithProductIdentifiers:_productIdentifiers];
_productsRequest.delegate = self;
[_productsRequest start];
}
- (BOOL)productPurchased:(NSString *)productIdentifier {
return [_purchasedProductIdentifiers containsObject:productIdentifier];
}
- (void)buyProduct:(SKProduct *)product {
NSLog(#"Buying %#...", product.productIdentifier);
SKPayment * payment = [SKPayment paymentWithProduct:product];
[[SKPaymentQueue defaultQueue] addPayment:payment];
}
#pragma mark - SKProductsRequestDelegate
- (void)productsRequest:(SKProductsRequest *)request didReceiveResponse:(SKProductsResponse *)response {
NSLog(#"Loaded list of products...");
_productsRequest = nil;
NSArray * skProducts = response.products;
for (SKProduct * skProduct in skProducts) {
NSLog(#"Found product: %# %# %0.2f",
skProduct.productIdentifier,
skProduct.localizedTitle,
skProduct.price.floatValue);
}
if (_completionHandler)
{
_completionHandler(YES, skProducts);
_completionHandler = nil;
}
}
- (void)request:(SKRequest *)request didFailWithError:(NSError *)error {
NSLog(#"Failed to load list of products.");
_productsRequest = nil;
if (_completionHandler)
{
_completionHandler(NO, nil);
_completionHandler = nil;
}
}
Here is the specific method that calls completeTransaction
- (void)paymentQueue:(SKPaymentQueue *)queue updatedTransactions:(NSArray *)transactions
{
for (SKPaymentTransaction * transaction in transactions) {
switch (transaction.transactionState)
{
case SKPaymentTransactionStatePurchased:
[self completeTransaction:transaction];
break;
case SKPaymentTransactionStateFailed:
[self failedTransaction:transaction];
break;
case SKPaymentTransactionStateRestored:
[self restoreTransaction:transaction];
default:
break;
}
};
}
We established in the comments that this question doesn't have anything to do with RMStore.
You're not testing a real fraud scenario so it doesn't matter if Apple shows an alert.
This would involve either using a fake receipt, or sending fake calls to the Store Kit transaction observer. In neither of those cases you would get the alert.
When using a valid transaction to simulate a failure scenario you can't expect Apple to consider the transaction invalid as well. There is no API to tell Apple that a transaction is fraudulent. You can only finish a transaction, or not.

Objective C and ARC: Object produced in one thread does not get released in the consuming thread

I have a small project that reads an HTTP stream from a remote server, demuxes it, extracts audio stream, decodes it into 16-bit PCM, and feeds into a corresponding AudioQueue. The decoder/demuxer/fetcher runs in a separate thread and it uses my home-grown blocking queue (see code below) to deliver the decoded frames to the AudioQueue callback. The queue uses NSMutableArray to store objects.
Once this thing is in-flight, it leaks objects inserted into the queue. Memory profiler says that the RefCt is 2 by the time I expect it to be 0 and to be released by ARC.
Here are the queue/dequeue methods:
- (id) dequeue {
dispatch_semaphore_wait(objectsReady, DISPATCH_TIME_FOREVER);
[lock lock];
id anObject = [queue objectAtIndex:0];
[queue removeObjectAtIndex:0];
[lock unlock];
dispatch_semaphore_signal(freeSlots);
return anObject;
}
- (void) enqueue:(id)element {
dispatch_semaphore_wait(freeSlots, DISPATCH_TIME_FOREVER);
[lock lock];
[queue addObject:element];
[lock unlock];
dispatch_semaphore_signal(objectsReady);
}
Producer thread does this:
[pAudioFrameQueue enqueue:[self convertAVFrameAudioToPcm:audioFrame]];
And the "convertAVFrameAudioToPcm" methods looks like this:
- (NSData*) convertAVFrameAudioToPcm:(AVFrame*)frame {
NSData* ret = nil;
int16_t* outputBuffer = malloc(outputByteLen);
// decode into outputBuffer and other stuff
ret = [NSData dataWithBytes:outputBuffer length:outputByteLen];
free(outputBuffer);
return ret;
}
Consumer does this:
- (void) fillAvailableAppleAudioBuffer:(AudioQueueBufferRef)bufferToFill {
#autoreleasepool {
NSData* nextAudioBuffer = [pAudioFrameQueue dequeue];
if (nextAudioBuffer != nil) {
[nextAudioBuffer getBytes:bufferToFill->mAudioData]; // I know this is not safe
bufferToFill->mAudioDataByteSize = nextAudioBuffer.length;
} else {
NSLog(#"ERR: End of stream...");
}
}
}
To me it looks like RefCt should become 0 when fillAvailableAppleAudioBuffer exits, but apparently ARC disagrees and does not release the object.
Am I having a bug in my simple queue code?
Or do I instantiate NSData in a wrong way?
Or am I missing some special rule of how ARC works between threads? By the way, the producer threads starts like this:
- (BOOL) startFrameFetcher {
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT,
(unsigned long)NULL),
^(void) {
[self frameFetcherThread];
});
return YES;
}
Any hints will be much appreciated!
PS: and last but not the least, I do have another instance of the same blocking queue that stores video frames that I dequeue and show via NSTimer. Video frames do not leak! I am guessing this may have something to do with threading. Otherwise, I would have expected to see the leak in both queues.

Geocoding Multiple Locations - Knowing When "All" Completion Blocks Have Been Called

I am using the CoreLocation's geocoder to get the CLLocation coordinates for multiple map items. The geocoder calls a completion block on completion for each item.
How do I create a similar block functionality which is called when all of these containing asynchronous geocoder calls have been completed? (I could use a manual counter. But there must be a more elegant solution)
Here's my geocoding function so far. It loops through an array of location items and starts a new geocoding process for each.
-(void)geoCodeAllItems {
for (EventItem* thisEvent in [[EventItemStore sharedStore] allItems]) {
if (![thisEvent eventLocationCLLocation]){ //Only geocode if the item has no location data yet
CLGeocoder *geocoder = [[CLGeocoder alloc]init];
[geocoder geocodeAddressString:[thisEvent eventLocationGeoQuery] completionHandler:^(NSArray *placemarks, NSError *error) {
if (error){
NSLog(#"\t Geo Code - Error - Failed to geocode";
return;
}
if (placemarks)
{
if ([placemarks count] > 1) NSLog(#"\t Geo Code - Warning - Multiple Placemarks (%i) returned - Picking the first one",[placemarks count]);
CLPlacemark* placemark = [[CLPlacemark alloc]initWithPlacemark:[placemarks objectAtIndex:0]];
CLLocationCoordinate2D placeCoord = [[placemark location]coordinate];
[thisEvent setEventLocationCLLocation:[[CLLocation alloc]initWithLatitude:placeCoord.latitude longitude:placeCoord.longitude]];
[[EventItemStore sharedStore] saveItems];
} else {
NSLog(#"\t Geo Code - Error - No Placemarks decoded");
}
}];
geocoder = nil;
}
}
}
This basically works however due to the asynchronous fashion I don't know when all geocoding activity has finally ended.
My feeling is, I either have to create an block for this or use Grand Central Dispatch but I am not really sure. I appreciate any help on this to find the right approach.
You can use a GCD dispatch group to do this. Also, I think you can make multiple requests with a single CLGeocoder.
Since we might not need to create the group and the geocoder at all, we'll create them lazily:
-(void)geocodeAllItems {
dispatch_group_t group = NULL;
CLGeocoder *geocoder = nil;
We loop over the items, skipping the ones that have already been geocoded:
for (EventItem *item in [[EventItemStore sharedStore] allItems]) {
if (item.eventLocationCLLocation)
continue;
Now that we've found one that needs geocoding, we create the geocoder and the dispatch group if we need to:
if (!geocoder) {
geocoder = [[CLGeocoder alloc] init];
group = dispatch_group_create();
}
We'll use a helper method to geocode just this item:
[self geocodeItem:item withGeocoder:geocoder dispatchGroup:group];
}
Now that we've gone through all the items, we'll check whether we geocoded any:
if (group) {
If we geocoded any, then there are blocks in the dispatch group. We'll ask the group to execute a notification block when it becomes empty:
dispatch_group_notify(group, dispatch_get_main_queue(), ^{
NSLog(#"note: all geocoding requests have completed");
});
Finally, we need to release the group to balance the +1 retain count returned by dispatch_group_create:
dispatch_release(group);
}
}
Here's the helper method that geocodes just one item:
- (void)geocodeItem:(EventItem *)item withGeocoder:(CLGeocoder *)geocoder dispatchGroup:(dispatch_group_t)group {
We “enter” the group. This increments the group's membership counter atomically:
dispatch_group_enter(group);
Then we can start the geocoding:
[geocoder geocodeAddressString:item.eventLocationGeoQuery completionHandler:^(NSArray *placemarks, NSError *error) {
if (error) {
NSLog(#"error: geocoding failed for item %#: %#", item, error);
} else {
if (placemarks.count == 0) {
NSLog(#"error: geocoding found no placemarks for item %#", item);
} else {
if (placemarks.count > 1) {
NSLog(#"warning: geocoding found %u placemarks for item %#: using the first", item, placemarks.count);
}
CLPlacemark* placemark = placemarks[0];
thisEvent.eventLocationCLLocation = placemarks[0].location;
[[EventItemStore sharedStore] saveItems];
}
}
In the geocoding completion block, after all the work is done, we “leave” the group, which decrements its membership count:
dispatch_group_leave(group);
}];
}
When the membership count goes to zero, the group will execute the notification block we added at the end of geocodeAllItems.
Take a look at NSBlockOperation used with NSOperationQueue
create an NSBlockOperation and then add all of the separate tasks as execution blocks addExecutionBlock: . Set your completion block setCompletionBlock: which is in the NSOperation super class and it will get called when all of the tasks are finished. They will by default not be run on the main thread so if you want your completion block to be executed on the main thread you will have to explicitly tell it to do so. Add the NSBlockOperation to a NSOperationQueue addOperation:(NSOperation *)operation
Reference to the Concurrency Guide page on Operation Queues
Recommend: WWDC 2012 video session 225 (skip to 16:55)