Suddenly iOS App is not responsive at all - ios

After I call a certain Google's Youtube library, my application suddenly becomes not responsive at all after one of its callback.
Not responsive means all UI components cannot be clicked.
Is there such thing in iOS that can disable entire screen to be not responsive at all?
The code:
self.uploadFileTicket = [service executeQuery:query
completionHandler:^(GTLServiceTicket *ticket,
GTLYouTubeVideo *uploadedVideo,
NSError *error) {
// Callback
_uploadFileTicket = nil;
if (error == nil) {
[_delegate videoUploadedSuccessfully:YES error:nil];
} else {
[_delegate videoUploadedSuccessfully:NO error:error.description];
}
}];
Inside my ViewController:
- (void)videoUploadedSuccessfully:(BOOL)success error:(NSString *)errorMessage{
dispatch_async(dispatch_get_main_queue(), ^{
if(success){
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Youtube"
message:#"Video uploaded successfully"
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:#"OK", nil];
[alert show];
}
else{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Youtube"
message:errorMessage
delegate:nil
cancelButtonTitle:nil
otherButtonTitles:#"OK", nil];
[alert show];
}
});
}
Update
I have tried using Instrument and got following:
Does this mean my Working Thread are blocking Main Thread?

Finally I found the ROOT cause of this issue. There is somewhere in the code before uploading the video to Youtube:
[[UIApplication sharedApplication] beginIgnoringInteractionEvents];

I cannot pin-point the issue. But, here are some of my suggestions.
The method call [service executeQuery:query completionHandler: has a completion handler. Therefore, it's mostly an async task. Therefore the task of the service should be done in a background thread and should not block the UI.
In case you're not sure whether the call is in the main thread, use the following LOC to clarify.
[NSThread isMainThread] will return true only if the executing thread is the main thread/ UI-thread.
Can you also put a NSLog at videoUploadedSuccessfully and check whether the delegate method gets called multiple times?
You do not need the block
dispatch_async(dispatch_get_main_queue(), ^{}
The delegate method should get executed on the main thread itself as long as you're calling the service-query method on the main thread.
Finally check whether you're calling the [service executeQuery:query method from the main thread?

Related

System hangs when showing a UIAllertView inside a SLRequestHandler

I am using SLRequest to send user's video to twitter. After finishing the post request, I want to inform the user whether the upload is successful. But if I show a UIAlertView inside the SLRequestHandler, the system simply hangs and doesn't show the alert view at all. Is it a no-go to have a UIAlertView inside the SLRequestHandler? What is the better way to show a custom message based on the result of the post request?
Here is my sample code:
SLRequest *postRequest2 = [SLRequest
requestForServiceType:SLServiceTypeTwitter
requestMethod:SLRequestMethodPOST
URL:requestURL2 parameters:message2];
postRequest2.account = twitterAccount;
[postRequest2
performRequestWithHandler:^(NSData *responseData,
NSHTTPURLResponse *urlResponse, NSError *error)
{
if (error != nil) {
NSLog(#"error: %#", error);
}
else {
UIAlertView *theAlert = [[UIAlertView alloc] initWithTitle:#"Success!"
message:#"Your video is now available in your Twitter account"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[theAlert show];
}
}];
All UI related operations must be on the main thread.
Would you try to dispatch on the main thread your alert view?
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *theAlert = [[UIAlertView alloc] initWithTitle:#"Success!" message:#"Your video is now available in your Twitter account" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[theAlert show];
});
Please note that UIAlertView is deprecated since iOS 8, and the use of UIAlertViewController is recommended.
You are trying to show the alert message in a block.
Alerts are UI Thread (main thread) controls. So, modify else part and show your alert in dispatch_async, it will work.
dispatch_async(dispatch_get_main_queue(), ^{
[theAlert show];
});

Blocks in Parse

I have started use Parse (which I downloaded using Cocoapods) in a practice iOS application and I having a little bit of trouble understanding certain concepts.
I have written this code so far:
- (IBAction)saveUserButtonClicked:(id)sender {
PFObject *loginCredentials = [PFObject objectWithClassName:#"LoginCredentials"];
loginCredentials[#"name"] = self.usernameTextField.text;
loginCredentials[#"password"] = self.passwordTextField.text;
[loginCredentials saveInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if(error.code == kPFErrorConnectionFailed){
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Error" message:#"Please check you connection" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alertView show];
}else if(succeeded && !error){
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Save" message:#"Your object saved" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alertView show];
}else if(error){
NSLog(#"%#", error);
}
}];
}
My main question I have is what is the purpose of using saveInBackGroundWithBlock. Could I do the same logic by doing:
[loginCredentials saveInBackground];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Save" message:#"Your object saved" delegate:nil cancelButtonTitle:#"Ok" otherButtonTitles: nil];
[alertView show];
Is the block only helpful if we want to have access to the succeeded and error variables?
in saveInBackgroundWithBlock the save operation is executed in a background thread not in the main thread(thread used by UI ), once it ends executing it calls back the block to execute it. Not using the main thread for this save-operation makes the user-interface responsive while executing the save in another thread.
you can make the save operation in the main thread by using save method and then show alert based on success, but you will completely block the user interface and its not a best practice unless its necessarily to continue on the app.
bool succeeded = [loginCredentials save];
Synchronous calls will execute code on the main thread (UI thread), which will prevent the UI from responding to user events. This is generally considered bad practice because it gives the user the impression that your app has frozen when in fact one thread can't do two things at once. Therefore, you typically use asynchronous calls (blocks) to execute complex code on background threads, freeing the UI to continue performing some operation so the user isn't confused or just left hanging waiting on the operation to finish.
Looking at the PFObject spec, the – saveInBackground and – saveInBackgroundWithBlock: methods are the asynchronous options. In this case, the callback block is just reporting whether or not the operation was successful, and includes an NSError object for reporting in case it failed.
That all being said, I think you can save the object using your code, but you will not get the opportunity to respond to any errors that arise in the process. Meaning, you are making an assumption that your object saved successfully when you are creating and presenting the alert view.
I'm basing this opinion on the saveInBackground method implementation, which is just returning a BFTask object:
- (BFTask *)saveInBackground {
return [self.taskQueue enqueue:^BFTask *(BFTask *toAwait) {
return [self saveAsync:toAwait];
}];
}
Here's the object documentation on the two methods from Parse:
– saveInBackground
Saves the PFObject asynchronously.
(BFTask PF_GENERIC ( NSNumber *) *)saveInBackground
Return Value
The task that encapsulates the work being done.
Declared In PFObject.h
– saveInBackgroundWithBlock:
Saves the PFObject asynchronously and executes the given callback block.
(void)saveInBackgroundWithBlock:(PF_NULLABLE PFBooleanResultBlock)block
Parameters
block
The block to execute.
It should have the following argument signature: ^(BOOL succeeded, NSError *error).
Declared In PFObject.h
If you aren't interested in handling the errors, and your objects are sufficiently simple to save, have you considered just using one of the synchronous save methods, like - (BOOL)save or - (BOOL)save:(NSError **)error? You could immediately respond to the BOOL value that is returned, in similar fashion to the Parse example you posted above (inside the completion block).

NSError release: message sent to deallocated instance

I am getting a crash on a iOS 7 app with the following error:
-[NSError release]: message sent to deallocated instance 0x3c443fe0
The error is initiated when I add a call to the following method:
-(void)loadMessages:(NSString*)customerUID {
NSString *formatUID = [NSString stringWithFormat:#"%s%#%s", "'", customerUID, "'"];
formatUID = [formatUID stringByReplacingOccurrencesOfString:#"'" withString:#"%27"];
NSString *servicePath = [NSString stringWithFormat:#"/api/messagerecipient?messageid=null&customeruid=%#", formatUID];
[[RKObjectManager sharedManager] getObjectsAtPath:servicePath parameters:nil success:^(RKObjectRequestOperation *operation, RKMappingResult *messagesResult)
{
NSArray *messageResults = messagesResult.array;
if (messageResults != nil || [messageResults count] != 0)
{
//Add some code here
}
} failure:^(RKObjectRequestOperation *operation, NSError *error) {
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"An Error Has Occurred" message:[error localizedDescription] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
}];
}
I added multiple breakpoints into the code at various points, and it's not returning any error details. Also, nothing in the console log indicates what the problem is (I have added full RestKit logging), just the above NSError release message.
I have also run a Zombie scan in Instruments. It shows the following.
I'm confused because this shows that the zombie is being created by a GSEventRunModal call. When I go to Extended Detail and select the call, it shows the following:
Any pointers would be gratefully appreciated, thanks.
Update: Instrument Extended Details stack trace
I've seen this a lot as well and the root of the problem appears to be in Core Data. I use the MagicalRecord database library (so does RestKit) and we thought the error was there. You can see a discussion here. After all of our investigation it seemed like MagicalRecord was right and Core Data was at fault.
This had actually been filed as a bug that Apple claimed to have fixed, but we are still seeing it. The only way I've been able to work around this is by preventing every instance where I might not be able to save data so no error is reported. You can see some of those tips in the discussion thread linked to above.
Could it be that you are trying to display an AlertView from inside a block? Interaction with the UI has to be on the main thread?
How do display a UIAlertView from a block on iOS?
Can you try to replace:
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"An Error Has Occurred" message:[error localizedDescription] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
With:
NSString * message = [error localizedDescription];
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"An Error Has Occurred" message:message delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alertView show];
Although I guess that the init method is getting a string anyway.
i think your problem is not with the method it self.
the error message says that you are sending a release call to an object of the type NSERROR.
please check the instance of the class which contains the method you are calling and make sure it's not deallocated.
or add the calling method to the question in order for us to check it.
In my case threading the database to a separate context helped. I used the following constructor on the class that was receiving the message:
-(id)init {
self = [super init];
if (self) {
self.managedObjectContext = [[NSManagedObjectContext alloc] initWithConcurrencyType:NSPrivateQueueConcurrencyType];
self.managedObjectContext.parentContext = [(AppDelegate *)[[UIApplication sharedApplication] delegate] managedObjectContext];
self.managedObjectContext.retainsRegisteredObjects = YES;
}
return self;
}

BAD ACCESS Error when using requestAccessToEntityType

I am getting a EXEC_BAD_ACCESS Error when I attempt to run this code, and the user has not allowed access to the calendar. Does requestAccessToEntityType run on a separate thread, if thats the case how do I access the main thread to display the UIAlertView?
EKEventStore *store = [[EKEventStore alloc] init];
if ([store respondsToSelector:#selector(requestAccessToEntityType:completion:)])
{
[store requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error)
{
if ( granted )
{
[self readEvents];
}
else
{
UIAlertView *alert = [[UIAlertView alloc]
initWithTitle:#"Denied Access To Calendar"
message:#"Access was denied to the calendar, please go into settings and allow this app access to the calendar!"
delegate:nil
cancelButtonTitle:#"Ok"
otherButtonTitles:nil,
nil];
[alert show];
}
}];
}
According to the docs for requestAccessToEntityType
When the user taps to grant or deny access, the completion handler
will be called on an arbitrary queue.
So, yes, it could be on a different thread than the UI one. You can only put up alerts from the main GUI thread.
Look into performSelectorOnMainThread. More information here: Perform UI Changes on main thread using dispatch_async or performSelectorOnMainThread?
Reason why your app is crashing because you are trying to deal with your GUI elements i.e UIAlertView in background thread, you need to run it on the main thread or try to use dispatch queues
Using Dispatch Queues
dispatch_queue_t queue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0ul);
dispatch_async(queue, ^{
//show your UIAlertView here... or any GUI stuff
});
OR you can show the GUI elements on the main thread like this
[alertView performSelectorOnMainThread:#selector(show) withObject:nil waitUntilDone:YES];
You can have more detail about using GUI elements on Threads on this link

Twitter.framework - twitterd session interrupted, restarting

Got a problem with the new Twitter.framework that I haven't been able to find a solution for yet.
Here is my code:
if ([TWTweetComposeViewController canSendTweet]){
TWTweetComposeViewController *twitter = [[TWTweetComposeViewController alloc] init];
[twitter addImage:tweetImage];
[twitter setInitialText:initalString];
[twitter addURL:url];
twitter.completionHandler = ^(TWTweetComposeViewControllerResult result) {
if (result == TWTweetComposeViewControllerResultDone) {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:#"Tweeted"
message:#"You successfully tweeted"
delegate:self cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
[alertView release];
});
} else if (result == TWTweetComposeViewControllerResultCancelled) {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView* alertView = [[UIAlertView alloc] initWithTitle:#"Twitter"
message:#"Tweet has been canceled"
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[alertView show];
[alertView release];
});
}
dispatch_async(dispatch_get_main_queue(), ^{
[self dismissModalViewControllerAnimated:YES];
});
};
[self presentViewController:twitter animated:YES completion:nil];
[twitter release];
}
Seems to be the standard way of implementing this although I made the addition of queuing the UI stuff on the main thread. The addImage, setInitialText and addURL parameters are all good. In, fact this works most of the time. The problem I am having is that occasionally when the TWTweetComposeViewController is alloc'd init the app freezes and I can see "twitterd session interrupted, restarting... " in the console. The app will hang sometimes for only a few seconds but more often it will hang for unreasonable amount of time (20 - 30 secs or more), I will get numerous of these messages and then the twitter controller will finally slide up. Occasionally, as well, it will just hang and never come back.
Was wondering it anybody has see this problem before or has any ideas on a solution to this problem?
Thanks in advance.
I never add these problems with the twitter view controller. I used my code pasted in this post: https://stackoverflow.com/questions/9314308/can-twtweetcomposeviewcontroller-tweet-sheet-rotate-to-landscape
You can try it, just change it to "self" when you dismiss or present in modal view, as I'm using a different view controller.

Resources