Ежемесячное уведомление Objective-C в конце месяца

В моем приложении есть система уведомлений, которая позволяет пользователям устанавливать несколько уведомлений, и они могут быть:

  • Ежедневно
  • Еженедельно
  • Ежемесячно

Первые 2 нормально, а вот с третьим проблема. Предположим, что мы находимся в марте, и пользователь устанавливает уведомление для срабатывания 31-го числа месяца. Уведомление запланировано правильно, но если мы, например, в апреле (30 дней), уведомление запланировано на 1 мая.

У меня есть 2 вопроса:

  • Как запланировать уведомление на последний день месяца? Или настройте их так, чтобы они корректно справились с этим делом в следующие месяцы.
  • Если уведомление на 31 марта запланировано правильно, будет ли следующее уведомление запланировано на 30 апреля, а затем на 31 мая? Я предполагаю, что нет, будет 31 марта, 1 апреля и 31 апреля.

Планирование нескольких уведомлений не подходит для меня, потому что у Apple есть ограничение, и этот предел может быть легко достигнут, если у пользователя есть 6 ежемесячных уведомлений (6x12), и у них может быть больше.

Спасибо,

Серхио

ИЗМЕНИТЬ

Извините, я не объяснил себя должным образом. У меня нет проблем с установкой уведомления в правильный день. Но у меня проблема с тем, как будут работать повторения. Если я настрою уведомление на запуск 31-го числа каждого месяца, начиная с 31-го марта (допустим, мы находимся в марте), первое уведомление появится в нужный день, но что произойдет в апреле?

Еще раз спасибо.


person SergioM    schedule 17.04.2014    source источник
comment
Можете ли вы опубликовать код, который вы используете для планирования уведомлений?   -  person Matías R    schedule 17.04.2014


Ответы (4)


Вы можете получить последний день месяца определенной даты, используя следующие методы.

-(NSDate*)lastDayOfMonthOfDate:(NSDate *)date
{
    NSInteger dayCount = [self numberOfDaysInMonthCountForDate:date];

    NSCalendar *calendar = [[NSCalendar alloc] initWithCalendarIdentifier:NSGregorianCalendar];

    [calendar setTimeZone:[NSTimeZone timeZoneWithName:@"GMT"]];

    NSDateComponents *comp = [calendar components:
                              NSYearCalendarUnit |
                              NSMonthCalendarUnit |
                              NSDayCalendarUnit fromDate:date];

    [comp setDay:dayCount];

    return [calendar dateFromComponents:comp];
}

-(NSInteger)numberOfDaysInMonthCountForDate:(NSDate *)date
{
    NSCalendar *calendar = [[NSCalendar alloc]   initWithCalendarIdentifier:NSGregorianCalendar];

   [calendar setTimeZone:[NSTimeZone timeZoneWithName:TIMEZONE]];

    NSRange dayRange = [calendar rangeOfUnit:NSDayCalendarUnit
                                      inUnit:NSMonthCalendarUnit
                                     forDate:date];

    return dayRange.length;
}

Найдите последний день из этого метода и запланируйте уведомление на этот день.

Источник: Получение последнего дня месяца

person manujmv    schedule 17.04.2014
comment
Спасибо, но это решает проблему только с первым уведомлением, что я уже делаю. Я использую ежемесячные уведомления с помощью RepeatInterval NSMonthCalendarUnit. Проблема заключается в том, как ОС планирует следующие уведомления, а не первое, которое я установил. Спасибо. - person SergioM; 17.04.2014
comment
Спасибо @manujmv. Я обновил вопрос и добавил некоторые пояснения. - person SergioM; 17.04.2014

-[NSCalendar rangeOfUnit:inUnit:forDate:] сообщит вам минимальное и максимальное значения, которые конкретный компонент даты может принимать в более крупном компоненте в контексте конкретной даты.

Если вы используете NSDayCalendarUnit и NSMonthCalendarUnit, length результата будет последний день месяца, содержащего переданную дату.

Например:

NSCalendar * c = [NSCalendar currentCalendar];
NSRange aprilRanger = [c rangeOfUnit:NSDayCalendarUnit 
                              inUnit:NSMonthCalendarUnit 
                             forDate:[NSDate date]];

NSLog(@"%lu", aprilRanger.length);

NSDateComponents * minusTwoMonths = [NSDateComponents new];
[minusTwoMonths setMonth:-2];
NSDate * febDay = [c dateByAddingComponents:minusTwoMonths 
                                  toDate:[NSDate date] 
                                 options:0];
NSRange febRange = [c rangeOfUnit:NSDayCalendarUnit 
                           inUnit:NSMonthCalendarUnit 
                          forDate:febDay];

NSLog(@"%lu", febRange.length);

Производит:

30
28

И это даст вам правильный ответ, когда случаются странные вещи, такие как високосные дни.

person jscs    schedule 17.04.2014
comment
Спасибо за ответ. Это позволяет мне установить первое уведомление на действительную дату текущего месяца, но проблема в том, что следующий месяц будет снова 30-го или 28-го, даже если пользователь выбрал 31-е. - person SergioM; 17.04.2014
comment
Насколько я знаю, UILocalNotification строго основаны на интервалах. Если вам нужна функциональность последнего дня месяца, я считаю, что вам придется перепланировать уведомление каждый месяц. - person jscs; 17.04.2014
comment
Спасибо, Джош. Я обновил вопрос. Добавил некоторые уточнения. - person SergioM; 17.04.2014
comment
Например, если вы используете NSTimer, когда срабатывает таймер, вы делаете его недействительным и создаете новый. Строго говоря, вы должны сделать то же самое с ежедневными и еженедельными уведомлениями, потому что иногда они на час длиннее или короче ожидаемого. - person gnasher729; 17.04.2014

Что мне нужно было сделать, так это: проверить, есть ли в текущем месяце день, который запросил пользователь (максимум 31).

  • Если он не запланирует обычное уведомление для текущего месяца в последний день, а затем запланирует ежемесячное уведомление в следующем месяце, ОС сделает все остальное.

Примечание. Если в текущем месяце нет запрошенного дня (31 февраля), в следующем он обязательно будет (31 марта).

  • Если это так, то запланируйте ежемесячно на текущий месяц.
person SergioM    schedule 02.05.2014

Эй, это не имеет большого значения.

В вашем объекте уведомления установите repeatInterval на NSMonthCalendarUnit

UILocalNotification *notify = [ [UILocalNotification alloc] init ];
notify.fireDate = notificationDate;
notify.repeatInterval = NSMonthCalendarUnit;
// use NSDayCalendarUnit, NSWeekCalendarUnit for daily or weekly respectively.

Спасибо, damithH.

person damithH    schedule 17.04.2014
comment
Спасибо, это не моя проблема. Я установил его как ежемесячное уведомление. Проблема в том, что если я настрою уведомление на запуск 31-го числа каждого месяца, начиная с 31-го марта (допустим, мы в марте), первое уведомление появится в нужный день, но что произойдет в апреле? - person SergioM; 17.04.2014
comment
проверьте ответ. stackoverflow.com/questions/10739834/ - person damithH; 17.04.2014