Handling Continuous AFNetworking API Calls - ios

This problem, when i swipe screen API Calls are to be made, separate calls for separate swipe i.e. Left, Right, Top, Bottom. Also there has to be Stop API which has to be sent in 1 minute delay of swipe.
Example Camera moving on swipe for 1 second and stops:
Swipe Left -> Left() GET API is called with AFNetworking -> (So Camera starts moving to left) -> Soon after Left API is triggered -> 1 second delay -> Stop GET API is called with AFNetworking. Repeats same method for Right(), Top(), Bottom()
I am not able to achieve this smoothly, I use this logic.
- (void)didSwipe:(UISwipeGestureRecognizer*)swipe{
if (swipe.direction == UISwipeGestureRecognizerDirectionLeft) {
[self performSelector:#selector(moveRight) withObject:self afterDelay:0.0];
[self performSelector:#selector(stop:) withObject:self afterDelay:1.0];
} else if (swipe.direction == UISwipeGestureRecognizerDirectionRight) {
[self performSelector:#selector(moveLeft) withObject:self afterDelay:0.0];
[self performSelector:#selector(stop:) withObject:self afterDelay:1.0];
} else if (swipe.direction == UISwipeGestureRecognizerDirectionUp) {
[self performSelector:#selector(moveDown) withObject:self afterDelay:0.0];
[self performSelector:#selector(stop:) withObject:self afterDelay:1.0];
} else if (swipe.direction == UISwipeGestureRecognizerDirectionDown) {
[self performSelector:#selector(moveUp) withObject:self afterDelay:0.0];
[self performSelector:#selector(stop:) withObject:self afterDelay:1.0];
}
}
This are Method calls
-(void) moveRight
{
NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
[parameters setValue:#"MoveRight" forKey:#"direction"];
[self changeDirection:parameters];
}
-(void) moveLeft
{
NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
[parameters setValue:#"MoveLeft" forKey:#"direction"];
[self changeDirection:parameters];
}
-(void) moveUp
{
NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
[parameters setValue:#"MoveUp" forKey:#"direction"];
[self changeDirection:parameters];
}
-(void) moveDown
{
NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
[parameters setValue:#"MoveDown" forKey:#"direction"];
[self changeDirection:parameters];
}
-(void) stop
{
NSMutableDictionary *parameters = [[NSMutableDictionary alloc] init];
[parameters setValue:#"Stop" forKey:#"direction"];
[self changeDirection:parameters];
}
Now ChangeDirection Method
-(void) changeDirection:(NSDictionary *) parameters
{
dispatch_queue_t myQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0);
dispatch_async(myQueue, ^{
[RestHandler afnetworkingGetApiCall():parameters :^(id response, NSString *respMsg)
{
}];
dispatch_async(dispatch_get_main_queue(), ^{
});
});
}
Hope I am able to explain my question. Let me know if its not clear
Cheers.

i think, you have to create model for each response. then for each call you have to create common operation queue.

Related

Async call causing EXC_BAD_INSTRUCTION crash

The app I'm working on is using a function that is working fine but blocks the main thread. I am attempting to add a loading spinner using SVProgressHUD and that requires I call my function asynchronously in order to display the spinner. As soon as I call the function asynchronously however the app crashes with EXC_BAD_INSTRUCTION (code=EXC_I386_INVOP, subcode=0x0 The only change I have made to the function is to invoke the popViewControllerAnimated lines on the main thread. Why is running this code on a new thread causing it to crash and how can I fix it?
Calling code:
-(void) _doSaveDataPoint {
[SVProgressHUD show];
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
[self _saveDataPoint];
dispatch_async(dispatch_get_main_queue(), ^{
[SVProgressHUD dismiss];
});
});
}
_saveDataPoint function. popViewController called on main thread near the end of this code:
-(void) _saveDataPoint {
NSString *errorMsg = nil;
if ([[myLegend type] isEqualToString:#"PIN"]) {
if ([myNodes count]==0) {
errorMsg = #"Please make sure you have added one point on to the map to continue.";
}
}
else if ([[myLegend type] isEqualToString:#"POLYGON"]) {
if ([myNodes count]<3) {
errorMsg = #"Please make sure you have at least 3 points set before continuing.";
}
}
else {
if ([myNodes count]<2) {
errorMsg = #"Please make sure you have at least 2 points set before continuing.";
}
}
if (errorMsg !=nil) {
UIAlertController *alertController = [UIAlertController
alertControllerWithTitle:#"Not enough points"
message:errorMsg
preferredStyle:UIAlertControllerStyleAlert];
UIAlertAction *okAction = [UIAlertAction
actionWithTitle:#"OK"
style:UIAlertActionStyleDefault
handler:^(UIAlertAction *action) {
// Just dismiss
}];
[alertController addAction:okAction];
dispatch_async(dispatch_get_main_queue(), ^{
[self presentViewController:alertController animated:YES completion:nil];
});
return;
}
ClientLegendDataPointBounds *bounds = [[ClientLegendDataPointBounds alloc] init];
int count = 0;
GeoPoint *first = nil;
NSMutableDictionary *attr = [[NSMutableDictionary alloc] init];
for (_EditAnnotation *anno in myNodes) {
GeoPoint *point = [[GeoPoint alloc] initWithLatitude:[anno coordinate].latitude andLongitude:[anno coordinate].longitude];
[bounds expand:point];
if (count==0) {
first = point;
count++;
continue;
}
NSString *xKey = [NSString stringWithFormat:#"x%d",count-1];
NSNumber *xCoord = [NSNumber numberWithDouble:[point latitude ]];
NSString *yKey = [NSString stringWithFormat:#"y%d",count-1];
NSNumber *yCoord = [NSNumber numberWithDouble:[point longitude]];
[attr setObject:xCoord forKey:xKey];
[attr setObject:yCoord forKey:yKey];
count++;
}
if (count>0) {
NSString *pointCount = [NSString stringWithFormat:#"%d", count-1];
[attr setObject:pointCount forKey:#"pointCount"];
}
[self _setBarThemeDefault];
if (myDataPoint==nil) {
myDataPoint = [myLegend addDataPoint:[NSNumber numberWithLongLong:[DateTime currentTimeInMillis]] title:#"" description:#"" latitude:[first latitude] longitude:[first longitude] attributes:attr type:[myLegend type] bounds:bounds];
dispatch_async(dispatch_get_main_queue(), ^{
[[self navigationController] popViewControllerAnimated:NO];
});
[myHandler newItemCreated:myDataPoint];
} else {
[myDataPoint setAttributes:attr];
[myDataPoint setBounds:bounds];
[myDataPoint setLatitude:[first latitude]];
[myDataPoint setLongitude:[first longitude]];
[myDataPoint setModified:[NSNumber numberWithLongLong:[DateTime currentTimeInMillis]]];
[myDataPoint update];
dispatch_async(dispatch_get_main_queue(), ^{
[[self navigationController] popViewControllerAnimated:YES];
});
[myHandler itemUpdated:myDataPoint];
}
[self _finishSurveyLog:[SurveyLogItem ACT_SAVE_SPATIAL_CONST]];
[self _saveUserLocation];
}
I don't know exactly the plugin but could it be that the plugin itselfs dispatches the ui stuff to the main queue? So you don't have to dispatch the call to the main queue by yourself. Take a look at the source code:
SVProgressHUD.m

Waiting for Network Connection To Complete before proceeding with Func?

I have a resume function that reestablishes a network connection before it gathers updated parameters for the app.
Issue is that it seems to be trying to get some parameters before the network link has been reestablished.
Is there a way i can pause until the connection is made?
The getParameters func is on a background thread using:
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^(void) { //code }
The initConnection is primary thread
Here is the func:
- (void)screenUnlocked {
[self initConnection];
CDCChannelCollectionView *scrollView;
if ([self channelView]) {
scrollView = [[self channelView] scrollView];
for (CDCChannelStrip* strip in [scrollView visibleCells]) {
[self getParameters:[NSNumber numberWithInteger:strip.channelNumber]];
}
}
}
edit:
Something like this?
- (void)initConnection: completion:(void (^)(void))completionBlock {
if (![self isDemo]) {
[self setTcpControl:[[CDCControl alloc] initWithAddress:[self IPAddress] usingProtocol:TCP]];
[[self tcpControl] setDelegate:self];
[self setUdpControl:[[CDCControl alloc] initWithAddress:[self IPAddress] usingProtocol:UDP]];
[[self udpControl] setDelegate:self];
if (successful) {
completionBlock();
}
}
}
For block syntax you can follow:
//Block funtion with void block return type.
- (void)initConnection:(void(^)(void))completionBlock {
if (![self isDemo]) {
[self setTcpControl:[[CDCControl alloc] initWithAddress:[self IPAddress] usingProtocol:TCP]];
[[self tcpControl] setDelegate:self];
[self setUdpControl:[[CDCControl alloc] initWithAddress:[self IPAddress] usingProtocol:UDP]];
[[self udpControl] setDelegate:self];
if (successful) {
//call completion when connection is made.
completionBlock();
}
}
}
calling function as:
//Calling initConnection funtion.
[self initConnection:^{
NSLog(#"complition success");
}];

iOS writeToURL delay or screen freeze, how to resolve the thread in the case

I have a complex operation, then when the operate complete,
It will save to NSData write to the file to preview.
I encounter a problem, when I click the button ,
the button action will start show MBProgress thing and async to complex operate in background.
When the file write success, It will go to prepareForSegue method pass value to destinationViewController.
I try to add thread, But I found my screen always freeze or can't write file success for stay this screen show the alert(I think it is operate is not complete, so get the BOOL is NO).
How to write the resolve in this case for show the MBProgress wait the operation complete , then navigation to next viewcontroller?
Thank you very much.
My Code below:
- (IBAction)fileBtnAction:(UIButton *)sender{
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self doComplexOperateAction];
dispatch_async(dispatch_get_main_queue(), ^{
fileHud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
fileHud.label.text = #"file operating....";
fileHud.userInteractionEnabled = NO;
});
});
}
- (void) doComplexOperateAction{
....... do something.......
NSError *error;
writeFileSuccess = [resultFilie.fileContent writeToURL:previewFileUrl
options:NSDataWritingFileProtectionComplete
error:&error];
}
-(BOOL) shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender{
if( [identifier isEqualToString:#"fileSuccessSegue"] ){
if( writeFileSuccess == YES ){
fileHud.hidden = YES;
fileHud = nil;
return YES;
}else{
dispatch_async(dispatch_get_main_queue(), ^{
msgAlertController.message = #"can't write file success";
[self presentViewController:msgAlertController animated:YES completion:nil];
fileHud.hidden = YES;
fileHud = nil;
});
return NO;
}
}
return NO;
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if( [[segue identifier] isEqualToString:#"fileSuccessSegue"] ){
.........
NSURL *fileURL = [NSURL fileURLWithPath:fileTmpPathString];
fileSuccessViewController *pfVC = [segue destinationViewController];
pfVC.filePathURL = fileURL;
}
}
I think It will be something like this:
#property (noatomic) BOOL uploading;
- (IBAction)fileBtnAction:(UIButton *)sender{
fileHud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
fileHud.label.text = #"file operating....";
fileHud.userInteractionEnabled = NO;
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_BACKGROUND, 0), ^{
[self doComplexOperateAction];
dispatch_sync(dispatch_get_main_queue(), ^{
[self performSegueWithIdentifier:#"fileSuccessSegue" sender:nil];
});
});
}
- (void) doComplexOperateAction{
self.uploading = YES;
....... do something.......
NSError *error;
writeFileSuccess = [resultFilie.fileContent writeToURL:previewFileUrl
options:NSDataWritingFileProtectionComplete
error:&error];
self.uploading = NO;
}
-(BOOL) shouldPerformSegueWithIdentifier:(NSString *)identifier sender:(id)sender{
if( [identifier isEqualToString:#"fileSuccessSegue"] ){
if( writeFileSuccess == YES ){
fileHud.hidden = YES;
fileHud = nil;
return YES;
}else{
msgAlertController.message = #"can't write file success";
[self presentViewController:msgAlertController animated:YES completion:nil];
fileHud.hidden = YES;
fileHud = nil;
});
return NO;
}
}
return !self.uploading;
}
-(void) prepareForSegue:(UIStoryboardSegue *)segue sender:(id)sender{
if( [[segue identifier] isEqualToString:#"fileSuccessSegue"] ){
.........
NSURL *fileURL = [NSURL fileURLWithPath:fileTmpPathString];
fileSuccessViewController *pfVC = [segue destinationViewController];
pfVC.filePathURL = fileURL;
}
}
I would advise you to rewrite this code. I do not like how you handle errors. Because writeFileSuccess have unsuspected value while you uploading content.
I couldn't understand if the file was written or no. Anyway, keep in mind that if you try to dispatch to main thread you should always first check if you are already on the main thread, because it can hang the application.
Example:
if([NSThread isMainThread]){
//perform gui operation
} else {
dispatch_async(dispatch_get_main_queue(), ^{
//perfrom gui operation
});
}
Edit: You should consider using dispatch_sync instead of dispatch_async for the main thread, since you have nested dispatches in - (IBAction)fileBtnAction:(UIButton *)sender:
[self doComplexOperateAction];
dispatch_**sync**(dispatch_get_main_queue(), ^{
fileHud = [MBProgressHUD showHUDAddedTo:self.view animated:YES];
fileHud.label.text = #"file operating....";
fileHud.userInteractionEnabled = NO;
});
});

"[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]" makes the UI non response

In the UIViewController viewDidAppear event, I want to get some data from web service. And the code like:
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
NSArray *arr = [self getCarList];
}
- (NSArray *)getCarList
{
if (!carList) {
ARequset *req = [[ARequset alloc] init];
[NetService sendRequest:req respClass:[Resp class] success:^(BaseResponse *response)
{
//after finished
self.requestFinished = YES;
} fail:^(NSInteger errcode, NSString *errmsg) {
self.requestFinished = YES;
}];
while (!self.requestFinished) {
[[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]];
}
}
return carList;
}
when run in to [[NSRunLoop currentRunLoop] runMode:NSDefaultRunLoopMode beforeDate:[NSDate distantFuture]]; the request success block will not be performed, and the UI become no response.
but if i change the - (void)viewDidAppear:(BOOL)animated like this, all goes well.
- (void)viewDidAppear:(BOOL)animated
{
[super viewDidAppear:animated];
[self performSelector:#selector(getCarList) withObject:self afterDelay:1];
}
Don't do that kind of syntax
The NetService request works asynchronously, the result of the request is passed
in the block. You have to process the BaseResponse object and update your UI or whatever you want to do with the data.
Polling is bad habit and consumes system resources unnecessarily.
Apart from that you're telling the currently running runloop of UIApplication to run which blocks it.
Do something like this
- (void)getCarList
{
if (!carList) {
ARequset *req = [[ARequset alloc] init];
[NetService sendRequest:req respClass:[Resp class] success:^(BaseResponse *response)
{
//after finished
self.carList = [self doSomethingWithTheResponse:response];
dispatch_async(dispatch_get_main_queue(), ^(void) {
// Update UI
});
} fail:^(NSInteger errcode, NSString *errmsg) {
dispatch_async(dispatch_get_main_queue(), ^(void) {
// show alert
});
}];
}
}
}
Edit: Alternatively use a delegate like pattern to handle the asynchronous behavior.
Instead of the synchronous method
- (void)methodToGetCarList
{
NSArray *cars = [self getCarList];
[self doSomethingWithCars:cars];
}
use this
- (void)methodToGetCarListAsynchronously
{
[self getCarList];
}
and the delegate method
- (void)didReceiveCars:(NSArray *)cars errorMessage:(NSString *)error
{
if (error) {
// do error handling
} else {
[self doSomethingWithCars:cars];
}
}
the getCarList method looks like
- (void)getCarList
{
if (!carList) {
ARequset *req = [[ARequset alloc] init];
[NetService sendRequest:req respClass:[Resp class] success:^(BaseResponse *response)
{
//after finished
self.carList = [self doSomethingWithTheResponse:response];
[self didReceiveCars:self.carList errorMessage:nil];
} fail:^(NSInteger errcode, NSString *errmsg) {
[self didReceiveCars:nil errorMessage:errmsg];
}];
}
} else {
[self didReceiveCars:self.carList errorMessage:nil];
}
}
The code does not consider potential issues if the response returns in a background thread.

iOS - Objective-C - How to stop NSThread, when it's waiting?

I have an nsthread, a while loop within this one. It's getting objects from a "thread-safe" queue in the main method. When I leave the UIViewController, which contains this nsthread object, I call the nsthread cancel method, but it doesn't stop, because it's locked by the "queueLock" NSCondition. When I go back to this UIViewController a new nsthread will be created and gets the objects form the queue, but the previous thread still exits and both of them try to use the same object from the queue, and this causes a memory management problem. My question is how should I stop this thread, when I leave the UIViewController.
NSThread main method:
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
while ([self isCancelled] == NO) {
RenderRequest *renderRequest = [queue get];
[self doRender:renderRequest];
[renderRequest release];
}
[pool drain];
This is the get method of the queue class:
- (id) get {
id toRet = nil;
[queueLock lock];
#try {
while ([queueContents count] == 0) {
[queueLock wait];
}
toRet = [queueContents lastObject];
[queueContents removeLastObject];
}
#finally {
[queueLock unlock];
return toRet;
}
}
Thanks!
I wrote a simple demo, hope this can help you :)
demo.h
#import <Foundation/Foundation.h>
#interface test : NSObject
{
NSCondition *queueCondition;
NSThread *queueThread;
NSMutableArray *queueTask;
NSTimer *timer;
}
- (id)init;
#end
demo.m
#import "demo.h"
#interface demo (PrivateMethods)
- (void)threadTest;
- (void)cancelThread;
- (void)addTask;
#end
#implementation demo
- (id)init
{
self = [super init];
if (self) {
if (!queueThread) {
if (!queueCondition) {
queueCondition = [[NSCondition alloc] init];
}
if (!queueTask) {
queueTask = [[NSMutableArray alloc] initWithCapacity:5];
}
queueThread = [[NSThread alloc] initWithTarget:self selector:#selector(threadTest) object:nil];
[queueThread start];
[self performSelector:#selector(cancelThread) withObject:nil afterDelay:10];
if (!timer) {
timer = [[NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(addTask) userInfo:nil repeats:YES] retain];
}
}
}
return self;
}
- (void)dealloc
{
[queueThread release];
[queueCondition release];
[queueTask release];
[timer invalidate];
[timer release];
[super dealloc];
}
- (void)threadTest
{
NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];
while (![[NSThread currentThread] isCancelled]) {
[queueCondition lock];
[queueCondition wait];
if ([queueTask count] == 0) {
[queueCondition unlock];
continue;
}
NSString *str = nil;
while ((str = [queueTask lastObject])) {
NSLog(#"getTask: %#", [queueTask lastObject]);
[queueTask removeLastObject];
}
[queueCondition unlock];
}
NSLog(#"threadTest end");
[pool drain];
}
- (void)addTask
{
[queueCondition lock];
if (!queueTask) {
queueTask = [[NSMutableArray alloc] initWithCapacity:5];
}
[queueTask addObject:#"new task"];
[queueCondition signal];
NSLog(#"add: new task");
[queueCondition unlock];
}
- (void)cancelThread
{
[timer invalidate];
[queueThread cancel];
[queueCondition lock];
[queueCondition signal];
[queueCondition unlock];
}
#end
- (id) get
{
id toRet = nil;
[queueLock lock];
#try
{
while ([queueContents count] == 0)
{
[queueLock wait];
if ([self isCancelled]) return nil; // stop waiting
}
toRet = [queueContents lastObject];
[queueContents removeLastObject];
}
#finally
{
[queueLock unlock];
return toRet;
}
}
thread main
NSAutoreleasePool* pool = [[NSAutoreleasePool alloc] init];
while ([self isCancelled] == NO)
{
RenderRequest *renderRequest = [queue get];
if (renderRequest == nil) break; // should stop
[self doRender:renderRequest];
[renderRequest release];
}
[pool drain];
then you can cancel the thread and notify queueLock to stop waiting

Resources