I'm trying to lookup a calendar event by the new iOS method calendarItemWithIdentifier. I can't use the eventWithIdentifier because the identifier is changed after the event is syncronized with the server. The calendarItemIdentifier is not.
But the calendarItemWithIdentifier always returns (null).
EKEventStore *store = [[EKEventStore alloc] init];
[store requestAccessToEntityType:EKEntityTypeEvent completion:^(BOOL granted, NSError *error) {
if (granted) {
// Create event.
EKEvent *event = [EKEvent eventWithEventStore:store];
event.title = self.title;
event.startDate = [[NSDate date] dateByAddingTimeInterval:3600];
event.endDate = [[NSDate date] dateByAddingTimeInterval:7200];
event.timeZone = [NSTimeZone defaultTimeZone];
event.calendar = [store defaultCalendarForNewEvents];
BOOL success = [store saveEvent:event span:EKSpanThisEvent commit:YES error:&error];
if (success)
{
NSString *calendarItemIdentifier = event.calendarItemIdentifier;
NSLog(#"Assigned identifier: %#", calendarItemIdentifier);
// Look up the event in the calendar.
event = (EKEvent *)[store calendarItemWithIdentifier:calendarItemIdentifier];
if (event) {
NSLog(#"FOUND");
} else {
NSLog(#"NOT FOUND");
}
}
}
}];
From the log:
2013-01-13 10:32:52.042 CalendarIntegration[6095:1303] Assigned identifier: C5FD3792-EBF1-4766-B27D-2767E5C8F3BE
2013-01-13 10:32:52.043 CalendarIntegration[6095:1303] NOT FOUND
Help would be appreciated.
According the doc, this behavior is as expected, link,
A full sync with the calendar will lose this identifier. You should have a plan for dealing with a calendar whose identifier is no longer fetch-able by caching its other properties.
Related
I execute the following code to let the user choose multiple calendars to use for my notepad app. Until iOS 10.3.1, there was no problem. On 11.0.2, it was still working on actural devices. However, since 11.1 it crashes with the following error.
*** Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[__NSDictionaryM setObject:forKeyedSubscript:]: key cannot be nil'
The code is as follows. Basically, I'm opening a blank calendar chooser.
if (_eventStore == nil) {
_eventStore = [[EKEventStore alloc] init];
}
// the selector is available, so we must be on iOS 6 or newer
[_eventStore requestAccessToEntityType:EKEntityTypeEvent
completion:^(BOOL granted, NSError *error) {
dispatch_async(dispatch_get_main_queue(), ^{
if (error)
{
// display error message here
}
else if (!granted)
{
// display access denied error message here
}
else
{
// access granted
EKCalendarChooser *calendarChooser = [[EKCalendarChooser alloc]
initWithSelectionStyle:EKCalendarChooserSelectionStyleMultiple
displayStyle:EKCalendarChooserDisplayAllCalendars
eventStore:_eventStore];
calendarChooser.modalTransitionStyle = UIModalTransitionStyleCoverVertical;
calendarChooser.delegate = self;
calendarChooser.showsCancelButton = YES;
calendarChooser.showsDoneButton = YES;
NSSet *calendarSet = [[NSSet alloc] init];
calendarChooser.selectedCalendars = calendarSet;
UINavigationController *sub = [[UINavigationController alloc] initWithRootViewController:calendarChooser];
sub.navigationBar.barStyle = UIBarStyleDefault;
sub.toolbar.barStyle = UIBarStyleDefault;
[self presentViewController:sub animated:YES completion:nil];
//ios11 crashes after this
}
});
}];
Thanks for your help.
It turns out that EKCalendarChooserDisplayAllCalendars was causing the crash. Although it's not ideal, now I can avoid the crash when iOS is 11.1 or higher.
EKCalendarChooserDisplayStyle displayStyle = EKCalendarChooserDisplayAllCalendars;
if (#available(iOS 11.1, *)) {
displayStyle = EKCalendarChooserDisplayWritableCalendarsOnly;
}
EKCalendarChooser *calendarChooser = [[EKCalendarChooser alloc]
initWithSelectionStyle:EKCalendarChooserSelectionStyleMultiple
displayStyle:displayStyle
eventStore:eventStore];
I have an iOS app where Users have to Register before using the app. I have created the UI in Storyboard and I am reading the Users details from the UITextfields. I then send the details to the Register API that sends back a JSON response. I am using NSURLConnection for the communication.
Here is the response I am receiving from the test URL - for testing purposes only:
{"username":"Hans","password":"Hans"}
However, when I try to read the password to make sure that the user doesn't already exists (again, just for testing purposes), I get nil returned for password value returned.
In my .h file I declare the Data and Connection:
#interface RegisterViewController : UIViewController <NSURLConnectionDataDelegate>
{
// Conform to the NSURLConnectionDelegate protocol and declare an instance variable to hold the response data
NSMutableData *buffer;
NSURLConnection *myNSURLConnection;
}
In my .m file, when someone clicks the REGISTER button, I create the request and start the connection as below. I am giving a dummy URL in example but the response I am receiving is:
{"username":"Hans","password":"Hans"}
- (IBAction)registerButtonClicked:(id)sender
{
NSMutableURLRequest *request = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:#"http://myDummyURL/Login.php"]];
// Construct the JSON Data
NSDictionary *stringDataDictionary = #{#"firstname": firstname, #"lastname": lastname, #"email": email, #"password" : password, #"telephone" : telephone};
NSError *error;
NSData *requestBodyData = [NSJSONSerialization dataWithJSONObject:stringDataDictionary options:0 error:&error];
// Specify that it will be a POST request
[request setHTTPMethod:#"POST"];
// Set header fields
[request setValue:#"text/plain" forHTTPHeaderField:#"Accept"];
[request setValue:#"application/json; charset=utf-8" forHTTPHeaderField:#"Content-Type"];
//NSData *requestBodyData = [stringData dataUsingEncoding:NSUTF8StringEncoding];
[request setHTTPBody:requestBodyData];
myNSURLConnection = [NSURLConnection connectionWithRequest:request delegate:self];
// Ensure the connection was created
if (myNSURLConnection)
{
// Initialize the buffer
buffer = [NSMutableData data];
// Start the request
[myNSURLConnection start];
}
}
The connection is created with no problem here.
In my .m file I implement the delegate methods and in connectionDidFinishLoading() I try and read the returned JSON. Below is the code I am using for this.
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// Dispatch off the main queue for JSON processing
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSError *error = nil;
NSString *jsonString = [[NSJSONSerialization JSONObjectWithData:buffer options:0 error:&error] description];
// Dispatch back to the main queue for UI
dispatch_async(dispatch_get_main_queue(), ^{
// Check for a JSON error
if (!error)
{
NSError *error = nil;
NSArray *jsonArray = [NSJSONSerialization JSONObjectWithData:[jsonString dataUsingEncoding:NSUTF8StringEncoding] options:NSJSONReadingMutableContainers error:&error];
NSDictionary *dictionary = [jsonArray objectAtIndex:0];
NSString *test = [dictionary objectForKey:#"password"];
NSLog(#"Test is: %#", test);
}
else
{
NSLog(#"JSON Error: %#", [error localizedDescription]);
}
// Stop animating the Progress HUD
});
});
}
From the log screen grab below you can see the jsonString returned has values but the jsonArray is always nil. The error reads: error NSError * domain: #"NSCocoaErrorDomain" - code: 3840 0x00007ff158498be0
Thanks in advance.
Your jsonString is in fact the NSDictionary object - created by NSJSONSerialization - you're looking for, there is no array. In a JSON string curly braces {} represent a dictionary and square brackets [] represent an array
Try this
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// Dispatch off the main queue for JSON processing
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSError *error = nil;
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:buffer options:0 error:&error];
// Dispatch back to the main queue for UI
dispatch_async(dispatch_get_main_queue(), ^{
// Check for a JSON error
if (!error)
{
NSString *test = [dictionary objectForKey:#"password"];
NSLog(#"Test is: %#", test);
}
else
{
NSLog(#"JSON Error: %#", [error localizedDescription]);
}
// Stop animating the Progress HUD
});
});
}
Edit: I overlooked the description method at the end of the NSJSONSerialization line. Of course that must be deleted.
Your code has 2 issues:
You are converting JSON server response to NSString.
Your JSON data is indeed a NSDictionary.
This must fix your issue:
- (void)connectionDidFinishLoading:(NSURLConnection *)connection
{
// Dispatch off the main queue for JSON processing
dispatch_async(dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_DEFAULT, 0), ^{
NSError *error = nil;
NSDictionary *dictionary = [NSJSONSerialization JSONObjectWithData:buffer options:0 error:&error];
// Dispatch back to the main queue for UI
dispatch_async(dispatch_get_main_queue(), ^{
// Check for a JSON error
if (!error)
{
NSString *test = [dictionary objectForKey:#"password"];
NSLog(#"Test is: %#", test);
}
else
{
NSLog(#"JSON Error: %#", [error localizedDescription]);
}
// Stop animating the Progress HUD
});
});
}
I am using TAPKU calendar in my iOS application. I want to add some more marks on date tile after loading complete data in tapku calendar.
I am getting some additional data from async process and I want to mark that data also on calendar.
How can i do this. Thanks in advance.
This is indeed possible, as an example for the day calendar view, modify _refreshDataPageWithAtIndex to be like this:
- (void) _refreshDataWithPageAtIndex:(NSInteger)index{
UIScrollView *sv = self.pages[index];
TKTimelineView *timeline = [self _timelineAtIndex:index];
CGRect r = CGRectInset(self.horizontalScrollView.bounds, HORIZONTAL_PAD, 0);
r.origin.x = self.horizontalScrollView.frame.size.width * index + HORIZONTAL_PAD;
sv.frame = r;
timeline.startY = VERTICAL_INSET;
for (UIView* view in sv.subviews) {
if ([view isKindOfClass:[TKCalendarDayEventView class]]){
[self.eventGraveYard addObject:view];
[view removeFromSuperview];
}
}
if(self.nowLineView.superview == sv) [self.nowLineView removeFromSuperview];
if([timeline.date isTodayWithTimeZone:self.timeZone]){
NSDate *date = [NSDate date];
NSDateComponents *comp = [date dateComponentsWithTimeZone:self.timeZone];
NSInteger hourStart = comp.hour;
CGFloat hourStartPosition = hourStart * VERTICAL_DIFF + VERTICAL_INSET;
NSInteger minuteStart = round(comp.minute / 5.0) * 5;
CGFloat minuteStartPosition = roundf((CGFloat)minuteStart / 60.0f * VERTICAL_DIFF);
CGRect eventFrame = CGRectMake(self.nowLineView.frame.origin.x, hourStartPosition + minuteStartPosition - 5, NOB_SIZE + self.frame.size.width - LEFT_INSET, NOB_SIZE);
self.nowLineView.frame = eventFrame;
[sv addSubview:self.nowLineView];
}
if(!self.dataSource) return;
timeline.events = [NSMutableArray new];
[self.dataSource calendarDayTimelineView:self eventsForDate:timeline.date andEvents:timeline.events success:^{
[timeline.events sortUsingComparator:^NSComparisonResult(TKCalendarDayEventView *obj1, TKCalendarDayEventView *obj2){
return [obj1.startDate compare:obj2.startDate];
}];
[self _realignEventsAtIndex:index];
if(self.nowLineView.superview == sv)
[sv bringSubviewToFront:self.nowLineView];
}];
}
and then change your eventsForDate function to look like this:
- (void) calendarDayTimelineView:(TKCalendarDayView*)calendarDayTimeline eventsForDate:(NSDate *)eventDate andEvents:(NSMutableArray *)events success:(void (^)())success {
[Model doSomethingAsync andSuccess:^(NSArray *classes) {
// .. Add stuff to events..
success();
}];
}
I'm assuming the pattern for the other controls is very similar. The premise is you're waiting to continue the formatting/layout flow til you get your data.
we try to implement the nice local notifications extension of distriqt.
With the deactivate event new notifications were set:
notification.id = int(Math.random()*100);
notification.tickerText = _asde + " asdasd!";
notification.title = _asde + " asd!";
notification.body = "asd!";
notification.iconType = NotificationIconType.APPLICATION;
notification.count = 0;
notification.repeatInterval = 0;
notification.vibrate = false;
notification.playSound = true;
notification.soundName = "assets/sounds/notification.mp3";
notification.delay = secondsToDeath;
notification.data = "Some notification data to attach "+notification.id;
try
{
Notifications.service.notify( notification.id, notification );
_count ++;
Notifications.service.setBadgeNumber( _count );
}
catch (e:Error)
{
}
If the user clicks on the app and deactivates it again, new notifications were set.
The old notifications are still available and are displayed but we want the old to be deleted.
We haven't found a method to unregister the old notifications.
Any idea?
private static const DEACTIVATE_NOTIFICATION_ID_4 : int = 4;
Is declared.
if(_GoodA == true){
setSpielenFertigDate.time = 2400000*(1-_aktuellerFreudeWert/_maximalerFreudeWert);
var secondsToSpielenFertig:int = int((setSpielenFertigDate.time)/ 1000);
trace("halloe" + _fernseherAn.toString());
notification4.id = DEACTIVATE_NOTIFICATION_ID_4;
notification4.tickerText = "He test it!";
notification4.title = "sdf is happy!";
notification4.body = "sdf test is on!";
notification4.iconType = NotificationIconType.APPLICATION;
notification4.count = 0;
notification4.repeatInterval = 0;
notification4.vibrate = false;
notification4.playSound = true;
notification4.soundName = "assets/sounds/notification.mp3";
notification4.delay = secondsToSpielenFertig;
notification4.data = "Some notification data to attach "+ notification4.id;
try
{
Notifications.service.notify( notification4.id, notification4 );
_count ++;
Notifications.service.setBadgeNumber( _count );
}
catch (e:Error)
{
}
}
else{
trace("halloe2" + _fernseherAn.toString());
setSpielenDate.time = 5100000*(_aktuellerFreudeWert/_maximalerFreudeWert);
var secondsToSpielen:int = int((setSpielenDate.time)/ 1000);
notification4.id = DEACTIVATE_NOTIFICATION_ID_4;
notification4.tickerText = "He tested it!";
notification4.title = "sdf is unhappy!";
notification4.body = "sdf test is off!";
notification4.iconType = NotificationIconType.APPLICATION;
notification4.count = 0;
notification4.repeatInterval = 0;
notification4.vibrate = false;
notification4.playSound = true;
notification4.soundName = "assets/sounds/notification.mp3";
//Sekunden bis Nachricht geschickt wird
notification4.delay = secondsToSpielen;
notification4.data = "Some notification data to attach "+notification4.id;
try
{
Notifications.service.notify( notification4.id, notification4 );
_count ++;
Notifications.service.setBadgeNumber( _count );
}
catch (e:Error)
{
}
}
If the deactivate event of the app is fired it traces the correct part of the if and else clause. But it won't update the body and title...
There are two ways to do this with our extension. Both involve tracking the ID of the notification.
The first is to track your last notification and "cancel" it from the notification area. To do this you need to store at least the ID of the last created notification. The part of the code that you're probably interested in is the cancel function, this removes a notification from the notification panel by specifying the id of the notification to remove.
Somewhere in your class declare a reference to the last notification:
private var _lastNotification : Notification;
Then in your deactivate handler:
var notification:Notification = new Notification();
notification.id = int(Math.random()*100);
notification.tickerText = "Deactivated";
notification.title = "TEST";
notification.body = "Application Deactivated";
if (_lastNotification != null)
Notifications.service.cancel( _lastNotification.id );
Notifications.service.notify( notification.id, notification );
// Set this to be the recent notification displayed
_lastNotification = notification;
The second option is to use a single notification id for all of your deactivate notifications. In this case you choose a constant ID to use for your notification and update the notification as required. The notification manager will not show an additional notification but simply update the one with the specified ID (or show it if it has been closed by the user).
private static const DEACTIVATE_NOTIFICATION_ID : int = 10;
var notification:Notification = new Notification();
notification.id = DEACTIVATE_NOTIFICATION_ID;
notification.tickerText = "Deactivated";
notification.title = "TEST";
notification.body = "Application Deactivated";
Notifications.service.notify( notification.id, notification );
Hope this helps!
Using iOS6:
I would like to retrieve the text entered by a user into a UITextField associated with the UIAlertView. I am aware that I could achieve the desired result with a delegate however I am curious about solving this issue with a callback function as I believe this may be an interesting pattern. I began by examining a common pattern for category extension of the UIAlertView class. Code below. Thanks in advance for any suggestions.
import <UIKit/UIKit.h>
#interface UIAlertView (Block)
- (id)initWithTitle:(NSString *)title message:(NSString *)message completion:(void (^)(BOOL cancelled, NSInteger buttonIndex, UITextField *textField))completion cancelButtonTitle:(NSString *)cancelButtonTitle otherButtonTitles:(NSString *)otherButtonTitles, ... NS_REQUIRES_NIL_TERMINATION;
#end
The .m for the category follows:
#import "UIAlertView+Block.h"
#import <objc/runtime.h>
static char const * const alertCompletionBlockTag = "alertCompletionBlock";
#implementation UIAlertView (Block)
- (id)initWithTitle:(NSString *)title
message:(NSString *)message
completion:(void (^)(BOOL cancelled, NSInteger buttonIndex))completion
cancelButtonTitle:(NSString *)cancelButtonTitle
otherButtonTitles:(NSString *)otherButtonTitles, ... {
self = [self initWithTitle:title message:message delegate:self cancelButtonTitle:cancelButtonTitle otherButtonTitles:nil ];
if (self) {
objc_setAssociatedObject(self, alertCompletionBlockTag, completion, OBJC_ASSOCIATION_COPY);
va_list _arguments;
va_start(_arguments, otherButtonTitles);
for (NSString *key = otherButtonTitles; key != nil; key = (__bridge NSString *)va_arg(_arguments, void *)) {
[self addButtonWithTitle:key];
}
va_end(_arguments);
}
[self setAlertViewStyle:UIAlertViewStylePlainTextInput];
return self;
}
- (void)alertView:(UIAlertView *)alertView didDismissWithButtonIndex:(NSInteger)buttonIndex {
id completion = objc_getAssociatedObject(self, alertCompletionBlockTag);
[self complete:completion index:buttonIndex];
}
- (void) complete:(void (^)(BOOL cancelled, NSInteger buttonIndex))block index:(NSInteger)buttonIndex {
BOOL _cancelled = (buttonIndex == self.cancelButtonIndex);
block(_cancelled, buttonIndex );
objc_setAssociatedObject(self, alertCompletionBlockTag, nil, OBJC_ASSOCIATION_COPY);
//objc_removeAssociatedObjects(block);
}
#end
Usage for the category is set below. The main problem is my inability to reference the UIAlertView textField at Index 0 from within the completion block.
[[[UIAlertView alloc] initWithTitle:#"Add"
message:#"Add New Asset Type"
completion:^(BOOL cancelled, NSInteger buttonIndex){
if (!cancelled) {
//call on completion of UISheetAction ???
NSLog(#"%#",needToAccessUIAlertView._textFields[0]);
}
}
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"OK", nil] show];
So basically you want to access the alert view from the block. You can do something like this:
__block __weak UIAlertView *alertViewWeak;
UIAlertView *alertView = [[UIAlertView alloc] initWithTitle:#"Add"
message:#"Add New Asset Type"
completion:^(BOOL cancelled, NSInteger buttonIndex){
if (!cancelled) {
//call on completion of UISheetAction ???
NSLog(#"%#",[alertViewWeak textFieldAtIndex:0]);
}
}
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"OK", nil];
alertViewWeak = alertView;
[alertView show];
If you want to make a category by yourself, above is good enough.
But, there are many classes that uses delegation pattern. Do you want to make categories one by one?
There is REKit. With it, you can use that classes as if they were Block-based:
UIAlertView *alertView;
alertView = [[UIAlertView alloc]
initWithTitle:#"title"
message:#"message"
delegate:nil
cancelButtonTitle:#"Cancel"
otherButtonTitles:#"OK", nil
];
[alertView
respondsToSelector:#selector(alertView:didDismissWithButtonIndex:)
withKey:nil
usingBlock:^(id receiver, UIAlertView *alertView, NSInteger buttonIndex) {
// Do something…
}
];
alertView.delegate = alertView;
Try this library Here is another useful library to do the same. http://ichathan.com/2014/08/19/ichalertview/