Trying to implement iCloud Calendar synchronization for iOS.
The idea is to create a new calendar from my app and sync it with iCloud when iCloud sync is on actually.
To get corresponding source I'm using the following code:
EKSource* localSource=nil;
for (EKSource* source in self.eventStore.sources)
{
if(source.sourceType == EKSourceTypeCalDAV && [source.title isEqualToString:#"iCloud"])
{
localSource = source;
break;
}
}
Then creating a calendar in that source and saving.
When iCloud sync is on and Calendar synchronization is on for iCloud as well from iPhone->Settings->iCloud->Calendar It's working just fine.
After switching off calendar synchronization from above mentioned settings theoretically it should not allow to create calendar in that store any more. But actually even in that case it allows to get corresponding iCloud store from my application and create/save a new calendar.
After creating a new calendar it's not showing it in iPhone's calendars list. But when you logging in to the iCloud web interface you can see there a lot of calendars with the same name that you have just added. The number of calendars with that name is getting more and more. Seams like there is an infinite loop problem in calendar synchronization for iCloud. So far seams like it's an iOS problem and could not find any report on that anywhere.
Not sure if you figured this out, if you did, post your solution :-)
But it does seem to be a bug - I just checked iCal on my Mac and it's loaded up with duplicate calendars.
Just figuring it out, but some rough code I think I have working is to create a Calendar in the EKSource and then check for that Calendar.
Something like this:
-(BOOL)testCal {
BOOL cal = 0;
NSUInteger counter = 1;
for (EKCalendar *thisCalendar in [[DGEK eventStore]calendars] ){
NSLog(#"%#", thisCalendar.title);
if ([thisCalendar.title isEqualToString:#"YourCalName"]) {
cal = YES;
return cal;
}
counter++;
}
return cal;
}
I think that works. Just doing some more testing at the moment.
If the Cal doesnt exist I'm getting the default source with something like this:
source = [[[self eventStore] defaultCalendarForNewEvents] source];
Hope that helps.
Related
I am using the EKEventStore API to save events to the default calendar using the following:
EKEventStore - saveEvent:span:commit:error:.
Once the event is saved, I store the externalID and localID in my database for future reference using the following:
externalID = [myEvent calendarItemExternalIdentifier]; and
localID = [myEvent eventIdentifier].
The problem I am having is that when I then go back to try and retrieve the event using the following:
[[eventStore calendarItemsWithExternalIdentifier:externalID] firstObject]
OR
[eventStore eventWithIdentifier:localID],
iOS is not able to find my event.
If I run the exact same code, but have my default calendar set to an iCloud calendar, however, everything works correctly.
But if the default calendar is an Exchange calendar, I am getting the following error message:
"Error getting calendar item with UUID [insert externalID here]: Error Domain=EKCADErrorDomain Code=1010 "(null)""
Has anyone encountered this issue?
I have had this code deployed for over 2 years now and users recently reported they weren't able to open appointments created on Exchange calendars. Not sure what changed or when, but I have tested this on iOS 10 and 11, and both have the issue.
Any insight would be greatly appreciated,
Sincerely,
~Arash
Unfortunately not... we were able to work around this issue by including an internal reference ID in the notes of the event. Then, if both the externalEventID and eventID failed to return the event, which is our issue with Exchange, then we fall back to use the start and end date in an EKEventSearchCallback to look for our event.
Basically, it ends up looking like this:
EKEventSearchCallback searchBlock = ^(EKEvent *event, BOOL *stop)
{
if([MyCustomClass event:event
notesMatchesKnownValueDict:knownValueDict])
{
foundEvent = event;
*stop = YES;
}
};
NSPredicate *predicate = [eventStore predicateForEventsWithStartDate:startDate
endDate:endDate
calendars:nil];
[eventStore enumerateEventsMatchingPredicate:predicate usingBlock:searchBlock];
Another solution might be waiting for the event to sync with Exchange. For example, I save the event and keep track of the ID assigned to it. Have a loop check every few seconds for a change. When the change happens, it's on the exchange server and you're good to reference that in your DB.
Try it out, let me know if it works!
I'm trying to create a calendar with EKSourceTypeLocal source. Iterate over the self.eventStore.sources, find the one with sourceType == .Local and attempt to create a new calendar with it.
self.calendar = EKCalendar(forEntityType: EKEntityType.Event, eventStore: self.eventStore)
self.calendar?.title = "My Awesome Calendar"
self.calendar?.source = src // src.sourceType == .Local
print("Created \(self.calendar!)")
do {
try self.eventStore.saveCalendar(self.calendar!, commit: true)
} catch let err as NSError {
print("Whoops: \(err)")
}
That executes without a problem, and allows me to add some events to that calendar as well. However, when I switch to the native Calendar app, this new one is no there, if I query self.eventStore.calendarsForEntityType(EKEntityType.Event) after the above has completed, it's not there either, and if I restart the app, the calendar I created and all its events are nowhere to be found. What's happening?
If you have calendars from other sources active on your device, calendars of type EKSourceTypeLocal will not be visible in the Apple Calendars app.
Additionally, the event store will not show the local calendar when other calendar sources are active.
If you de-activate the other calendars, you should be able to see the saved local calendar in both the Calendars app and the event store.
I’m assuming your app has the necessary permissions obtained using requestAccessToEntityType:completion:.
I want to create a new custom calendar on iOS, in my application.
I search for some explications on how do it work. But my code doesn't work / the calendar is not created
EKEventStore *eventStore = [[EKEventStore alloc] init];
EKSource *theSource = nil;
for (EKSource *source in eventStore.sources) {
if (source.sourceType == EKSourceTypeLocal) {
theSource = source;
break;
}
}
NSString *identifier;
EKCalendar *cal;
if (identifier == nil)
{
cal = [EKCalendar calendarForEntityType:EKEntityTypeEvent eventStore:eventStore];
cal.title = #"MyCustomCalendar";
cal.source = theSource;
[eventStore saveCalendar:cal commit:YES error:nil];
NSLog(#"cal id = %#", cal.calendarIdentifier);
}
else
{
cal = [eventStore calendarWithIdentifier:identifier];
}
I'm not sure if you are completely set on using EKCalendar, but you do not need to to make a calendar in iOS. You can design your own however you want with a UICollectionView or Buttons laid out dynamically. The important part is to make sure all of your dates are arranged correctly, which is not necessarily trivial. But, if you have a good way to get the first weekday for the first day of a given month, you can get it done. I followed this algorithm: http://en.wikipedia.org/wiki/Determination_of_the_day_of_the_week which seemed a bit complicated, but in practice was easy to code and reliable. Otherwise I believe apple has a 'firstWeekday' method/property you can try, but that didn't work for me. Once you have the right first weekday you can enumerate through the days of the rest of the month and put them how you want on your calendar.
It seems like you are unsure how to setup your calendar data with EKCalendar. I found a nice tutorial you may find useful:
http://oleb.net/blog/2012/05/creating-and-deleting-calendars-in-ios/
For more specific questions try the documentation, you can find it with a google search.
Otherwise, what kind of data export are you looking for? iCloud? Local? Are you working with an actual calendar provided or just saving something with all of your calendar data that another application can read?
The one possible way to get this issue is, mismatching calendar source type.
Since you've created the calendar with local source type, system could not find local source if you already synced your iCloud / gmail / exchange calendars.
So my suggestion is don't stick with local source and try to create the calendar in any available source. If you go for this approach, you'll also be ready for solving appropriate issues (like the case iCloud is synced but user has not given permission for iCloud-calendar sync).
I am trying to track my app speed using Google analytics but i could not see anything under app speed in my google analytics account. I have tracked the other parameters like events,crashes and exceptions. For those parameter am able to see the reports generated in my google analytic account. Following is the code what i am using to send event timing.
self.endDate=[NSDate date];
double timeDiff=[_startDate
timeIntervalSinceDate:_endDate];
NSLog(#"timeDiff----%f",timeDiff);
if([[[GAI sharedInstance]defaultTracker] sendTimingWithCategory:category
withValue:timeDiff
withName:#"LoadTime"
withLabel:category]) {
NSLog(#"Succesfully sent load time to GA");
}
Following is the message printed in the console.
GoogleAnalytics 2.0b4 -[GAIDispatcher
dispatchComplete:withStartTime:withRetryNumber:withResponse:withData:withError:]
(GAIDispatcher.m:415) DEBUG: Successfully dispatched hit /GAIHit/p479
(0 retries).
Provide me any sample code if u have.
Please help me in that. Thanks in advance.
I found that the interval had to be a whole number. It's expecting milliseconds but NSTimeInterval is seconds so it tries to send it as "3.1234" but if you convert it to whole milliseconds it will send it as 3123 and you should see results. To convert I used (GA V3)
[tracker send:[[GAIDictionaryBuilder createTimingWithCategory:category interval:#((int)(interval * 1000)) name:name label:label] build]]
Did you happen to see the real time data tab in GA dashboard??? They show the last 30 minutes usage data. Later it updates in your Google analytics dashboard. I have worked with flurry, Google analytics, I find GA is better and faster. Keep trying!!!.
Your implementation seems fine. I don't think it's the problem (and as you get the basic events, it's probably not a problem of initialization). I have the same way to log the timing events (you can find my code below if you want to compare).
What I can tell you is:
1/ It's a "beta" version (yes ok, all things #google are in beta xD), pretty unstable and events take time to be displayed in the administration (I don't see any events for the 18 february yet, for example). At least, more than for a website with similar stats.
2/ I can't display the time events on more than 2 days, or it displays me some errors ^^ (probably too many datas asked for a large timezone)
3/ If there is no label, don't put the category, just set nil. Same for the name. I think they are both optional parameters. And it'll slow down the display of your analytics when you have more stats.
4/ For big set of datas, time events are calculated on part of your visits. But it shouldn't be your problem right now ^^
http://support.google.com/analytics/bin/answer.py?hl=en&answer=1042498
Wait 2 days. If you still don't see anything, try to contact a google analytics representative. Or take the "risk“ to submit it like it is.
My implementation
(in case it could help)
+ (void)trackGoogleTimingInCategory:(NSString *)category withTimeInterval:(NSTimeInterval)time withName:(NSString *)name withLabel:(NSString *)label {
//
if (![ category isKindOfClass:[ NSString class ] ])
return;
NSLog(#"[%#] %# time=%f (%#)", category, name, time, label);
//
if (![ name isKindOfClass:[ NSString class ] ])
name = nil;
if (![ label isKindOfClass:[ NSString class ] ])
label = nil;
//
[ [ [ GAI sharedInstance ] defaultTracker ] sendTimingWithCategory:category withValue:time withName:name withLabel:label ];
}
For the time calculation, I do the same way:
NSTimeInterval timeInterval = [ [ NSDate date ] timeIntervalSinceDate:timeStart ];
You must wait at least 24 hours for data to become available in your Google Analytics dashboard, unless you have a GA Premium account:
http://www.google.com/analytics/premium/features.html
I had the same issue with Google Analytics component for Xamarin on iOS, but got it working using NSNumber.FromInt32:
var start = DateTime.Now;
// do operation
var finish = DateTime.Now;
var timeElapsed = finish.Subtract (start);
GAI.SharedInstance.DefaultTracker
.Send(GAIDictionaryBuilder.CreateTiming(
"Storage",
NSNumber.FromInt32((int)timeElapsed.TotalMilliseconds),
"LoadItems", null)
.Build());
I have an app on the App Store, and I want to make some changes that will not effect users that previously downloaded my app.
Is there a way to determine if the user has previously downloaded my app?
Incase anyone is still wondering, a great solution to this problem (assuming you don't already have it) is using the Keychain, which persists through app installation/uninstalls. This library allows you to access the Keychain using NSDictionary-like syntax.
https://github.com/nicklockwood/FXKeychain
So you could implement a function like this:
-(BOOL)alreadyInstalled
{
NSString *installDate = [[FXKeychain defaultKeychain] objectForKey:#"InstallDate"];
if (!installDate)
{
NSString *newInstallDate = [NSString stringWithFormat:#"%i", [[NSDate date] timeIntervalSince1970]];
[[FXKeychain defaultKeychain] setObject:newInstallDate forKey:#"InstallDate"]
return NO;
}
return YES;
}
I don't know a great way to do this but there are some tricks you can do, e.g.:
Look for some data that your application generates. If the data already exists then it's not an update (or an update that completed previously);
Prepare yourself for this, even if this means issuing an intermediate update to your application, then go back to #1. See: How to tell if an iOS application has been newly installed or updated?