0

I'm using the UNIRest library and I'm trying to switch to another ViewController on the response of an HTTP GET method.

[[UNIRest get:^(UNISimpleRequest *request) {
        [request setUrl:GEtFamilyMembers];
    }] asJsonAsync:^(UNIHTTPJsonResponse* response, NSError *error) {
        NSLog(@"res: %@", response.body);
        if (response.code == 200) {
            GCHFamilyMembersViewConroller* fmViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"gchFamilyMembers"];

            self.navigationController.viewControllers = @[fmViewController];
            [self.mm_drawerController setCenterViewController:self.navigationController withCloseAnimation:YES completion:nil];
        } else {
            [self showToast:@"Something went wrong, Please try again after some time."];
        }
    }];

Even after printing the response using NSLog. Nothing happens for about 30 seconds. Then suddenly app crashes.

I thought it's because Asynch call blocks UI so I tired a method like:

- (void) gotoSelectFamilyMembers {
    dispatch_async(dispatch_get_main_queue(), ^{
        GCHFamilyMembersViewConroller* fmViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"gchFamilyMembers"];
        self.navigationController.viewControllers = @[fmViewController];
        [self.mm_drawerController setCenterViewController:self.navigationController withCloseAnimation:YES completion:nil];
    });
}

but still the app crashes.

Earlier when I was using a Synchronous call it was working. But not now. Please help!

EDIT:

2016-05-02 17:58:22.695 Checkme Mobile[6208:136321] *** Assertion failure in -[UIKeyboardTaskQueue waitUntilAllTasksAreFinished], /SourceCache/UIKit_Sim/UIKit-3347.44.2/Keyboard/UIKeyboardTaskQueue.m:374
2016-05-02 17:58:22.728 Checkme Mobile[6208:136224] NSScanner: nil string argument
2016-05-02 17:58:22.728 Checkme Mobile[6208:136224] NSScanner: nil string argument
2016-05-02 17:58:23.730 Checkme Mobile[6208:136224] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[UIKeyboardTaskQueue waitUntilAllTasksAreFinished] may only be called from the main thread.'
*** First throw call stack:
(
    0   CoreFoundation                      0x00000001094a6c65 __exceptionPreprocess + 165
    1   libobjc.A.dylib                     0x0000000108da2bb7 objc_exception_throw + 45
    2   CoreFoundation                      0x00000001094a6aca +[NSException raise:format:arguments:] + 106
    3   Foundation                          0x00000001089b798f -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 195
    4   UIKit                               0x0000000107d887d6 -[UIKeyboardTaskQueue waitUntilAllTasksAreFinished] + 151
    5   UIKit                               0x0000000107829912 -[UIKeyboardImpl setDelegate:force:] + 473
    6   UIKit                               0x0000000107ad44ad -[UIPeripheralHost(UIKitInternal) _reloadInputViewsForResponder:] + 1002
    7   UIKit                               0x0000000107adc834 -[UIPeripheralHost(UIKitInternal) _preserveInputViewsWithId:animated:reset:] + 504
    8   UIKit                               0x000000010778e181 -[UINavigationController navigationTransitionView:didStartTransition:] + 578
    9   UIKit                               0x00000001079948bc -[UINavigationTransitionView transition:fromView:toView:] + 655
    10  UIKit                               0x0000000107792170 -[UINavigationController _startTransition:fromViewController:toViewController:] + 2984
    11  UIKit                               0x0000000107792408 -[UINavigationController _startDeferredTransitionIfNeeded:] + 523
    12  UIKit                               0x0000000107792ece -[UINavigationController __viewWillLayoutSubviews] + 43
    13  UIKit                               0x00000001078dd6d5 -[UILayoutContainerView layoutSubviews] + 202
    14  UIKit                               0x00000001076b09eb -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 536
    15  QuartzCore                          0x0000000109d83ed2 -[CALayer layoutSublayers] + 146
    16  QuartzCore                          0x0000000109d786e6 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 380
    17  QuartzCore                          0x0000000109d78556 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 24
    18  QuartzCore                          0x0000000109ce486e _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 242
    19  QuartzCore                          0x0000000109ce5a22 _ZN2CA11Transaction6commitEv + 462
    20  QuartzCore                          0x0000000109ce5c99 _ZN2CA11Transaction14release_threadEPv + 199
    21  libsystem_pthread.dylib             0x000000010a35172a _pthread_tsd_cleanup + 86
    22  libsystem_pthread.dylib             0x000000010a351451 _pthread_exit + 117
    23  libsystem_pthread.dylib             0x000000010a3506cd _pthread_wqthread + 879
    24  libsystem_pthread.dylib             0x000000010a34e40d start_wqthread + 13
)
2016-05-02 17:58:23.730 Checkme Mobile[6208:136321] *** Terminating app due to uncaught exception 'NSInternalInconsistencyException', reason: '-[UIKeyboardTaskQueue waitUntilAllTasksAreFinished] may only be called from the main thread.'
*** First throw call stack:
(
    0   CoreFoundation                      0x00000001094a6c65 __exceptionPreprocess + 165
    1   libobjc.A.dylib                     0x0000000108da2bb7 objc_exception_throw + 45
    2   CoreFoundation                      0x00000001094a6aca +[NSException raise:format:arguments:] + 106
    3   Foundation                          0x00000001089b798f -[NSAssertionHandler handleFailureInMethod:object:file:lineNumber:description:] + 195
    4   UIKit                               0x0000000107d887d6 -[UIKeyboardTaskQueue waitUntilAllTasksAreFinished] + 151
    5   UIKit                               0x0000000107829912 -[UIKeyboardImpl setDelegate:force:] + 473
    6   UIKit                               0x0000000107ad44ad -[UIPeripheralHost(UIKitInternal) _reloadInputViewsForResponder:] + 1002
libc++abi.dylib:    7   UIKit                               0x0000000107adc834 -[UIPeripheralHost(UIKitInternal) _preserveInputViewsWithId:animated:reset:] + 504
    8   UIKit                               0x000000010778e181 -[UINavigationController navigationTransitionView:didStartTransition:] + 578
    9   UIKit                               0x00000001079948bc -[UINavigationTransitionView transition:fromView:toView:] + 655
    10  UIKit                               0x0000000107792170 -[UINavigationController _startTransition:fromViewController:toViewController:] + 2984
    11  UIKit                               0x0000000107792408 -[UINavigationController _startDeferredTransitionIfNeeded:] + 523
    12  UIKit                               0x0000000107792ece -[UINavigationController __viewWillLayoutSubviews] + 43
    13  UIKit                               0x00000001078dd6d5 -[UILayoutContainerView layoutSubviews] + 202
    14  UIKit                               0x00000001076b09eb -[UIView(CALayerDelegate) layoutSublayersOfLayer:] + 536
    15  QuartzCore                          0x0000000109d83ed2 -[CALayer layoutSublayers] + 146
    16  QuartzCore                          0x0000000109d786e6 _ZN2CA5Layer16layout_if_neededEPNS_11TransactionE + 380
    17  QuartzCore                          0x0000000109d78556 _ZN2CA5Layer28layout_and_display_if_neededEPNS_11TransactionE + 24
    18  QuartzCore                          0x0000000109ce486e _ZN2CA7Context18commit_transactionEPNS_11TransactionE + 242
    19  QuartzCore                          0x0000000109ce5a22 _ZN2CA11Transaction6commitEv + 462
    20  QuartzCore                          0x0000000109ce5c99 _ZN2CA11Transaction14release_threadEPv + 199
    21  libsystem_pthread.dylib             0x000000010a35172a _pthread_tsd_cleanup + 86
    22  libsystem_pthread.dylib             0x000000010a351451 _pthread_exit + 117
    23  libsystem_pthread.dylib             0x000000010a3506cd _pthread_wqthread + 879
    24  libsystem_pthread.dylib             0x000000010a34e40d start_wqthread + 13
)
terminating with uncaught exception of type NSExceptionlibc++abi.dylib: 
terminating with uncaught exception of type NSException
(lldb) 

EDit:

- (IBAction)loginAction:(id)sender {
    self.email = usernameTextField.text;
    self.password = passwordTextField.text;
    if(!([self.email isEqualToString:@""] && [self.password isEqualToString:@""])) {
        [self performLogin];
    } else{
        UIAlertView *alert = [[UIAlertView alloc] initWithTitle:@"error" message:@"Enter both username and password." delegate:nil cancelButtonTitle:@"Ok" otherButtonTitles:nil, nil];
        [alert show];
    }
}

- (void) performLogin {
    NSDictionary* headers = @{@"Content-Type": @"application/x-www-form-urlencoded"};
    NSDictionary* parameters = @{@"username": self.email, @"password": self.password};
     [[NSThread currentThread] isMainThread] ? NSLog(@"1MAIN THREAD") : NSLog(@"1NOT MAIN THREAD");
    self.loginConnection = [[UNIRest post:^(UNISimpleRequest *request) {
        [request setUrl:LOgin];
        [request setHeaders:headers];
        [request setParameters:parameters];
    }] asJsonAsync:^(UNIHTTPJsonResponse* response, NSError *error) {
        // This is the asyncronous callback block
         [[NSThread currentThread] isMainThread] ? NSLog(@"2MAIN THREAD") : NSLog(@"2NOT MAIN THREAD");
        UNIJsonNode *body = response.body;
        NSData *rawBody = response.rawBody;
        NSString *string = [[NSString alloc] initWithData:rawBody encoding:NSUTF8StringEncoding];
        NSLog(@"response signup: %@", string);
        if (response.code == 200) {
            [self processLoginResponse:body];
        }
    }];

}


- (void) processLoginResponse:(UNIJsonNode *) body {
    if ([body.JSONObject[@"status"] boolValue] == YES) {
        NSString *firstName = body.JSONObject[@"firstName"];
        NSString *lastName = body.JSONObject[@"lastName"];
        NSString *name = [firstName stringByAppendingString:lastName];
        NSMutableDictionary *mDictionary = [[NSMutableDictionary alloc] init];
        [mDictionary setObject:body.JSONObject[@"accountId"] forKey:@"accountId"];
        [mDictionary setObject:name forKey:@"name"];
        [mDictionary setObject:@"Custodian" forKey:@"relation"];
        [self fetchFamilyMembers:mDictionary];
    } else {
        [self showAlert:body.JSONObject[@"message"]];
    }
}

- (void) fetchFamilyMembers:(NSMutableDictionary *) mDictionary {
     [[NSThread currentThread] isMainThread] ? NSLog(@"3MAIN THREAD") : NSLog(@"3NOT MAIN THREAD");
    self.fmConnection = [[UNIRest get:^(UNISimpleRequest *request) {
        [request setUrl:GEtFamilyMembers];
    }] asJsonAsync:^(UNIHTTPJsonResponse* response, NSError *error) {
        NSLog(@"res: %@", response.body);
        if (response.code == 200) {
             [[NSThread currentThread] isMainThread] ? NSLog(@"4MAIN THREAD") : NSLog(@"4NOT MAIN THREAD");
            NSData *rawBody = response.rawBody;
            NSString *string = [[NSString alloc] initWithData:rawBody encoding:NSUTF8StringEncoding];
            NSLog(@"family signup: %@", string);
            [[NSThread currentThread] isMainThread] ? NSLog(@"5MAIN THREAD") : NSLog(@"5NOT MAIN THREAD");
            [self processFamilyMembersResponse:mDictionary :response.body];

        } else {
            [self showToast:@"Something went wrong, Please try again after some time."];
        }
    }];
}

- (void) processFamilyMembersResponse:(NSMutableDictionary *) mDictionary :(UNIJsonNode *) body {

    NSArray *fmArray = body.JSONArray;
    if (fmArray == nil || [fmArray count] == 0) {
        NSNumber *accountId = (NSNumber *)[mDictionary objectForKey:@"accountId"];

    } else {
        NSMutableArray *mArray = [[NSMutableArray alloc] init];
        [mArray addObject:mDictionary];
        for (int i = 0; i < fmArray.count; i++) {
            NSDictionary *fmDictionary = [fmArray objectAtIndex:i];
            mDictionary = [[NSMutableDictionary alloc] init];
            NSNumber *accountId = (NSNumber *)[fmDictionary objectForKey:@"id"];
            [mDictionary setObject:accountId forKey:@"accountId"];
            [mDictionary setObject:[fmDictionary objectForKey:@"name"] forKey:@"name"];
            [mDictionary setObject:[fmDictionary objectForKey:@"relation"] forKey:@"relation"];
            [mArray addObject:mDictionary];
        }
        [[NSThread currentThread] isMainThread] ? NSLog(@"6MAIN THREAD") : NSLog(@"6NOT MAIN THREAD");
        [self gotoSelectFamilyMembers:mArray];
    }

}

- (void) gotoSelectFamilyMembers:(NSMutableArray *) mArray {
    dispatch_async(dispatch_get_main_queue(), ^{
        GCHFamilyMembersViewConroller* fmViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"gchFamilyMembers"];
        fmViewController._tblData = mArray;
        self.navigationController.viewControllers = @[fmViewController];
        [self.mm_drawerController setCenterViewController:self.navigationController withCloseAnimation:YES completion:nil];
    });
}
Drunken Daddy
  • 7,326
  • 14
  • 70
  • 104

2 Answers2

1

Try below code:

dispatch_async(dispatch_get_main_queue(), ^{
        GCHFamilyMembersViewConroller* fmViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"gchFamilyMembers"];
        fmViewController._tblData = mArray;

        ((AppDelegate*)[[UIApplication sharedApplication]delegate]).navigationController.viewControllers = @[fmViewController];
        [self.mm_drawerController setCenterViewController:((AppDelegate*)[[UIApplication sharedApplication]delegate]).navigationController withCloseAnimation:YES completion:nil];
    });
Ronak Chaniyara
  • 5,335
  • 3
  • 24
  • 51
0

You're using the asynchronous calls of Unirest and the response should be called on the UI thread (Main thread). Usually libraries similar to this deliver the result on the main thread. Therefore, I think you do not need to specifically call dispatch_async on the main queue. However, you can use the following check to see whether it gets called on the main thread or not.

 [[NSThread currentThread] isMainThread] ? NSLog(@"MAIN THREAD") : NSLog(@"NOT MAIN THREAD");

Here's what I suspect

  1. Your ViewController creation method has some fault and is consuming time
  2. Your response is very large

I suggest that you call the following without any web service calls to confirm that they do not cause the crash.

GCHFamilyMembersViewConroller* fmViewController = [self.storyboard instantiateViewControllerWithIdentifier:@"gchFamilyMembers"];

            self.navigationController.viewControllers = @[fmViewController];
            [self.mm_drawerController setCenterViewController:self.navigationController withCloseAnimation:YES completion:nil];
Ruchira Randana
  • 4,021
  • 1
  • 27
  • 24
  • Yes it is working without web calls. And I did say in my post that I did work with Synchronous a call – Drunken Daddy May 02 '16 at 12:36
  • Hi, I modified my answer which includes a way to check whether an executing block is being executed on the main thread or not. Can you please check with that because I see from your log, a certain possibility of a non-main thread being used to update UI. – Ruchira Randana May 02 '16 at 12:39
  • Well, it is printing NOT MAIN THREAD. How do I go back to the main thread? – Drunken Daddy May 02 '16 at 12:40
  • Really? Can you also confirm that you're calling the Unirest get method on the main thread? ie: Put the check before you call the web service call. – Ruchira Randana May 02 '16 at 12:41
  • And I also I'm calling this async task from the response of another async task. – Drunken Daddy May 02 '16 at 12:41
  • Well what you've done is technically correct to call the main thread. But, can you also check whether you're calling the web service above, from the main thread too? – Ruchira Randana May 02 '16 at 12:44
  • Can u post the logs which displays what gets printed for the values of the NSThread isMainThread? E.g: 1.MAIN THREAD 2. NOT MAIN THREAD etc – Ruchira Randana May 02 '16 at 12:57