Hi i know this question maybe silly but anyway i ask it now.
i have this function :
- (int)getToday {
NSDateFormatter *dateFormatter = [[NSDateFormatter alloc] init];
dateFormatter.locale = [[NSLocale alloc] initWithLocaleIdentifier:#"fa_IR"];
[dateFormatter setDateFormat:#"dd"];
int day = [[dateFormatter stringFromDate:[NSDate date]] intValue];
NSNotificationCenter *nc = [NSNotificationCenter defaultCenter];
[nc addObserver:self
selector:#selector(handleSysTimeChanged:)
name:NSSystemClockDidChangeNotification
object:nil];
return day;
}
and after that i handle notification like this :
-(void) handleSysTimeChanged: (NSNotification*) notification
{
if (NSSystemClockDidChangeNotification) {
NSLog(#"%i", [self getToday]);
}
}
and i get exactly the changes i want in my NSLOG. BUT after that how can i update my getToday to show new number after notification happened.
I'm new to objective-c and NSNotificationCenter. So don't be mad.
Firstly, as #rmaddy said, you should not observe for notifications inside of getToday. This code here is the problem:
[nc addObserver:self
selector:#selector(handleSysTimeChanged:)
name:NSSystemClockDidChangeNotification
object:nil];
You only want to set up your observer once and if you call getToday on each notification, it's going to be called a ton of times. Repetitive addObserver calls CAN break things. Set this up inside of applicationDidFinishLaunchingWithOptions or your class's initialization method. Also, don't forget to call removeObserver before your application terminates or you'll get some really weird errors.
Secondly, I don't think what you're doing is broken. Based on your provided code, you're getting the current date with [NSDate date] each time that the notification is received. This should provide you with the correct day as desired.
Related
I have app that stores system events to Core Data Database. To perform saving I use MagicalRecord.
So, in my logger class in init I have:
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleDidFinishLaunchingNotification) name:UIApplicationDidFinishLaunchingNotification object:nil];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(handleWillTerminateNotification) name:UIApplicationWillTerminateNotification object:nil];
In handle functions I store simple entity with text and timestamp property:
- (void)handleDidFinishLaunchingNotification
{
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
DBMessage *dbMessage = [DBMessage createEntityInContext:localContext];
dbMessage.text = #"Did finish launching";
dbMessage.timestamp = [NSDate date];
}];
}
- (void)handleWillTerminateNotification
{
[MagicalRecord saveWithBlock:^(NSManagedObjectContext *localContext) {
DBMessage *dbMessage = [DBMessage createEntityInContext:localContext];
dbMessage.text = #"Will terminate";
dbMessage.timestamp = [NSDate date];
}];
}
When I open and close (without crash) app few times, in my DB I can see "Did finish launching" entries (more that one, so I'm sure app was closed, not only moved to BG), but none of "Will terminate".
I would be less surprised if the launching event were missed, because I could expect that init method will be called after notification is posted.
What I can do to store terminate events?
I have an answer:
It looks like when application gets terminated, wont't let me to save in new background thread. But saving in current thread works fine. So all I had to do was change saving method to save in current thread, by calling saveWithBlockAndWait: instead of saveWithBlock:
- (void)handleWillTerminateNotification
{
[MagicalRecord saveWithBlockAndWait:^(NSManagedObjectContext *localContext) {
DBMessage *dbMessage = [DBMessage createEntityInContext:localContext];
dbMessage.text = #"Will terminate";
dbMessage.timestamp = [NSDate date];
}];
}
Now events are succesfully saved on DB.
- (IBAction)hitAlarm:(id)sender {
UILocalNotification *localNotif = [[UILocalNotification alloc] init];
NSDateFormatter *formatter;
NSString *dateString;
formatter = [[NSDateFormatter alloc] init];
[formatter setDateFormat:#"dd-MM-yyyy HH:mm"];
dateString = [formatter stringFromDate:_timePicker.date];
NSLog(#"%#",dateString);
[localNotif setFireDate:_timePicker.date];
[localNotif setRepeatInterval:NSCalendarUnitDay];
[[UIApplication sharedApplication] scheduleLocalNotification:localNotif];
NSLog(#"Hit Button");
}
This is a method to set an alarm that will create a local notification at a specified time. However, when the time comes when it should have created a notification, it appears that it did not. There are no given error messages it simply does not seem to deliver a notification. Why is this happening?
On iOS8, you must call [[UIApplication sharedApplication] registerUserNotificationSettings:settings]; to get permissions to display background notifications.
Seems like you're obliged to provide the alert body in order to show a notification. This is what I've found in the documentation:
Assign a string or, preferably, a localized-string key (using NSLocalizedString) as the value of the message. If the value of this property is non-nil, an alert is displayed
You DO NOT set any alert body, therefore it is nil, therefore you don't see the notification
And of course you have to register notification settings as algal said
I'm a beginner of iOS development. I'm now developing a battery utility app in my university project. In my Settings column, I have an option to let user to turn on or off for whether you want to get a notification when the battery is fully charged. I've a problem of getting local notification when the switch is on. I only get an alert view when I tried to turn off the switch and turn it on back when the battery level == 1.0. Appreciate if someone could help me with this. :)
Here's my codes:
-(void)notifySwitched
{
if([self.notifyFullyChargedSwitch isOn])
{
float batterylevel = [UIDevice currentDevice].batteryLevel;
[UIDevice currentDevice].batteryMonitoringEnabled = YES;
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(batteryLevelChanged:) name:UIDeviceBatteryLevelDidChangeNotification object:nil];
[self notifyFullyCharged];
}
}
-(void)notifyFullyCharged{
float batterylevel = [UIDevice currentDevice].batteryLevel;
if(batterylevel == 1.0){
// Schedule the notification
UILocalNotification* localNotification = [[UILocalNotification alloc] init];
//localNotification.fireDate = pickerDate;
localNotification.alertBody = #"Unplug your device. Your battery has been fully charged.";
localNotification.alertAction = #"Slide to view";
localNotification.timeZone = [NSTimeZone defaultTimeZone];
localNotification.applicationIconBadgeNumber = [[UIApplication sharedApplication] applicationIconBadgeNumber] + 1;
[[UIApplication sharedApplication] scheduleLocalNotification:localNotification];
}
}
The code actually adds observer only when the switch is turned ON for the first time. Moving the following code to any initialise method example:- ViewDownload, should add the observer irrespective of switch state.
[UIDevice currentDevice].batteryMonitoringEnabled = YES;
[[NSNotificationCenter defaultCenter]addObserver:self selector:#selector(batteryLevelChanged:) name:UIDeviceBatteryLevelDidChangeNotification object:nil];
Then in the batteryLevelChanged method, the switch state condition needs to be checked before showing the alert.
Hope this helps.
I have a UILabel that randomly will show text from a list I have provided.
I want the UILabel to show one item per day.
What's the best way to handle this?
Should I use an NSTimer or is there a different method?
I'm not worried about a specific time of the day, just that the UILabel updates once per day.
Thank you!
One option would be to save the current date into NSUserDefaults when you show the label.
When your view controller is loaded you get that date from NSUserDefaults. If the difference between the saved date and "now" is more than 24 hours you update the label (and save the new date), otherwise show the current label.
You probably also want the view controller to listen for the "will enter foreground" notification. Each time your app returns to the foreground you will want to do the same check.
Store the date in preferences, and compare when the app comes into the foreground. Your appDelegate would look something like this:
- (void)applicationDidEnterBackground:(UIApplication *)application
{
NSUserDefaults *prefs = [NSUserDefaults standardUserDefaults];
[prefs setObject:[NSDate date] forKey:#"savedDate"];
[prefs synchronize];
}
- (void)applicationDidBecomeActive:(UIApplication *)application
{
NSDate *savedDate = [[NSUserDefaults standardUserDefaults] objectForKey:#"savedDate"];
NSDateComponents *dateComponents = [[NSCalendar currentCalendar] components:NSCalendarUnitDay fromDate:savedDate toDate:[NSDate date] options:0];
if ([dateComponents day] >= 1) {
[[NSNotificationCenter defaultCenter] postNotificationName:#"updateLabel" object:nil];
}
}
Then in your view controller, listen for the notification:
-(void) viewWillAppear:(BOOL)animated {
[super viewWillAppear:animated];
[[NSNotificationCenter defaultCenter] addObserver:self selector:#selector(updateLabel) name:#"updateLabel" object:nil];
}
-(void) viewWillDisappear:(BOOL)animated {
[super viewWillDisappear:animated];
[[NSNotificationCenter defaultCenter] removeObserver:self];
}
-(void) updateLabel {
//update your label here
}
For updating at midnight, check out UIApplicationSignificantTimeChangeNotification. There is a relevant answer here: https://stackoverflow.com/a/15537806/1144632
i am making reminder application there is table view and there is dates in cell and when that cell date become todays day UILocalNotification fires. for that i am using following code
-(void)notification {
// logic for local notification start
NSDateFormatter *Form = [[NSDateFormatter alloc] init];
[Form setDateFormat:#"dd/MM/yyyy"];
UILocalNotification *notification = [[UILocalNotification alloc] init];
for (int i=0;i<_convertedBdates.count;i++)
{
NSDate *date =[Form dateFromString:[_convertedBdates objectAtIndex:i ]];
// NSLog(#"date%#",date);
if(notification)
{
notification.fireDate = date;
notification.timeZone = [NSTimeZone defaultTimeZone];
notification.alertBody = [NSString stringWithFormat:#"Today is %#\'s Birthday",[_combinedNameArray objectAtIndex:i]];
notification.alertAction = #"View";
notification.soundName = UILocalNotificationDefaultSoundName;
notification.applicationIconBadgeNumber = 1;
[[UIApplication sharedApplication] scheduleLocalNotification:notification];
}
}
// local notification logic ends here
}
now i have also implemented functionality of deleting cell from table view
now my problem is cell gets removed but its notification not there is no cell but when that date comes then notification fire.
how should i remove that particular notification when that cell is removed?
Edited: My initial answer was wrong, because I didn't realize that the OS copies UILocalNotifications when you schedule them, rather than just retaining them. So...
There are two ways to do this, as far as I can tell.
Cancel all notifications when a row is deleted, and then reschedule the remaining ones.
This is going to be more efficient if you don't have many notifications scheduled, and it will definitely be much easier to code. (Note: I don't know enough about the low-level things at work to say which is necessarily more efficient, but my guess is that the difference isn't really important.)
Whenever a row is deleted, simply call
[[UIApplcation sharedApplication] cancelAllLocalNotifications];
Then update _convertedBDates appropriately, and finally call your notification method again to reschedule new local notifications for those events that are still around.
Create unique identifiers for your local notifications
This is possibly the more efficient way, if you can come up with a good way to make those unique identifiers and if you have a lot of notifications scheduled. (Emphasis on possibly). One possibility is to use the time at which the notification will fire, if you can guarantee that no two notifications will fire at the same time. Other possibilities are the label for the notification (again if you can be sure of uniqueness). Whatever you decide to use for your unique identifier, you can store it by adding this outside your for loop:
self.uniqueIDArray = [[NSMutableArray alloc] init];
(where uniqueIDArray is an NSMutableArray* #property of your class) and then this right before you schedule the notification:
[uniqueIDArray addObject:whateverObjectYouUseForTheUniqueID];
notification.userInfo = [[NSDictionary alloc] initWithObjects:whateverObjectYouUseForTheUniqueID
forKeys:#"uniqueID"];
Then, in whatever method you're using to delete the cells, you would do something like this:
uniqueIDToDelete = [self.uniqueIDArray objectAtIndex:indexOfCellBeingDeleted];
NSArray *scheduledNotifications = [[UIApplication sharedApplication] scheduledNotifications];
UILocalNotification *notifToDelete;
for (UILocalNotification *notif in scheduledNotifications) {
if ([[notif.userInfo objectForKey:#"uniqueID"] isEqual:uniqueIDToDelete]) {
[[UIApplication sharedApplication] cancelLocalNotification:notif];
}
}
[self.uniqueIDArray removeObjectAtIndex:indexOfCellBeingDeleted];
I think you shouldn't use cell as source for notification. Use data model instead. Also you could remove notification easily
UPDATE
Model *model = [_array objectAtIndex:indexPath.row]; model.notificationID = // here you store created notification identifier
[[UIApplication sharedApplication] scheduledLocalNotifications] // contains all local notifications, you should search you need by ID and remove it using cancelLocalNotification method.
P.S. notification ID you can store in notification userInfo
Try this
NSArray *notificationArray = [[UIApplication sharedApplication] scheduledLocalNotifications];
UILocalNotification *notif = [notificationArray objectAtIndex:indexPath];
[[UIApplication sharedApplication] cancelLocalNotification:notif];