How to make UILocalNotification not repeat in swift? - ios

I'm a newbie in swift language. I am using UILocalNotification to make a local notification. In Objective-C, I can sent repeatInterval to not repeat by assigning it to 0.
UILocalNotification* alarm = [[UILocalNotification alloc] init];
alarm.repeatInterval = 0;
But when I assign 0 to repeatInterval in Swift I got an error.
let alarm : UILocalNotification = UILocalNotification()
alarm.repeatInterval = 0
If you have any idea to fix it, please tell me.

You could likely set it to zero like this:
alarm.repeatInterval = NSCalendarUnit(rawValue: 0)
This way you are properly casting the value.

Related

Subtract minutes from time picker value?

I have being wondering these days how can I subtract 3, 10 and 20 minutes from selected time from time picker in Swift 3. So for example when the user selects the start time of an event, to be able to receive notification 3, 10 or 20 minutes before the selected from the picker time value. I have tried
let value:TimeInterval = 1187.5
let periodComponents = startTimePicker.date.addingTimeInterval(-value)
for the 20 minutes earlier,
but I'm getting the value 2 hours behind(may be the console is showing me the GTM time only). Is it possible to schedule notification from "periodComponens" to repeat every week on the selected time on specific day of the week? Or I should use other subtracting method?
Use the built-in Calendar class:
let threeMinutesEarlier = Calendar.current.date(byAdding: .minute, value: -3, to: myPicker.date)
There is nothing wrong with your code.
The xcode console just prints out GMT date time by default. If you use NSDateFormatter to convert your NSDate into NSString and print it out, the result will be as you expected
If you use UILocalNotification and wish to schedule it to fire weekly you can set its provided property repeatInterval to NSCalendarUnitWeekOfYear.
For example:
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
NSDate *fireTime; // Your desired date time.
localNotif.fireDate = fireTime;
localNotif.repeatInterval = NSCalendarUnitWeekOfYear;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];

How do I change the displayed time format after NSTimer is initiated?

I'm starting out my iOS experience with a project that incorporates a large clock as a portion of the user interface. When I try to incorporate a user-adjustable toggle to switch from 12-hr to 24-hr time format, I get issues. After the initial user change of the on-screen toggle, the displayed time "flickers" between the 2 formats.
My approach:
viewDidLoad sets the attribute used for 12-hr time format and calls the startTimer method
startTimer method: NSDateFormatter is set. NSTimer is initiated with scheduled interval, passing the DateFormatter to the upDateTime method
updateTimer method checks current time and sets the on-screen
If the user, clicks the on-screen 12-hr/24-hr segmented control, the IBAction invalidates the timer and passes the desired time format attribute to the startTimer method
Testing/Observations:
Code below shows an approach where I modified the time format by setting the Locale and passing it to the DateFormatter. I thought this might be an issue conflicting with the device Locale. I tried using a custom Date Formatter (example #"hh:mm a") instead and had the same flickering issue.
NSTimer scheduled interval shows as 1 sec. I have this for testing, but saw same behavior at 0.1 sec.
NSLog calls within the updateTime method show that the DateFormatter object id and the displayed time are changing between successive loops, even though the user did not adjust the toggle switch.
I suspect this may have something to do with how the Timer is initiated and stopped. Perhaps the old "calls" with the previous format is still running in the background? I experimented with moving the [updateTimer invalidate] to a couple of places in the methods without success
Root Question:
Any suggestions or better approach to having an on-screen clock with a user switch for 12-hr vs. 24-hr time format? Any reason why the displayed time format keeps cycling?
Code:
- (void)startTimer:(NSString *)displayedClockMode {
// using locale within formatter overrides device system behavior chosen by user
NSString *localeValue = nil;
NSDateFormatter *timeFormatter = [[NSDateFormatter alloc] init];
if([displayedClockMode isEqual:#"12-hr"]){
//then 12 hr format - based on US locale
localeValue = #"en_US_POSIX";
//[timeFormatter setDateFormat:#"hh:mm a"];
}
else { //assume no other value exists
// 24 hr format - based on GB locale
localeValue = #"en_GB";
//[timeFormatter setDateFormat:#"HH:mm"];
}
NSLocale *clockLocale = [NSLocale localeWithLocaleIdentifier:localeValue];
[timeFormatter setLocale:clockLocale];
[timeFormatter setTimeStyle:NSDateFormatterMediumStyle];
//stop timer before re-starting with new format
[self.updateTimer invalidate];
self.updateTimer = nil;
NSTimer *updateTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateTime:) userInfo:timeFormatter repeats:YES];
}
- (void)updateTime:(NSTimer *)updateTimer {
NSDate *currentTime = [NSDate date];
NSLog(#"old display time: %#",self.displayedTime.text);
self.displayedTime.text = [updateTimer.userInfo stringFromDate:currentTime];
NSLog(#"new display time: %#",self.displayedTime.text);
NSLog(#"new timeformatter: %#",updateTimer.userInfo);
}
- (IBAction)displayedTimeMode:(id)sender {
[self.updateTimer invalidate];
self.updateTimer = nil;
NSString *timeFormat = nil;
if(self.displayedTimeToggle.selectedSegmentIndex == 0){
//if 0, then 12 hr format
timeFormat = #"12-hr";
}
else {
// is 1, 24 hr format
timeFormat = #"24-hr";
}
[self startTimer:timeFormat];
}
The problem is that you have two different updateTimers -- self.updateTimer the class variable and updateTimer the local variable. You're invalidating the class variable, but initializing and running multiple local NSTimers with different locales during each call to startTimer. That's why you see this "flickering" -- it's because multiple NSTimers are setting the label using different localeValues.
To fix this, change:
NSTimer *updateTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateTime:) userInfo:timeFormatter repeats:YES];
to
self.updateTimer = [NSTimer scheduledTimerWithTimeInterval:1 target:self selector:#selector(updateTime:) userInfo:timeFormatter repeats:YES];

Best Way to Handle Local Notifications

I have the following problem:
I have 2 calendars which both need to create local notifications (which will fire 5 min before each event). In the settings the user has the ability to turn notifications on or off for either calendar. If the user was originally using the notifications for both calendars and now wants to only use notifications for one calendar, how can I delete only the notifications in one calendar?
I think I have 3 options:
Running [[UIApplication sharedApplication] cancelAllLocalNotifications]; and then add back all the ones in the other calendar (this will probably be harder than it sounds).
Storing arrays of created notifications in something like the user defaults and then looping through the arrays calling: [[UIApplication sharedApplication] cancelLocalNotification:notification];
Subclassing UILocalNotification and adding some field which will allow me to sort the notifications. Then maybe I could call [[UIApplication sharedApplication] scheduledLocalNotifications] and loop through that checking that field and deleting those which are necessary.
Is there a standard way of doing this? I think the third is probably the easiest, but I'm not sure if it would work.
UILocalNotification has a standard userInfo property that is a NSDictionary of arbitrary values as long as the keys are valid property-list types. If you subclass UILocalNotification, you must use that dictionary as the backing store for additional properties or fields you wish to persist. And to be able to use your subclass, you will need an initialization method that will copy the properties from the base class to your subclass.
#define kNoficationCalendarName NSStringFromSelector(#selector(calendarName))
#interface XXLocalNotification : UILocalNotification
#property (nonatomic, strong) NSString * calendarName;
- (instancetype)initWithLocalNotification:(UILocalNotification *)notification;
#end
#implementation XXLocalNotification
- (instancetype)initWithLocalNotification:(UILocalNotification *)notification
{
self = [super init];
if (self)
{
//Copy properties
self.alertAction = notification.alertAction;
self.alertBody = notification.alertBody;
self.alertLaunchImage = notification.alertLaunchImage;
self.applicationIconBadgeNumber = notification.applicationIconBadgeNumber;
self.fireDate = notification.fireDate;
self.hasAction = notification.hasAction;
self.repeatCalendar = notification.repeatCalendar;
self.repeatInterval = notification.repeatInterval;
self.soundName = notification.soundName;
self.timeZone = notification.timeZone;
self.userInfo = notification.userInfo;
}
return self;
}
-(void)setCalendarName:(NSString *)calendarName
{
NSMutableDictionary * userInfo = [[self userInfo] mutableCopy];
[userInfo setValue:calendarName
forKey:kNoficationCalendarName];
}
- (NSString *)calendarName
{
return [[self userInfo] valueForKey:kNoficationCalendarName];
}
#end

UILocalNotification show countdown timer on startup or viewdidload

I'm working on app that sets an UILocalNotification. This is the first time I use it and I'd like to have some explanations to use it efficiently and in the best way.
Which is the bast way to countdown to the time of notification expiration? I'd like to show to the user an UILabel with the missing time, this UILabel updates every second and when I reopen my app it must show the timer.
I've done this in this way:
When I reopen my app or the recall the view that has to show the timer I check if there is a UILocalNotification with the userInfo I've setted previously then I subtract to its NSDate
the current time so I get the difference, and update the UILabel. I know it shows time in seconds but if this procedure it's good I will convert it into time.
Is there any best way to do this?
Here is my code:
UILocalNotification *countDownNotificationSetted;
NSArray *scheduledNotificationArray = [[UIApplication sharedApplication] scheduledLocalNotifications];
for (int i = 0; i<[scheduledNotificationArray count]; i++)
if ([[[[scheduledNotificationArray objectAtIndex:i] userInfo] objectForKey:#"ID"] isEqualToString:#"ParkNotification"]) {
countDownNotificationSetted = [scheduledNotificationArray objectAtIndex:i];
break;
}
NSDate *expringDate = [countDownNotificationSetted fireDate];
NSTimeInterval timeLeftToNotification = [expringDate timeIntervalSinceNow];
if(timeLeftToNotification == 0){
[countDownTimer invalidate];
}
datePicker.countDownDuration = timeLeftToNotification;
_countDownLabel.text = [NSString stringWithFormat:#"%.0f", timeLeftToNotification];
NSLog(#"%.0f", timeLeftToNotification);
I think it would be better if you would update your label with a timer as you already do , when you close the app, store that time locally in a file or user defaults and when the app launches again read from that file the time. Or if you want the time passed since the first openning of the app, just write the time in appDidFinishLaunching and get it from there every time . I don't see the benefit of the local notification here.
Hope this helps.
Cheers!

NSDate crash with successive actions

I have the following code below that is meant to change a class var called "today" forward or backward by one day. It will work one time but then after that it crashes. It will do the same no matter if I press the left button or right button. What am I doing wrong?
the var today is a class var initiated as .. today = [NSDate date]
Here is the method that crashes:
(IBAction)changeDateByOne:(id)sender{
NSDate *newDay;
NSDate *currentDay = today;
NSTimeInterval secondsPerDay = 24 * 60 * 60;
if(sender == leftButton){
newDay = [currentDay addTimeInterval:-secondsPerDay];
}else if(sender == rightButton) {
newDay = [currentDay addTimeInterval: secondsPerDay];
}
today = newDay;
}
Not only do you need to retain the date created, but you also need to release the existing value held by "today," otherwise you'll leak the old reference.
When initializing the instance, use:
today = [[NSDate date] retain];
I would change the last line to:
[today release];
today = [newDay retain];
And finally, in your dealloc method, add:
[today release];
before calling [super dealloc];
You need to read the memory management documentation. That’s here:
http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/MemoryMgmt/MemoryMgmt.html
If you’re adding or subtracting days, you might want to read this which is an alternative way of doing the same thing:
http://developer.apple.com/iphone/library/documentation/Cocoa/Conceptual/DatesAndTimes/Articles/dtCalendricalCalculations.html#//apple_ref/doc/uid/TP40007836-SW1
Lastly, if something crashes, it’s often helpful to look at the back traces (and include them in your questions if you can’t figure it out for yourself). Memory management bugs are usually the problem if you see objc_msgSend (or one of its companions) in the trace.
Maybe you need to say
today = [[NSDate date] retain]
I think you need to retain the newDay object returned from the addTimeInterval method. You may also need to release today before you do the assignment at the end.

Resources