I have two (2) calendars (iCal) on my iPad (one personal, one for the app). They are sync'd to my iMac for testing only. (Saves me time making entries to the specific app calendar).
I am currently writing an app that needs to access the app's calendar. It is the primary calendar on the iPad. I am trying to get Apple's SimpleEKDemo (unmodified) to work with the app's calendar, but so far I can't even get it not crash, much less to return anything. I have been looking at Google and SO questions for hours now, and decided it's time to call in the big guns.
This is the code where it's crashing:
- (void)viewDidLoad
{
self.title = #"Events List";
// Initialize an event store object with the init method. Initilize the array for events.
self.eventStore = [[EKEventStore alloc] init];
self.eventsList = [[NSMutableArray alloc] initWithArray:0];
// Get the default calendar from store.
self.defaultCalendar = [self.eventStore defaultCalendarForNewEvents]; // <---- crashes here --------
// Create an Add button
UIBarButtonItem *addButtonItem = [[UIBarButtonItem alloc] initWithBarButtonSystemItem:
UIBarButtonSystemItemAdd target:self action:#selector(addEvent:)];
self.navigationItem.rightBarButtonItem = addButtonItem;
[addButtonItem release];
self.navigationController.delegate = self;
// Fetch today's event on selected calendar and put them into the eventsList array
[self.eventsList addObjectsFromArray:[self fetchEventsForToday]];
[self.tableView reloadData];
}
This is the output from the "crash":
2012-10-05 14:33:12.555 SimpleEKDemo[874:907] defaultCalendarForNewEvents failed: Error Domain=EKCADErrorDomain Code=1013 "The operation couldn’t be completed. (EKCADErrorDomain error 1013.)"
I need to make sure I'm on the correct calendar... how do I do that?
You need to ensure you ask permission before trying to access the Event Store. Note that you need to only call this once. If the user denies access, they need to go to iOS Settings (see comment in code) to enable permissions for your app.
/* iOS 6 requires the user grant your application access to the Event Stores */
if ([eventStore respondsToSelector:#selector(requestAccessToEntityType:completion:)])
{
/* iOS Settings > Privacy > Calendars > MY APP > ENABLE | DISABLE */
[eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error)
{
if ( granted )
{
NSLog(#"User has granted permission!");
}
else
{
NSLog(#"User has not granted permission!");
}
}];
}
In iOS 5, you are only allowed to access Events (EKEntityTypeEvent) in the Event Store, unlike in iOS 6, where you can access Reminders (EKEntityTypeReminder). But you need the above code to at least get granted 1 time.
I should also mention that you need to be granted permission BEFORE you access the EventStore, in your case: [self.eventStore defaultCalendarForNewEvents];.
Also, defaultCalendarForNewEvents would be the correct way to access the users Default Calendar. If you wish to access a calendar with another name, then you need to iterate through the calendars and choose the appropriate one based on the results returned.
//Check if iOS6 or later is installed on user's device *********
if([eventStore respondsToSelector:#selector(requestAccessToEntityType:completion:)]) {
//Request the access to the Calendar
[eventStore requestAccessToEntityType:EKEntityTypeReminder completion:^(BOOL granted,NSError* error){
//Access not granted-------------
if(!granted){
NSString *message = #"Hey! I Can't access your Calendar... check your privacy settings to let me in!";
UIAlertView *alertView = [[UIAlertView alloc]initWithTitle:#"Warning"
message:message
delegate:self
cancelButtonTitle:#"Ok"
otherButtonTitles:nil,nil];
//Show an alert message!
//UIKit needs every change to be done in the main queue
dispatch_async(dispatch_get_main_queue(), ^{[alertView show];});
//Access granted------------------
}
else
{
self.defaultCalendar=[self.eventStore defaultCalendarForNewEvents];
}
}];
}
//Device prior to iOS 6.0 *********************************************
else{
self.defaultCalendar=[self.eventStore defaultCalendarForNewEvents];
}
Related
I have an app that worked great before Apple insisted users be given a choice between "Always" and "When In Use" for Location Manager.
The app used iBeacons to send invitations to play games and accept.
When "Always" is selected the beacons work fine but when I switch to "When In Use" they don't work at all.
I started out using "Always" but change the following code to give users the choice.
In the app's plist I added "Privacy-Location Always and When In Use Usage Descriptions and Privacy-Location When In Use Usage Description" and removed the "Privacy-Location Always Usage Description".
In the app's Delegate I have this
- (void)locationManager:(CLLocationManager *)manager
didChangeAuthorizationStatus:(CLAuthorizationStatus)status{
if([CLLocationManager authorizationStatus]==kCLAuthorizationStatusAuthorizedAlways){
NSLog(#"Always");
AlertView2 = [[UIAlertView alloc] initWithTitle:#"Dual player on two devices is enabled."
message:#"To save battery power go to Settings/Privacy/Location Services and choose \"Never\" when not using I'M GAME. Two people can still play on one device when in \"Never\" mode. To recieve invitations to play only when the app is open select \"When In Use\"."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[AlertView2 show];
[[NSUserDefaults standardUserDefaults] setObject:#"YES" forKey:#"accessKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
if([CLLocationManager authorizationStatus]==kCLAuthorizationStatusAuthorizedWhenInUse){
NSLog(#"WhenInUse");
AlertView2 = [[UIAlertView alloc] initWithTitle:#"Dual player on two devices is enabled."
message:#"To save battery power go to Settings/Privacy/Location Services and choose \"Never\" when not using I'M GAME. Two people can still play on one device when in \"Never\" mode. To recieve invitations to play while app is in background select \"Always\"."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[AlertView2 show];
[[NSUserDefaults standardUserDefaults] setObject:#"YES" forKey:#"accessKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
if([CLLocationManager authorizationStatus]==kCLAuthorizationStatusRestricted){
NSLog(#"restricted");
}
if([CLLocationManager authorizationStatus]==kCLAuthorizationStatusDenied){
NSLog(#"denied");
AlertView2 = [[UIAlertView alloc] initWithTitle:#"Dual player on a single device Only."
message:#"To play on two devices go to Settings Privacy/Location Services and choose \"Always\" or \"When In Use\" for I'M GAME. In \"Always\" you can recieve invites while app is in background, in \"When In Use\" invites only appear when the app is on screen. To preserve battery choose \"Never\" when not using I'M GAME."
delegate:nil
cancelButtonTitle:#"OK"
otherButtonTitles:nil];
[AlertView2 show];
[[NSUserDefaults standardUserDefaults] setObject:#"YES" forKey:#"accessKey"];
[[NSUserDefaults standardUserDefaults] synchronize];
}
if([CLLocationManager authorizationStatus]==kCLAuthorizationStatusNotDetermined){
NSLog(#"undetermined2");
[locationManager requestAlwaysAuthorization];
[locationManager requestWhenInUseAuthorization];
}
}
Does iBeacon need to have Privacy-Location set to "Always" to work?
So I just found out that in "When In Use" you can't monitor for beacon region is entered or exited only find its range. So I guess the question is how would I use range to send a notification to my user.
When you have your app authorized to do beacon ranging only when it is in the foreground, it's easy to simulate entry/exit logic with just a didRangeBeacons callback.
Set up one class variable:
var beaconLastSeen: Date? = nil
Add this code to your didRangeBeacons method:
if beacons.count > 0 {
if beaconLastSeen == nil {
// call didEnterRegion logic here
}
beaconLastSeen = Date()
}
else {
if beconLastSeen != nil && Date() - beaconLastSeen > 30 {
beaconLastSeen = nil
// call didExitRegion logic here
}
}
You will get an exit event 30 secs after the last beacon detection. You will get an enter event when one is first seen.
EDIT: Here's the same code in Objective C:
NSDate *beaconLastSeen = nil;
...
if (beacons.count > 0) {
if (beaconLastSeen == nil) {
// call didEnterRegion logic here
}
beaconLastSeen = [[NSDate alloc] init];
}
else {
if (beaconLastSeen != nil && [[[NSDate alloc] init] timeIntervalSinceDate: beaconLastSeen] > 30 ) {
beaconLastSeen = nil;
// call didExitRegion logic here
}
}
I update my Xcode to 7 , and Facebook to 4.6 sdk.
this My warning :
Warning: Attempt to present <FBSDKContainerViewController: 0x159337700> on <UIAlertController: 0x159262700> whose view is not in the window hierarchy!
in My project the BitCode is NO - because if I turn it to Yes I got this Error :
ld:'/Users/MyName/Desktop/MyProjectName/ProjectName/ProjectName/Resources/Frameworks/Fabric.framework/Fabric(Fabric.o)' does not contain bitcode. You must rebuild it with bitcode enabled (Xcode setting ENABLE_BITCODE), obtain an updated library from the vendor, or disable bitcode for this target. for architecture arm64
clang: error: linker command failed with exit code 1 (use -v to see invocation)
this is the parse method :
-(void)signInWithFacebookClicked
{
NSArray *permissions = [NSArray arrayWithObjects:#"email",#"user_friends", nil];
[PFFacebookUtils logInInBackgroundWithReadPermissions:permissions block:^(PFUser *user, NSError *error)
{
if (!user) // The user cancelled the Facebook login
{
NSLog(#"Uh oh. The user cancelled the Facebook login.");
}
else if (user.isNew) // New user (not stored on DB) - User signed up and logged in through Facebook
{
[self handleNewUser];
}
else if (user) // the user is exist at DB
{
// the user is exist at DB
}
else if (error)
{
// showAlertOfSomethingWentWrong
}
}];
}
this is FBSDKGraphRequest :
-(void)handleNewUser
{
FBSDKGraphRequest *request = [[FBSDKGraphRequest alloc] initWithGraphPath:#"me" parameters:#{#"fields": #"friends, first_name, gender, last_name, link, name, verified, picture, email"}];
[request startWithCompletionHandler:^(FBSDKGraphRequestConnection *connection, id result, NSError *error)
{
NSMutableDictionary *userData = (NSMutableDictionary *)result;
}];
my problem is that that line :
[PFFacebookUtils logInInBackgroundWithReadPermissions:permissions block:^(PFUser *user, NSError *error)
the run never go into this block in iPhone , in simulator this work fine.
I had a similar problem where I was trying to show a login alert on top of FBSDKContainerViewController.
In this call
- (void)logInWithReadPermissions:(NSArray *)permissions
fromViewController:(UIViewController *)fromViewController
handler:(FBSDKLoginManagerRequestTokenHandler)handler;
Facebook presents its own view controller and if you don't specify the fromViewController, "the topmost view controller will be automatically determined as best as possible."
In your case, it sounds like Facebook is trying to present on top of an alert that was dismissed, even if this is not the call being invoked.
do you add NSAppTransportSecurity & LSApplicationQueriesSchemes key to your info.plist?
I did find a workaround to this, thanks to #PastryPup's answer.
The warning is displayed when trying to present the Facebook login view controller on top of a dismissed alertview. Switching to a UIAlertController, however, fixed the problem.
My impression is that this works because UIAlertController is a full-fledged controller, and thus exists in the view hierarchy even after it has been dismissed.
The solution is to basically replace the UIAlertView with a UIAlertController, and call [PFFacebookUtils logInInBackgroundWithReadPermissions:permissions block:^(PFUser *user, NSError *error) inside the default action of the UIAlerController.
Link on how to implement a UIAlertController
I did some trick to make this work. I don't know if it's the best way to do it. But i just made a boolean that is false before the activity executes (the fb button is tapped). So after it returns, just set the boolean to true and have a condition in the ViewDidAppear that if the boolean is true perform the segue to the next window you want to go.
I hope it helps!
I am having a problem using the new Facebook SDK invite system. The view controller which appears when using FBSDKAppInviteDialog displays very oddly, then disappears before the user has time to interact with it.
FBSDKAppInviteContent *content =[[FBSDKAppInviteContent alloc] init];
content.appLinkURL = [NSURL URLWithString:#"http://example.com"];
//content.appInvitePreviewImageURL = [NSURL URLWithString:#"https://www.example.com/my_invite_image.jpg"];
FBSDKAppInviteDialog *inviteDialog = [[FBSDKAppInviteDialog alloc] init];
if ([inviteDialog canShow]) {
inviteDialog.content = content;
inviteDialog.delegate = self;
[inviteDialog show];
}
This is what appears on the screen when the modal launches:
Does anyone know of any reason this would happen?
Edit[Sep 8, 2015 15:15 EDT]: We are currently getting an error code of 1; unknown error. Here is the exact print out:
Failed to perform app invite: The operation couldn’t be completed. (com.facebook.Facebook.platform error 1.)
Full Report => {
}
We thought it might be that we don't have friend permissions or something to do with iOS 9; however we are running our test on iOS 8, and we added friend permissions, and we have not seen a change.
I would call the validateWithError function they provided. It might give an idea of what the issue is.
FBSDKAppInviteDialog documentation
In my app, I am reading the calendar as soon as the app launches. So for the first time as per the guidelines my app asks for calendar access but what's happening is it never shows up and all i can see is the splash screen.
When i close the app i see the popup for granting access to calendar on my phone. I grant access to it and after that point everything works fine.
Here's my code :
-(NSArray*) listOfEventsInCalendar
{
_eventStore = [[EKEventStore alloc]init];
__block NSArray * events;
if ([_eventStore respondsToSelector:#selector(requestAccessToEntityType:completion:)])
{
dispatch_semaphore_t sema = dispatch_semaphore_create(0);
/* iOS Settings > Privacy > Calendars > MY APP > ENABLE | DISABLE */
[_eventStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error)
{
if ( granted )
{
NSDate * startDate = [NSDate date];
NSDate * endDate = [NSDate distantFuture];
NSPredicate * predicate = [_eventStore predicateForEventsWithStartDate:startDate endDate:endDate calendars:nil];
events = [_eventStore eventsMatchingPredicate:predicate];
if (events) {
insideArray = [self castEvents:events];
}
else{
NSLog(#"No Events found in the calendar");
}
}
else
{
NSLog(#"User has not granted permission!");
}
dispatch_semaphore_signal(sema);
}];
dispatch_semaphore_wait(sema, DISPATCH_TIME_FOREVER);
}
return insideArray;
}
What am i doing wrong??
Thanks,
The problem is simple. You are blocking the main thread with your semaphore. This is a bad idea.
Don't have your listOfEventsInCalendar block. Let it return immediately. Let the completion block handle the result. One option would be to add a block parameter to your listOfEventsInCalendar method. Then call the block from the completion block passing the obtained array.
I've recently found out, that I do not receive any EKCalendar objects from EKEventStore in iOS7. In iOS 6.x.x there are no problems with same code snippet. When I'm trying to access defaultCalendarForNewEvents - I do receive a single EKCalendar object (as expected).
I do request access to entity type EKEntityTypeEvent.
The snippet:
__block NSMutableArray *calendarsArray = nil;
if ([self.eventsStore respondsToSelector:#selector(requestAccessToEntityType:completion:)]) {
[self.eventsStore requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
if (granted) {
EKAuthorizationStatus status = [EKEventStore authorizationStatusForEntityType:EKEntityTypeEvent];
if (status == EKAuthorizationStatusAuthorized) {
calendarsArray = [[NSMutableArray alloc] initWithArray:[self.eventsStore calendarsForEntityType:EKEntityMaskEvent]];
}
} else {
UIAlertView *alert = [[UIAlertView alloc] initWithTitle:#"Error!" message:#"You haven't granted access to calendars. Expected things are not going to happen." delegate:nil cancelButtonTitle:#"I understand" otherButtonTitles:nil];
[alert show];
}
}];
} else {
calendarsArray = [NSMutableArray arrayWithArray:[self.eventsStore calendarsForEntityType:EKEntityTypeEvent]];
}
I do receive 0 objects into calendarsArray. I've also tried to get it by "running through" all EKSources that are of type Local or CalDAV ([source calendarsForEntityType:] - got the same empty (0 object containing) set).
By the way - access to calendars IS granted.
Any suggestions?
After a brief investigation I have found out, that the problem was not in the code. It appears that the problem is in iOS 7.0.3 itself.
After deleting all the sync'ed calendars from the iDevice and adding it back all of the calendars were displayed both within the native Calendar application, and the one I made. After taking this action my code was able to retrieve the calendars from EventStore not depending on the method I would access calendars (via EKSources or EKEventStore itself).