captureOutput:didOutputMetadataObjects:fromConnection: Deleagate firing Multiple times. I need to pass scanned value to server so multiple calls happens to server for one scan
Here is the same question
Delegate method being invoked multiple times, even after returning to a view
As Apple documentation says : "this method may be called frequently, your implementation should be efficient to prevent capture performance problems, including dropped metadata objects".
handle multiple invoke do the following :
- (void)captureOutput:(AVCaptureOutput *)captureOutput didOutputMetadataObjects:(NSArray *)metadataObjects fromConnection:(AVCaptureConnection *)connection
{
id capturedData;
if ([metadataObjects count] > 0) {
// handle your captured data here
[self performSelectorOnMainThread:#selector(stopReading:) withObject:capturedData waitUntilDone:NO];
}
}
the stopReading: method looks (assuming your _session is AVCaptureSession object and _prevLayer is AVCaptureVideoPreviewLayer you used earlier) :
-(void)stopReading:(id) data{
NSLog(#"stop reading");
[_session stopRunning];
_session = nil;
[_prevLayer removeFromSuperlayer];
// do what you want with captured data
[self.delegate didScanBarCodeWithContext:data];
}
Related
Following TDD I'm developing an iPad app that downloads some info from the internet and displays it on a list, allowing the user to filter that list using a search bar.
I want to test that, as the user types in the search bar, the internal variable with the filter text is updated, the filtered list of items is updated, and finally the table view receives a "reloadData" message.
These are my tests:
- (void)testSutChangesFilterTextWhenSearchBarTextChanges
{
// given
sut.filterText = #"previous text";
// when
[sut searchBar:nil textDidChange:#"new text"];
// then
assertThat(sut.filterText, is(equalTo(#"new text")));
}
- (void)testSutReloadsTableViewDataAfterChangeFilterTextFromSearchBar
{
// given
sut.tableView = mock([UITableView class]);
// when
[sut searchBar:nil textDidChange:#"new text"];
// then
[verify(sut.tableView) reloadData];
}
NOTE: Changing the "filterText" property triggers right now the actual filtering process, which has been tested in other tests.
This works OK as my searchBar delegate code was written as follows:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
self.filterText = searchText;
[self.tableView reloadData];
}
The problem is that filtering this data is becoming a heavy process that right now is being done on the main thread, so during that time the UI is blocked.
Therefore, I thought of doing something like this:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSArray *filteredData = [self filteredDataWithText:searchText];
dispatch_async(dispatch_get_main_queue(), ^{
self.filteredData = filteredData;
[self.tableView reloadData];
});
});
}
So that the filtering process occurs in a different thread and when it has finished, the table is asked to reload its data.
The question is... how do I test these things inside dispatch_async calls?
Is there any elegant way of doing that other than time-based solutions? (like waiting for some time and expect that those tasks have finished, not very deterministic)
Or maybe I should put my code on a different way to make it more testable?
In case you need to know, I'm using OCMockito and OCHamcrest by Jon Reid.
Thanks in advance!!
There are two basic approaches. Either
Make things synchronous only while testing. Or,
Keep things asynchronous, but write an acceptance test that does resynchronizing.
To make things synchronous for testing only, extract the code that actually does work into their own methods. You already have -filteredDataWithText:. Here's another extraction:
- (void)updateTableWithFilteredData:(NSArray *)filteredData
{
self.filteredData = filteredData;
[self.tableView reloadData];
}
The real method that takes care of all the threading now looks like this:
- (void)searchBar:(UISearchBar *)searchBar textDidChange:(NSString *)searchText
{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSArray *filteredData = [self filteredDataWithText:searchText];
dispatch_async(dispatch_get_main_queue(), ^{
[self updateTableWithFilteredData:filteredData];
});
});
}
Notice that underneath all that threading fanciness, it really just calls two methods. So now to pretend that all that threading was done, have your tests just invoke those two methods in order:
NSArray *filteredData = [self filteredDataWithText:searchText];
[self updateTableWithFilteredData:filteredData];
This does mean that -searchBar:textDidChange: won't be covered by unit tests. A single manual test can confirm that it's dispatching the right things.
If you really want an automated test on the delegate method, write an acceptance test that has its own run loop. See Pattern for unit testing async queue that calls main queue on completion. (But keep acceptance tests in a separate test target. They're too slow to include with unit tests.)
Albite Jons options are very good options most of the time, sometime it creates less cluttered code when doing the following. For example if your API has a lot small methods that are synchronised using a dispatch queue.
Have a function like this (it could be a method of your class as well).
void dispatch(dispatch_queue_t queue, void (^block)())
{
if(queue)
{
dispatch_async(queue, block);
}
else
{
block();
}
}
Then use this function to call the blocks in your API methods
- (void)anAPIMethod
{
dispatch(dispQueue, ^
{
// dispatched code here
});
}
You would usually initialise the queue in your init method.
#implementation MyAPI
{
dispatch_queue_t dispQueue;
}
- (instancetype)init
{
self = [super init];
if (self)
{
dispQueue = dispatch_queue_create("myQueue", DISPATCH_QUEUE_SERIAL);
}
return self;
}
Then have a private method like this, to set this queue to nil. It is not part of your interface, the API consumer will never see this.
- (void) disableGCD
{
dispQueue = nil;
}
In your test target you create a category to expose the GCD disabling method:
#interface TTBLocationBasedTrackStore (Testing)
- (void) disableGCD;
#end
You call this in your test setup and your blocks will be called directly.
The advantage in my eyes is debugging. When a test case involves a runloop so that blocks are actually called, the problem is that there has to be a timeout involved. This timeout is usually quite short because you don't want to have tests that last long if the they run into the timeout. But having a short timeout means your test runs into the timeout when debugging.
In the MagicalRecord github docs it states:
MagicalRecord provides a background saving queue so that saving all
data is performed off the main thread, in the background. This means
that it may be necessary to use MR_saveNestedContexts rather than the
typical MR_save method in order to persist your changes all the way to
your persistent store.
looking at the source, I can't figure out the difference between these two methods minus the dispatch_async command. I see that they both save all nested contexts up to the root, therefore persisting to the store. But why and in what situation would I use one over the other?
Also, as far as just saving a nested context up one level (without persisting to store) I'm assuming I would still use NSManagedObjectContext's - (BOOL)save:(NSError **)error method?
Below is the source code of the two methods.
- (void) MR_save {
[self MR_saveErrorHandler:nil];
}
- (void) MR_saveErrorHandler:(void (^)(NSError *))errorCallback {
[self performBlockAndWait:^{
[self MR_saveWithErrorCallback:errorCallback];
if (self.parentContext) {
[[self parentContext] performBlockAndWait:^{
[[self parentContext] MR_saveErrorHandler:errorCallback];
}];
}
}];
}
- (void) MR_saveNestedContexts {
[self MR_saveNestedContextsErrorHandler:nil];
}
- (void) MR_saveNestedContextsErrorHandler:(void (^)(NSError *))errorCallback {
[self MR_saveNestedContextsErrorHandler:nil completion:nil];
}
- (void) MR_saveNestedContextsErrorHandler:(void (^)(NSError *))errorCallback completion:(void (^)(void))completion {
[self performBlock:^{
[self MR_saveWithErrorCallback:errorCallback];
if (self.parentContext) {
[[self parentContext] performBlock:^{
[[self parentContext] MR_saveNestedContextsErrorHandler:errorCallback completion:completion];
}];
} else {
if (completion) {
dispatch_async(dispatch_get_main_queue(), ^{
completion();
});
}
}
}];
}
With the new Parent/Child feature in CoreData from iOS5 and Lion on up, it is sometimes necessary to "force" a save all the way to disk. That is, when you have, for example, 3 contexts, such as:
root <- child1 <- child2
If you save in child2, those changes are only notified up a single level to child1. Root won't have them. In order to do this, you must call save one more time. The fact that you can have an arbitrarily long list of these contexts means you won't know if your save ever gets to the root context, which also is responsible for persisting changes to the store (disk). saveNestedContexts uses recursion to traverse this tree and make sure your save actually goes to disk, when you intended it to do so.
The similarity between 'MR_save' and 'MR_saveNestedContexts' was changed in version 2.0.8 (I was using 2.0.7).
Here is the commit on github: https://github.com/magicalpanda/MagicalRecord/commit/f7c4350e9daf7d90eec83ba5eafeccfa7af34312
And the discussion: https://github.com/magicalpanda/MagicalRecord/issues/305
So to summarize, in version 2.0.8, 'MR_save' now only saves the current context, and 'MR_saveNestedContexts' recursively saves up to the topmost context.
I am using the AWS SDK for iOS to upload and download files to and from local hard drive to Amazon S3 storage. I am capable of making this work but I am unable to get the S3 delegate to respond properly to alert me when operations have finished or resulted in an error.
I have an array of files that I want to upload. For each file I create a NSOperation where the main routine consist mostly of:
AmazonCredentials * credentials = [[AmazonCredentials alloc] initWithAccessKey:ACCESS_KEY_ID withSecretKey:SECRET_KEY];
putObjectRequest = [[S3PutObjectRequest alloc] initWithKey:pathCopy inBucket:[self bucket]];
putObjectRequest.filename = pathSource;
putObjectRequest.credentials=credentials;
[putObjectRequest setDelegate:s3Delegate];
Here, the delegate (s3Delegate) is created as a regular AmazonServiceRequestDelegate which should be able to fire off responses when an operation has finished. Each of my NSOperations are added to my NSOperationQueue which executes operations non-concurrently. If I use the delegate [putObjectRequest setDelegate:s3Delegate] the operations are not working. If I remove the use of the delegate the operations are performed correctly but I am unable to receive any responses to the operations as I do not have a delegate.
If I remove the use of the NSOperationQueue completely and use the [putObjectRequest setDelegate:s3Delegate] the delegate works perfectly.
My question is what am I doing wrong with using a delegate in a queue? Since the delegate is perfectly capable of performing while not in a queue could this be related to not performing on the main thread? I really want to be able to use the queue to limit the number of non-concurrent operations, however I am unable to figure this out. I hope someone has an idea of what is going on here and any example code would be greatly appreciated. Thanks!
Cheers, Trond
It seems that the aws sdk behaves asynchronously after the time you set your delegate.
So in order to have your asynchronous aws stuff work in a (asynchronous) NSOperation, you got to put some magic to wait for AWS to complete:
In your .h NSOperation file, add a boolean:
#interface UploadOperation : NSOperation <AmazonServiceRequestDelegate> {
#private
BOOL _doneUploadingToS3;
}
and in your .m file, your main method will look like this:
- (void) main
{
.... do your stuff …..
_doneUploadingToS3 = NO;
S3PutObjectRequest *por = nil;
AmazonS3Client *s3Client = [[AmazonS3Client alloc] initWithAccessKey:ACCESS_KEY withSecretKey:SECRET_KEY];
s3Client.endpoint = endpoint;
#try {
por = [[[S3PutObjectRequest alloc] initWithKey:KEY inBucket:BUCKET] autorelease];
por.delegate = self;
por.contentType = #"image/jpeg";
por.data = _imageData;
[s3Client putObject:por];
}
#catch (AmazonClientException *exception) {
_doneUploadingToS3 = YES;
}
do {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
} while (!_doneUploadingToS3);
por.delegate = nil;
.... continue with your stuff ….
}
do not forget to implement your delegate methods
-(void)request:(AmazonServiceRequest *)request didCompleteWithResponse:(AmazonServiceResponse *)response
{
_doneUploadingToS3 = YES;
}
-(void)request:(AmazonServiceRequest *)request didFailWithError:(NSError *)error
{
_doneUploadingToS3 = YES;
}
-(void)request:(AmazonServiceRequest *)request didFailWithServiceException:(NSException *)exception
{
_doneUploadingToS3 = YES;
}
- (void) request:(AmazonServiceRequest *)request didSendData:(NSInteger)bytesWritten totalBytesWritten:(NSInteger)totalBytesWritten totalBytesExpectedToWrite:(NSInteger)totalBytesExpectedToWrite
{
// Do what you want
}
-(void)request:(AmazonServiceRequest *)request didReceiveResponse:(NSURLResponse *)response
{
// Do what you want
}
-(void)request:(AmazonServiceRequest *)request didReceiveData:(NSData *)data
{
// Do what you want
}
Note: this magic can work for any stuff that performs asynchronously but have to be implemented in a NSOperation.
I've been trying to get a PDF from an NSURL that is changed during a
- (void)tableView:(UITableView *)tableView didSelectRowAtIndexPath:(NSIndexPath *)indexPath
The change in NSURL logs perfectly, but the view is loaded before the app has a chance to act upon that change. Is there a way to delay the reading of the change in URL by simply moving the code to the
viewDidLoad
section, or do I have to drastically change everything? Here's my -(id)init method:
- (id)init {
if (self = [super init]) {
CFURLRef pdfURL = (CFURLRef)[NSURL fileURLWithPath:[[NSBundle mainBundle] pathForResource:appDelegate.baseURL ofType:#"pdf"]];
pdf = CGPDFDocumentCreateWithURL((CFURLRef)pdfURL);
}
return self;
}
When you need to work with network the proven approach is to use asynchronous calls. This is because of the nature of a network connection; it is unpredictable, not always reliable, the time you need to spend to get the result from the server can vary from millisecond to minutes.
I would make a data model class, MyPDFModel, with an asynchronous method, that should run a thread to get the file from the server:
- (void)requestPDFWithURL:(NSURL*)fileURL
{
[NSThread detachNewThreadSelector:#selector(requestPDFWithURLThreaded:) toTarget:self fileURL];
}
- (void)requestPDFWithURLThreaded:(NSURL*)fileURL
{
NSAutoreleasePool* pool = [NSAutoreleasePool new];
// do whatever you need to get either the file or an error
if (isTheFileValid)
[_delegate performSelectorOnMainThread:#selector(requestDidGetPDF:) withObject:PDFFile waitUntilDone:NO];
else
[_delegate performSelectorOnMainThread:#selector(requestDidFailWithError:) withObject:error waitUntilDone:NO];
[pool release];
}
Meanwhile the UI should display an activity indicator.
The MyPDFModelDelegate protocol should have two methods:
- (void)requestDidGetPDF:(YourPDFWrapperClass*)PDFDocument;
- (void)requestDidFailWithError:(NSError*)error;
YourPDFWrapperClass is used to return an autoreleased document.
The delegate can let the UI know that the data has been updated, for example by posting a notification if the delegate is a part of the data model.
This is just an example, the implementation can be different depending on your needs, but I think you will get the idea.
P.S. Delaying an init is a very bad idea.
This is with reference to the StackOverflow question Managing multiple asynchronous NSURLConnection connections
I have multiple asynchronous HTTP requests being made at the same time. All these use the same NSURLConnection delegate functions. (The receivedData object is different for each connection as specified in the other question above. In the delegate, I parse the receivedDate object, and do additional operations on those parsed strings)
Everything works fine for me so far, but I'm not sure if I need to do anything to ensure correct “multithreaded” behavior.
Is it possible that more than two connections will use the delegate at the same time? (I would think yes)
If yes, how is it resolved? (Does Cocoa do this automatically?)
Do I need to have additional checks in place to ensure that each request is handled “correctly”?
I enhanced the Three20 library to implement asynchronous connections across multiple threads in order to fetch data even if the user was playing with the UI. After many hours of chasing down random memory leaks that were detected within the CFNetwork framework I finally root caused the issue. I was occasionally losing track of responses and data.
Any data structures which are accessed by multiple threads must be protected by an appropriate lock. If you are not using locks to access shared data structures in a mutually exclusive manner then you are not thread safe. See the "Using Locks" section of Apple's Threading Programming Guide.
The best solution is to subclass NSURLConnection and add instance variables to store its associated response and response data. In each connection delegate method you then cast the NSURLConnection to your subclass and access those instance variables. This is guaranteed to be mutually exclusive because every connection will be bundled with its own response and data. I highly recommend trying this since it is the cleanest solution. Here's the code from my implementation:
#interface TTURLConnection : NSURLConnection {
NSHTTPURLResponse* _response;
NSMutableData* _responseData;
}
#property(nonatomic,retain) NSHTTPURLResponse* response;
#property(nonatomic,retain) NSMutableData* responseData;
#end
#implementation TTURLConnection
#synthesize response = _response, responseData = _responseData;
- (id)initWithRequest:(NSURLRequest *)request delegate:(id)delegate {
NSAssert(self != nil, #"self is nil!");
// Initialize the ivars before initializing with the request
// because the connection is asynchronous and may start
// calling the delegates before we even return from this
// function.
self.response = nil;
self.responseData = nil;
self = [super initWithRequest:request delegate:delegate];
return self;
}
- (void)dealloc {
[self.response release];
[self.responseData release];
[super dealloc];
}
#end
/////////////////////////////////////////////////////////////////
////// NSURLConnectionDelegate
- (void)connection:(NSURLConnection*)connection
didReceiveResponse:(NSHTTPURLResponse*)response {
TTURLConnection* ttConnection = (TTURLConnection*)connection;
ttConnection.response = response;
ttConnection.responseData = [NSMutableData
dataWithCapacity:contentLength];
}
- (void)connection:(NSURLConnection*)connection
didReceiveData:(NSData*)data {
TTURLConnection* ttConnection = (TTURLConnection*)connection;
[ttConnection.responseData appendData:data];
}
- (void)connectionDidFinishLoading:(NSURLConnection *)connection {
TTURLConnection* ttConnection = (TTURLConnection*)connection;
if (ttConnection.response.statusCode == 200) {
// Connection success
}
}
- (void)connection:(NSURLConnection *)connection
didFailWithError:(NSError *)error {
TTURLConnection* ttConnection = (TTURLConnection*)connection;
// Handle the error
}
Assuming you're launching all of the (asynchronous) connections on a single thread, then the delegate messages will all get posted in that thread's run loop. Therefore the delegate only needs to be able to deal with one message being handled at once; the run loop will hand one message off at a time. This means that while the order of the delegate messages is unknown and the next message could come from any connection object, there will be no concurrent execution of your delegate methods.
However, were you actually trying to use the same delegate object across multiple threads, rather than just using the asynchronous nature of the API, then you would need to deal with concurrent delegate methods.
Yes it's possible to have multiple connections. the notification object contains a pointer to the NSURLConnection that triggered the notification.
Internally I guess NSURLConnection listens to a socket and does something like this when it has data ready.
[your_delegate
performSelectorOnMainThread:#selector(connectionCallback:)
withObject:self
waitUntilDone:NO];
so you don't have to worry about it being multithreaded, NSURLConnection will take care of this. For simplicity I have written self, in the real world a NSNotification object is given.
You shouldn't have to do any checks related to multithreading.