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.
Related
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?
I'm using Cloud Kit in my app which makes use of the camera and only allows the user to submit photos if they're logged into iCloud. So when the user clicks the camera button, I call a CloudKit method to get the user's icloud status, which returns a CKAccountStatus value (0-3). I implemented this initially in the view controller and it worked perfectly. Then I did some refactoring and created a CKManager class to house all CK related methods. So now when the camera is clicked instead of calling the CK method off the container directly in the VC, I'm calling it via a method from my CKManager property (which is lazy instantiated). It should only return values 0-3, but it keeps returning 448 for some reason. However, in the CKManager logging, I can see it logging correctly that I'm logged into iCloud. So there's an issue of it translating from there back to the VC. I have feeling this is a threading/callback issue, which I'm not that well versed in.
Can someone take a look at the code and see if there's something obvious I'm doing wrong? Thanks in advance!
- (IBAction)cameraBarButtonPressed:(UIBarButtonItem *)sender {
NSLog(#"Entered cameraBarButtonPressed");
//CKContainer *container = [CKContainer defaultContainer];
dispatch_queue_t fetchQ = dispatch_queue_create("check user status", NULL);
__block CKAccountStatus userAccountStatus;
dispatch_async(fetchQ, ^{ // check user's CK status on different thread
userAccountStatus = [self.ckManager getUsersCKStatus];
NSLog(#"cameraBarButtonPressed userAccountStatus: %ld", userAccountStatus);
if (userAccountStatus == CKAccountStatusAvailable) {
//NSLog(#"User is logged into CK - user can upload pics!");
UIImagePickerController *cameraUI = [[UIImagePickerController alloc] init];
cameraUI.delegate = self; // set the deleage for the ImagePickerController
// check to see if the camera is available as source type, else check for photo album
if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeCamera]) {
cameraUI.sourceType = UIImagePickerControllerSourceTypeCamera;
} else if ([UIImagePickerController isSourceTypeAvailable:UIImagePickerControllerSourceTypeSavedPhotosAlbum]) {
cameraUI.sourceType = UIImagePickerControllerSourceTypeSavedPhotosAlbum;
}
[cameraUI setAllowsEditing:YES]; // let the user edit the photo
// set the camera presentation style
//cameraUI.modalPresentationStyle = UIModalPresentationFullScreen;
cameraUI.modalPresentationStyle = UIModalPresentationCurrentContext;
dispatch_async(dispatch_get_main_queue(), ^{ // show the camera on main thread to avoid latency
[self presentViewController:cameraUI animated:YES completion:nil]; // show the camera with animation
});
} else if (userAccountStatus == CKAccountStatusNoAccount) {
//NSLog(#"User is not logged into CK - Camera not available!");
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"iCloud Not Available" message:#"You must be logged into your iCloud account to submit photos and recipes. Go into iCloud under Settings on your device to login." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
dispatch_async(dispatch_get_main_queue(), ^{
[alert show];
});
} else if (userAccountStatus == CKAccountStatusRestricted) {
NSLog(#"User CK account is RESTRICTED !");
} else if (userAccountStatus == CKAccountStatusCouldNotDetermine) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"iCloud Status Undetermined" message:#"We could not determine your iCloud status. You must be logged into your iCloud account to submit photos and recipes. Go into iCloud under Settings on your device to login." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
dispatch_async(dispatch_get_main_queue(), ^{
[alert show];
});
} else { // did not get back one of the above values so show the Could Not Determine message
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"iCloud Status Undetermined" message:#"We could not determine your iCloud status. You must be logged into your iCloud account to submit photos and recipes. Go into iCloud under Settings on your device to login." delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil, nil];
dispatch_async(dispatch_get_main_queue(), ^{
[alert show];
});
}
});
}
The above code is the code that does not work. Here is the code that does work. Just copying the beginning code as the rest is redundant from that point on...
CKContainer *container = [CKContainer defaultContainer];
dispatch_async(fetchQ, ^{ // check user's CK status on different thread
[container accountStatusWithCompletionHandler:^(CKAccountStatus accountStatus, NSError *error) {
if (error) {...
Lastly, here is the code that gets called from CKManager for the code that does not work...
- (CKAccountStatus)getUsersCKStatus {
NSLog(#"Entered getUsersCKStatus...");
__block CKAccountStatus userAccountStatus;
[self.container accountStatusWithCompletionHandler:^(CKAccountStatus accountStatus, NSError *error) {
if (error) {
NSLog(#"Error: Error encountered while getting user CloudKit status: %#", error.localizedDescription);
} else {
if (accountStatus == CKAccountStatusAvailable) {
NSLog(#"Info: User is logged into CK - camera is available!");
userAccountStatus = CKAccountStatusAvailable;
} else if (accountStatus == CKAccountStatusNoAccount) {
NSLog(#"Info: User is not logged into CK - Camera not available!");
userAccountStatus = CKAccountStatusNoAccount;
} else if (accountStatus == CKAccountStatusRestricted) {
NSLog(#"Info: User CK account is RESTRICTED - what does that mean!?");
userAccountStatus = CKAccountStatusRestricted;
} else if (accountStatus == CKAccountStatusCouldNotDetermine) {
NSLog(#"Error: Could not determine user CK Account Status: %#", error.localizedDescription);
userAccountStatus = CKAccountStatusCouldNotDetermine;
}
}
}];
NSLog(#"CKAccountStatus: %ld", userAccountStatus);
return userAccountStatus;
}
In the getUsersCKStatus you are calling the accountStatusWithCompletionHandler. That is an asynchronous method. In your case it will return the userAccountStatus before it is set by its callback method.
You could solve this by making that method synchronous by implementing a semaphore. A better way would be passing on a callback block to that method and not returning a value.
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;
}
In my app there's a UIWebView showing a web page. It sometimes displays errors in a UIAlertView that are really annoying. I would like to intercept this and show the errors in a more sophisticated way.
Is there a way where i can intercept the error message in a function and decide for myself what i want to do with it?
Thanks in advance!
This seems to do it:
- (void)webViewDidFinishLoad:(UIWebView *)webView
{
JSContext *ctx = [webView valueForKeyPath:#"documentView.webView.mainFrame.javaScriptContext"];
ctx[#"window"][#"alert"] = ^(JSValue *message) {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"JavaScript Alert" message:[message toString] delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[alert show];
};
}
Note: only tested on iOS 8.
I want to execute some code only, and only if I am connected to the internet:
//Reachability
[[NSNotificationCenter defaultCenter] addObserver:self
selector:#selector(reachabilityChanged:)
name:kReachabilityChangedNotification
object:nil];
Reachability * reach = [Reachability reachabilityWithHostname:#"www.dropbox.com"];
reach.reachableBlock = ^(Reachability * reachability)
{
dispatch_async(dispatch_get_main_queue(), ^{
NSLog(#"Block Says Reachable");
connect = #"yes";
});
};
reach.unreachableBlock = ^(Reachability * reachability)
{
dispatch_async(dispatch_get_main_queue(), ^{
connect = #"no";
});
};
[reach startNotifier];
//Reachability
if (connect == #"no") {
UIAlertView *alert1 = [[UIAlertView alloc] initWithTitle:#"" message:#"There is no internet connection. Please connect to the internet. If you are already connected, there might be a problem with our server. Try again in a moment." delegate:self cancelButtonTitle:#"Dismiss" otherButtonTitles: nil];
[alert1 show];
} else if (titleSet == NULL){
UIAlertView *alert1 = [[UIAlertView alloc] initWithTitle:#"" message:#"Please select a group or create a new one" delegate:self cancelButtonTitle:#"Dismiss" otherButtonTitles: nil];
[alert1 show];
}else if (NavBar.topItem.title.length < 1){
UIAlertView *alert1 = [[UIAlertView alloc] initWithTitle:#"" message:#"Please select a group or create a new one" delegate:self cancelButtonTitle:#"Dismiss" otherButtonTitles: nil];
[alert1 show];
} else if (newmessagename.text.length < 4){
UIAlertView *alert1 = [[UIAlertView alloc] initWithTitle:#"" message:#"Please give a name to your event that is at least 4 characters long" delegate:self cancelButtonTitle:#"Dismiss" otherButtonTitles: nil];
[alert1 show];
}
It seems that the code is not executed in order. I think it is taking more time to check the Internet connection than it takes to execute the code. How can I fix this?
Please do not tell me to place the code directly in the parenthesis where connect = #"no"; is located.
The blocks aren't executed sequentially, they are executed asynchronously.
This means that you can't tell when the code inside the blocks is going to be called. The code using the block may finish and execute before the rest of your method (however this is unlikely, especially with Internet connections).
You should place your ifs in a method that is called at a valid time. This time is probably when you receive a response from your blocks, or, if my memory serves true, [reach startNotifier]; can notify you when there is a change in reachability status, this appears to be your reachabilityChanged: method:
-(void) reachabilityChanged:(id) parameter
{
//Query reachability and notify / cache as required.
}
Of course its not executed in order, the whole point of those methods is to stop the ui to freeze while you get your reachability response. Basically you set up the reachability responses and immediately ask for the result when nothing has been checked yet. What you have to really is to move it inside the brackets.
Something else that you can do is make a function with those results, and call this function in both of the blocks.
If you want to have this on the load of the viewcontroller or before you display anything else, then you either have to check for reachability before you show this controller, or add a "loading" screen.
EDIT: Something else that i dont understand is, those reachability methods seem to fire a block when they get the results, but you are registering for a notification as well. And i dont see you posting a notification for this. You are using 2 asynchronous approches here (blocks and notifications)