Allocating/showing a UIAlertView in a Block statement - objective-c

I'm pretty new to blocks in objective C. I've read the docs and I have a pretty basic understanding of them.
Why won't this work? This is a framework callback for requesting Calendar access. It takes a block as an argument. All I want to do is allocate and show the UIAlertView in the block, but it will crash when it tries to show.
I hope this isn't a silly question... all the intro examples on the net using blocks just show trivial examples with counters.
//Request access
[eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
if (granted == FALSE) {
UIAlertView *myAlert = [[[UIAlertView alloc]initWithTitle:#"Calendar Access Denied"
message:#"<InfoText>"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil] autorelease];
[myAlert show];
}
else {
[self addToCalendar];
}
}];

have you tried?
if (granted == FALSE)
{
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *myAlert = [[[UIAlertView alloc]initWithTitle:#"Calendar Access Denied"
message:# <InfoText>"
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil] autorelease];
[myAlert show];
});
}
this makes calls back in the main thread, useful for mixing blocks and UIKit

Related

CloudKit Query Time

I've just started trialling CloudKit and am having some pretty slow query times. Here is some sample code I am using:
//CLOUDKIT
CKContainer *container = [CKContainer defaultContainer];
CKDatabase *privateDatabase = [container privateCloudDatabase];
NSPredicate *predicate = [NSPredicate predicateWithFormat:#"TRUEPREDICATE"];
CKQuery *query = [[CKQuery alloc] initWithRecordType:#"FlightLog" predicate:predicate];
[privateDatabase performQuery:query inZoneWithID:nil completionHandler:^(NSArray *results, NSError *error) {
//SUCCESS
if (!error)
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"SUCCESS" message:#"IT WORKED" delegate:self cancelButtonTitle:#"dismiss" otherButtonTitles:nil];
[alert show];
NSLog(#"%#", #"fetchFlights success!");
NSLog(#"%#", self.fetchedRecords);
self.fetchedRecords = results;
[self.tableView reloadData];
}
//ERROR
else
{
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"error" message:error.localizedDescription delegate:self cancelButtonTitle:#"dismiss" otherButtonTitles:nil];
[alert show];
NSLog(#"%#", error);
}
}];
I get the private database, and query for all records. There is just four simple ones I added in the dashboard.
Upon calling this code, I can see from my console log that the success message gets called almost immediately, with a null results array. Then moments later, the results are returned, as seen in the log. However, the alert view isn't shown and results displayed in my table for about 3-4 more seconds.
What's going on?
This is resolved. As Edwin mentions, I didn't know that the callback is on a background thread. So when I call [self.tableView reloadData] in the completion block, it is also running on the background thread.
By putting it back on the main thread, the table view reloads within about a second. Vs taking about 4-5 seconds if running on the same thread as the callback.
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_HIGH, 0), ^{
dispatch_async(dispatch_get_main_queue(), ^{
[self.tableView reloadData];
});
});
Let me know if I have misunderstood, but I think that's what has happened.

iOS 7.x: app would like to access your motion activity does not show up

I have an app that uses Motion activity feature. On the first run there is a pop up asking user permission to use the feature. But after re-installing the app, the pop up does not show up anymore.
Is this an intended behavior or am I missing something?
so it figures you need to instantiate CMMotionActivityManager for this to work.
make sure you don't miss out on the first line
_motionActivityManager =[[CMMotionActivityManager alloc] init];
if([CMMotionActivityManager isActivityAvailable])
NSLog(#"available");
else
NSLog(#"not available");
[_motionActivityManager startActivityUpdatesToQueue:[NSOperationQueue new] withHandler: ^(CMMotionActivity *activity) {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *theAlert = [[UIAlertView alloc] initWithTitle:#"activity"
message: activity.description
delegate:self
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[theAlert show];
if(activity.stationary)
{
NSLog(#"stationatry");
}
if(activity.walking)
{
NSLog(#"walking");
}
if(activity.automotive)
{
NSLog(#"automotive");
}
if(activity.cycling)
{
NSLog(#"cycling");
}
if(activity.unknown)
{
NSLog(#"unknown");
}
});
}];

UIAlertView does not appear if requestAccessToEntityType fails

i'm making an app that adds events to the default calendar but i found a problem. This is the code used to make the app access to the calendar:
// create eventStore object.
EKEventStore *eventStore = [[EKEventStore alloc] init];
if([eventStore respondsToSelector:#selector(requestAccessToEntityType:completion:)])
{
[eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error)
{
if(granted)
{
// create an instance of event with the help of event-store object.
EKEvent *event = [EKEvent eventWithEventStore:eventStore];
// set the title of the event.
event.title = #"Event";
event.startDate = [[NSDate date] dateByAddingTimeInterval:86400];
event.endDate = [[NSDate date] dateByAddingTimeInterval:90000];
// set the calendar of the event. - here default calendar
[event setCalendar:[eventStore defaultCalendarForNewEvents]];
// store the event using EventStore.
NSError *err;
[eventStore saveEvent:event span:EKSpanThisEvent error:&err];
}
else {
UIAlertView *warningAlert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"No permission to access!" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[warningAlert show];
}
}];
}
I supposed that if i got to general>privacy and set the access to calendar to "NO", whenever i click on the button that makes the action listed before, the program should skip in the "else" (founding the bool as false) that creates the alertView. But when i try this the program crashes not letting me do anything and if i press the home-button and then re-enter the app an empty alertView will appear (no title or message).
How can i solve this? i put the alertView in the wrong place?
Added info: this function is on a button that i click every time i mean to an event.
Have you tried showing the alert in main thread..
if (granted)
{
....
}
else {
dispatch_async(dispatch_get_main_queue(), ^{
UIAlertView *warningAlert = [[UIAlertView alloc] initWithTitle:#"Error" message:#"No permission to access!" delegate:nil cancelButtonTitle:#"OK" otherButtonTitles:nil];
[warningAlert show];
});
}
Requesting permissions from user won't happen in main thread. You can do your UI stuff (Alert) by getting to the main thread. Hope it helps..

Preventing UIView from switching before UIAlertView appears with Parse (Form not Complete, Logout Yes/No, etc)

I am currently coding an app that has a login and sign-up page with Parse as the backend. I am able to get the username and all of that registered, but I need help with the code that notifies the user if one of the fields are blank. I want to display the UIAlertView of the error and prevent the view from switching in this case (it switches and then displays the notification that the fields were left blank). I know it's probably something simple. Just need a little help. Here is the code below.
-(IBAction)signUpUserPressed:(id)sender
{
PFUser *user = [PFUser user];
user.username = self.userRegisterTextField.text;
user.password = self.passwordRegisterTextField.text;
[user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error) {
[self performSegueWithIdentifier:#"FinishSignup" sender:self];
} else {
[[[UIAlertView alloc] initWithTitle:#"Error" message:[error userInfo][#"error"] delegate:nil cancelButtonTitle:#"Okay" otherButtonTitles:nil] show];
}
}];
}
Help would be much appreciated on this. This is also similar to if you are logging out of an app and it asks you if you would like to logout- stopping the view from switching and displaying the notification.
Thanks!
Before calling signUpInBackgroundWithBlock: check if the fields are empty yourself.
PFUser *user = [PFUser user];
user.username = self.userRegisterTextField.text;
user.password = self.passwordRegisterTextField.text;
if (user.username.length > 0 && user.password.length > 0) {
//sign up only if username/password given
[user signUpInBackgroundWithBlock:^(BOOL succeeded, NSError *error) {
if (!error) {
[self performSegueWithIdentifier:#"FinishSignup" sender:self];
} else {
[[[UIAlertView alloc] initWithTitle:#"Error" message:[error userInfo][#"error"] delegate:nil cancelButtonTitle:#"Okay" otherButtonTitles:nil] show];
}
}];
}
}
else {
//show alert that username or password was left blank
}

GameKit Server/Client

I have been trying to implement the GameKit Framework for bluetooth connection and want to use a Server/Client relationship to reduce lag and be able to distinguish between to connected devices. I found this thread and it is similar to what I am trying to do, but the code doesn't work for me. Here is what I have:
Connect Method:
-(IBAction) btnConnect:(id) sender {
if(sender == server){
[self.currentSession initWithSessionID:#"BT" displayName:nil sessionMode:GKSessionModeServer];
currentSession.available == YES;
NSLog(#"Setup Server");
}else{
[self.currentSession initWithSessionID:#"BT" displayName:nil sessionMode:GKSessionModeClient];
currentSession.available == YES;
NSLog(#"Setup Client");
}
currentSession.delegate = self;
currentSession.disconnectTimeout = 0;
[currentSession setDataReceiveHandler:self withContext:nil];
[client setHidden:YES];
[server setHidden:YES];
[disconnect setHidden:NO];
}
didChangeState:
- (void)session:(GKSession *)session peer:(NSString *)peerID didChangeState:(GKPeerConnectionState)state {
NSLog(#"didChangeState was called with status: %#.", state);
switch (state)
{
case GKPeerStateConnected:
NSLog(#"connected");
break;
case GKPeerStateDisconnected:
NSLog(#"disconnected");
[self.currentSession release];
currentSession = nil;
[connect setHidden:NO];
[disconnect setHidden:YES];
break;
case GKPeerStateAvailable:
NSLog(#"Server is Available, Presenting UIALert...");
NSLog(#"%#", peerID);
peerName = [session displayNameForPeer:peerID];
NSLog(#"%#", peerName);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Server Available!" message:[NSString stringWithFormat:#"The Server %# is Available, Would you like to Connect?", peerName] delegate:self cancelButtonTitle:#"Decline" otherButtonTitles:#"Accept", nil];
[alert show];
[alert release];
if(selection == #"accept"){
[session connectToPeer:peerID withTimeout:15];
session.available = NO;
}else{
}
break;
}
}
didReceiveConnectionRequest:
- (void)session:(GKSession *)session didReceiveConnectionRequestFromPeer:(NSString *)peerID{
NSLog(#"Recieved Connection Request");
NSLog(#"%#", peerID);
peerName = [session displayNameForPeer:peerID];
NSLog(#"%#", peerName);
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Connection Request" message:[NSString stringWithFormat:#"The Client %# is trying to connect.", peerName] delegate:self cancelButtonTitle:#"Decline" otherButtonTitles:#"Accept", nil];
[alert show];
[alert release];
if(selection == #"accept"){
[session acceptConnectionFromPeer:peerID error:nil];
}else{
[session denyConnectionFromPeer:peerID];
}
}
I think I have this all setup right, but the didChangeState isn't getting called to inform the user that another device is available. Am I missing something or should I try to use a different method. Thanks for any help
currentSession.disconnectTimeout = 0;
The disconnect timeout is a time in seconds that peers should wait before disconnecting unresponsive peers. You don't want this to be 0. The default is 20 seconds, you should leave it there or say like 10 seconds. I actually don't set this in my GameKit code and it works well.
Also, it might help to post your entire implementation class somewhere. We'll need to make sure you are implementing GKSessionDelegate, e.g.:
#interface SomeObject : NSObject <GKSessionDelegate>
Also, you're setting up a Peer-2-Peer above. You said you were trying to do client/server. If so you should start the client session with a mode of GKSessionModeClient and server as GKSessionModePeer.
Lastly...are you testing this on actual devices or with a device and simulator? Don't forget that simulator and first gen iPhones and touches do not support bluetooth. So you'll have to have everyone involved connected to the same wireless network for anything to happen.
What are you seeing in the console when you start up your debug session?