Локальное уведомление каждый X день - Swift - PullRequest
0 голосов
/ 04 сентября 2018

Прежде чем я начну, пожалуйста, не сжигайте меня, так как я знаю, что об этом уже сотни раз спрашивали без надежного ответа, но я считаю, что есть решение, использующее фоновое обновление. https://medisafe.com/ приложение, похоже, решило эту проблему!

Цель: Для запуска локального уведомления в указанное время каждые x дней

Мое решение

шаг 1: получить интервал таймера от даты начала и нечетного появления (в данном случае 2) дней от (отредактировано)

шаг 2: установить интервал таймера на эту разницу с повтором

шаг 3: активировать фоновое обновление (если приложение даже будет закрыто, оно загрузит приложение в фоновом режиме и даст мне небольшое окно для выполнения некоторых задач)

шаг 4. установить фоновое обновление, чтобы запускаться один раз в день

шаг 5: выполните API-интерфейсы get items, которые обновят все таймеры и уведомления

шаг 6 сядь и с удивлением улыбаюсь моему решению

но это не удалось.

так, интервал таймера

 let newTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 172800,repeats: true)

, но это будет просто сбрасывать таймер каждый день, когда выполняется фоновая выборка, и будет запускаться через 2 дня с СЕЙЧАС, а не с даты начала.

Таким образом, должен быть способ сравнения дат, часов и минут (дата начала, дата x и текущая дата, чтобы определить значение интервала таймера.

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

      var triggerType : DateComponents? {
    var triggerT : DateComponents?
    var cal = Calendar(identifier: .gregorian)
    cal.firstWeekday = 2
    if let notificationModel = self.notificationModel {
        switch notificationModel.reminderType {
        case .daily?, .weekly?:
            if let date = notificationModel.date {
                triggerT = cal.dateComponents([.weekday, .hour, .minute], from:date)
                if let weekday = notificationModel.weekday {
                    triggerT?.weekday = weekday
                }
            }
        case .alternateDays?:
            if let date = notificationModel.date {
                triggerT = cal.dateComponents([ .hour, .minute], from:date)
                // THIS IS WHERE I NEED HELP
            }
        case .monthly?:
            if let date = notificationModel.date {
                triggerT = cal.dateComponents([.day,.hour,.minute], from: date)
            }
        case .yearly?:
            triggerT = Calendar.current.dateComponents([.month,.day,.hour,.minute], from: (notificationModel.date)!)
        case .oneOff?:
            triggerT = Calendar.current.dateComponents([.year,.month,.day,.hour,.minute], from: (notificationModel.date)!)
        case .none:
            DispatchQueue.main.async {
                if let category = self.notificationModel?.category, let title = self.notificationModel?.title {
                    Toast.down("An error was discovered in \(category). Please change the occurance value for the following \(title)")
                }
            }
        }
    } else {
        print("NOTIFICATION MODEL IS CORRUPT")
    }
    return triggerT
}

func add(notification: NotificationModel){
    let content = UNMutableNotificationContent()

    if let title = notification.title,
        let body = notification.body,
        let identifier = notification.identifier {


        content.title = title
        content.body = body
        content.sound = UNNotificationSound.default()
        content.categoryIdentifier = (notification.category?.rawValue)!
        content.setValue("YES", forKeyPath: "shouldAlwaysAlertWhileAppIsForeground")

        var trigger : UNCalendarNotificationTrigger?

        if let triggerType = self.triggerType {

            if let occurance = notification.occurance {
                if occurance > 0 {
                }
            }
            trigger = UNCalendarNotificationTrigger(dateMatching: triggerType, repeats: true)

        } else {
            return
        }

        let interval = Date().timeIntervalSince1970
        let identifierString = "2\(interval)"

        var request : UNNotificationRequest!
        if notification.reminderType == .alternateDays {
            print("ADDING TIMER NOTIFICATION")
            print("REMINDER TIME = \(notification.date)")
            // 172800 = two days
            let newTrigger = UNTimeIntervalNotificationTrigger(timeInterval: 172800,
                                                        repeats: true)
            request = UNNotificationRequest(identifier: identifierString,
                                            content: content, trigger: newTrigger)
        } else {
            request = UNNotificationRequest(identifier: identifierString,
                                            content: content, trigger: trigger)
        }


        center.add(request, withCompletionHandler: { (error) in
            if let error = error {
                // Something went wrong
                print(error.localizedDescription)
            } else
            {
                print("ADDING NOTIDCIATION \(content.title)")

            }
        })



        //SNOOZE OR DELETE NOTIFICATIONS
        let snoozeAction = UNNotificationAction(identifier: "Snooze",                                          title: "Snooze", options: [])
        let deleteAction = UNNotificationAction(identifier: "UYLDeleteAction",title: "Delete", options: [.destructive])
        //Create a category with the actions: This requires another unique identifier (you probably want to define these magic strings in an enum):
        let category = UNNotificationCategory(identifier: notification.category!.rawValue,
                                              actions: [snoozeAction,deleteAction],
                                              intentIdentifiers: [], options: [])

        //Register the category with the notification center. It is recommended to do this early in the app lifecycle.

        center.setNotificationCategories([category])
        //To include this action in our notifications we need to set the category in the notification content:

    } else {
        print("Failed to add notification")
    }
}

однако, я хочу через день и не хочу использовать ограничение на 64 уведомления.

спасибо за ваше время

Thomas

Ответы [ 2 ]

0 голосов
/ 05 сентября 2018

Так что спасибо за указания здесь это окончательное решение, которое я придумал. Убедитесь, что вы включаете фоновые режимы в возможностях приложения, чтобы текущая неделя обновлялась. я делал мой каждый день.

Background Modes in Xcode

Тогда код с комментариями.

//: Playground - noun: a place where people can play

import UIKit
import UserNotifications

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

// HELPERS

extension Date {

public var weekday: Int {
    return Calendar.current.component(.weekday, from: self)
}

public var hour: Int {
    get {
        return Calendar.current.component(.hour, from: self)
    }
    set {
        let allowedRange = Calendar.current.range(of: .hour, in: .day, for: self)!
        guard allowedRange.contains(newValue) else { return }

        let currentHour = Calendar.current.component(.hour, from: self)
        let hoursToAdd = newValue - currentHour
        if let date = Calendar.current.date(byAdding: .hour, value: hoursToAdd, to: self) {
            self = date
        }
    }
}

public var minute: Int {
    get {
        return Calendar.current.component(.minute, from: self)
    }
    set {
        let allowedRange = Calendar.current.range(of: .minute, in: .hour, for: self)!
        guard allowedRange.contains(newValue) else { return }

        let currentMinutes = Calendar.current.component(.minute, from: self)
        let minutesToAdd = newValue - currentMinutes
        if let date = Calendar.current.date(byAdding: .minute, value: minutesToAdd, to: self) {
            self = date
        }
    }
}
}

Затем мы создаем нашу собственную структуру уведомлений

struct CustomNotification {

static func everyOtherDay(wtihStartDate startDate: Date) -> [Int]? {

    //
    let currentDate = Date()
    // get initial week day from start date to compare dates
    let weekDay = startDate.weekday

    // Then we need to get week of years for both dates
    let cal = Calendar.current

    guard let weekA = cal.dateComponents([.weekOfYear], from: startDate).weekOfYear else { return nil}

    guard let weekB = cal.dateComponents([.weekOfYear], from: currentDate).weekOfYear else {return nil}

    // create two arrays for week days

    let weekOne = [1,3,5,7]
    let weekTwo = [2,4,6]

    // then we create a module to check if we are in week one or week two

    let currentWeek = (weekA - weekB) % 2

    if currentWeek == 0 {
        //week 1
        return weekOne.contains(weekDay) ? weekOne : weekTwo
    } else {
        // week 2
        return weekOne.contains(weekDay) ? weekTwo : weekOne
    }
}
}

наконец, в нашем классе, где мы создаем уведомление. Я лично использую менеджер уведомлений. но чтобы показать тебе быстро

class AClass : NSObject {

func setupNotifications() {

    let startDate = Date()
    let weekDays =  CustomNotification.everyOtherDay(wtihStartDate: startDate)
    let cal = Calendar.current
    let center = UNUserNotificationCenter.current()
    if let weekDays = weekDays {

        for day in weekDays {

            let identifier = "Some Random ID"

            let content = UNMutableNotificationContent()
            content.title = "title"
            content.body = "body"
            content.sound = UNNotificationSound.default()
            content.categoryIdentifier = "SOME CATEGORY"
            content.setValue("YES", forKeyPath: "shouldAlwaysAlertWhileAppIsForeground")

            var components = cal.dateComponents([.hour, .minute], from:startDate)
            components.weekday = day
            let trigger = UNCalendarNotificationTrigger(dateMatching: components, repeats: true)
            let request = UNNotificationRequest(identifier: identifier,
                                            content: content, trigger: trigger)

            center.add(request, withCompletionHandler: { (error) in
                if let error = error {
                    // Something went wrong
                    print("ERROR ADDING NOTIFICATION TO CENTER \(error.localizedDescription)")
                } else
                {
                    print("ADDING NOTIFCIATION \(content.categoryIdentifier)")
                }
            })

        }
    }
}
}

Затем нам нужно настроить фоновую выборку в нашем приложении и делегат приложения

        // OVER IN APP DELEGATE

func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplicationLaunchOptionsKey: Any]?) -> Bool {
    // setup background refresh ensuring you turn it on in app capabilities
    // trigger back ground refrsh once a day
    UIApplication.shared.setMinimumBackgroundFetchInterval(86400)

    return true

}

func application(_ application: UIApplication, performFetchWithCompletionHandler completionHandler: @escaping (UIBackgroundFetchResult) -> Void) {
     // FETCH DATA and REFRESH NOTIFICATIONS
    // We need to do this to ensure the current week value is updated to either 1 or 0
    // You will need to delete all notifications with same same category first else your going to be getting both weeks notifications
    let  aClass = AClass()
    aClass.setupNotifications()
}

Надеюсь, это кому-нибудь поможет: D Томас

0 голосов
/ 04 сентября 2018

Допустим, вы хотите активировать уведомление через 2, 4 и 6 дней, вот как вы можете это сделать:

Для моего примера я добавил расширение к Date

extension Date {
    func adding(days: Int) -> Date? {
        var dateComponents = DateComponents()
        dateComponents.day = days

        return NSCalendar.current.date(byAdding: dateComponents, to: self)
    }
}

Тогда вы можете просто создать новые уведомления на указанные даты, в этом примере через 2, 4, 6 дней

let date = Date()
for i in [2, 4, 6] {
    if let date = date.adding(days: i) {
        scheduleNotification(withDate: date)
    }
}

func scheduleNotification(withDate date: Date) {
    let notificationContent = UNMutableNotificationContent()
    notificationContent.title = "Title"
    notificationContent.subtitle = "Subtitle"
    notificationContent.body = "Body"

    let identifier = "Make up identifiers here"
    let dateComponents = Calendar.autoupdatingCurrent.dateComponents([.day, .month, .year, .hour, .minute, .second], from: date)

    let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: false)

    let notificationReques = UNNotificationRequest(identifier: identifier, content: notificationContent, trigger: trigger)

    UNUserNotificationCenter.current().add(notificationReques) { error in
        if let e = error {
            print("Error \(e.localizedDescription)")
        }
    }
}

Это должно запланировать 3 уведомления - через 2, 4, 6 дней ...

...