local notification with repeats - swift

I am making a local notification that will run every year, the problem is that I need notifications that come based on the Hebrew calendar, that is, July 20, 2020 according to the Hebrew calendar 28 tamuz 5780 and if we add the year to 28 tamuz 5781 then according to the Gregorian calendar comes out on July 1, 2021, that is, according to the Gregorian calendar, the date changes, I also need to take into account the leap year, AnniversariesReminderService is responsible for the notification, thanks
July 20, 2020 is 28 tamuz 5780
but
28 tamuz 5781 is July 1, 2021
I need to automatically calculate this time difference so that every year I receive a notification according to the Jewish date
struct ReminderAnniversaries {
var id:Int!
var description:String!
var date:Date!
}
class AnniversariesReminderService {
fileprivate var formatter :DateFormatter {
let dateformatterDay = DateFormatter()
dateformatterDay.dateFormat = "yyyy.MM.dd HH:mm"
return dateformatterDay
}
func scheduleReminder(data: ReminderAnniversaries, complited: (() -> Void)? = nil) {
removeScheduledReminder(data: data)
let request = self.prepareNotificationRequest(data: data)
UNUserNotificationCenter.current().add(request, withCompletionHandler: { error in
if error != nil {
print("ERROR SCHEDULE NOTIFICATION TYPE: \(String(describing: "\(data.id!)")), ERROR: \(String(describing:error?.localizedDescription))")
}
complited?()
})
}
fileprivate func prepareNotificationRequest(data: ReminderAnniversaries) -> UNNotificationRequest {
let content = UNMutableNotificationContent()
content.sound = .default
content.title = "AppDisplayName".localized()
content.body = data.description
let fireDate = data.date!
var dateInfo = DateComponents()
let triggerDaily = Calendar(identifier: .hebrew).dateComponents([.year, .month, .day, .hour, .second], from: fireDate)
dateInfo.year = fireDate.year
dateInfo.month = fireDate.month
dateInfo.day = fireDate.day
dateInfo.hour = fireDate.hour
dateInfo.minute = fireDate.minute + 1
dateInfo.second = fireDate.second
content.userInfo = ["id": data.id, "date" : fireDate,
"description": data.description]
let trigger = UNCalendarNotificationTrigger(dateMatching: triggerDaily, repeats: true)
let requestIdentifier = "\(data.id!)"
let request = UNNotificationRequest(identifier: requestIdentifier,
content: content, trigger: trigger)
print("NOTIFICATION SCHEDULED: (String(describing: requestIdentifier))")
print("NOTIFICATION TITLE: \(String(describing: content.body))")
print("NOTIFICATION TIME: \(String(describing: self.formatter.string(from: fireDate)))")
return request
}
func removeScheduledReminder(data: ReminderAnniversaries) {
UNUserNotificationCenter.current().removePendingNotificationRequests(withIdentifiers: ["\(data.id!)"])
print("NOTIFICATION REMOVED: \(String(describing: "\(data.id!)"))")
}
func getScheduledReminderDate(data: ReminderAnniversaries) -> String? {
var scheduledDate: Date?
let semaphore = DispatchSemaphore(value: 0)
UNUserNotificationCenter.current().getPendingNotificationRequests { notifications in
notifications.forEach({ notification in
if let scheduledType = notification.content.userInfo["id"] as? Int, scheduledType == data.id {
scheduledDate = notification.content.userInfo["date"] as? Date ?? Date()
// continue;
semaphore.signal()
}
})
if scheduledDate == nil {
semaphore.signal()
}
}
semaphore.wait()
return scheduledDate != nil ? formatter.string(from: scheduledDate!) : nil
}
}
Setting of date
#objc func doneDatePicker(){
if isAlarmClock && stage != 1 {
datePicker.datePickerMode = .dateAndTime
pickerDone.setTitle("Done".localized(), for: .normal)
pickerCancel.setTitle("Back".localized(), for: .normal)
stage = 1
formatter.dateFormat = "dd-MMM-yyyy HH:mm"
} else if !isAlarmClock {
//1
formatter.dateFormat = "dd-MMM-yyyy"
formatter.calendar = Calendar(identifier: .gregorian)
let dateOfDied = formatter.string(from: datePicker.date)
selectedDate = datePicker.date
// result Jule 21 2020 г.
//2
formatter.calendar = Calendar(identifier: .hebrew) //convert to jewish date
formatter.dateFormat = "dd-MMM-yyyy"
jewishDatePicker.date = datePicker.date
jewishDatePicker.calendar = NSCalendar(calendarIdentifier: NSCalendar.Identifier.hebrew)! as Calendar //convert to jewish calendar for adding year
//count of years between date of died and current date
let calendar = Calendar.current
// Replace the hour (time) of both dates with 00:00
let date1 = calendar.startOfDay(for: selectedDate)
let date2 = calendar.startOfDay(for: Date())
let components = calendar.dateComponents([.year], from: date1, to: date2)
var dayComponent = DateComponents()
dayComponent.year = components.year! + 1
let new_date = jewishDatePicker.calendar.date(byAdding: dayComponent, to: jewishDatePicker.date) // adding year by jewish calendar
jewishSelectedDate = new_date!
let hebDate = formatter.string(from: jewishSelectedDate)
jewishDatePicker.locale = Locale(identifier: "he")
jewishDatePicker.setDate(new_date!, animated: false)
print(jewishDatePicker.date)
// result Jule 9 2021 г.
//3
formatter.calendar = Calendar(identifier: .gregorian) //convert to gregorian date
let gregDate = formatter.string(from: new_date!)
// result Jule 09 2021 г.
alarmClock.isEnabled = true
alarmClockBtn()
var txt = Localized("You choosed", arguments: "\(dateOfDied)")
txt = txt + " " + "Jewish anniversary falls on".localized() + " " + "\(gregDate) (\(hebDate))," + " " + "select the date and time to remind you".localized()
text.text = txt
} else {
reminderTime = datePicker.date
pickerView.isHidden = true
}
}
#objc func addNotificationBtn(){
if !(name.text?.isEmpty ?? false) && !(parentName.text?.isEmpty ?? false) &&
jewishSelectedDate != nil && reminderTime != nil {
errorNotifier.notify("Done".localized(), title: "Notification configured".localized(), showOK: true) { [weak self] in
let remind = ReminderService()
var txt = Localized("You have set an anniversary reminder", arguments: self!.name.text!)
txt = txt + " " + "Бен(Бат)".localized() + " " + self!.parentName.text!
let data = ReminderAnniversaries(id: 11, description: txt, date: self?.reminderTime)
remind.anniversaries.scheduleReminder(data: data) {
print("ready")
}
self?.dismiss(animated: true, completion: nil)
}
} else if (name.text?.isEmpty ?? false) && (parentName.text?.isEmpty ?? false) {
errorNotifier.notify("Check the fields".localized(), title: "Fields must not be empty".localized(), showOK: true)
} else if jewishSelectedDate == nil {
errorNotifier.notify("Please select date if died".localized(), title:"", showOK: true)
} else if reminderTime == nil {
errorNotifier.notify("Please select a time for the anniversary reminder, the time is automatically set the day before the death anniversary".localized(), title: "Please select a time reminder".localized(), showOK: true)
}
}

Related

Convert string time into date swift

I have json from rest API :
{
"status": "ok",
"query": {
"format": "json",
"kota": "703",
"tanggal": "2017-02-07"
},
"jadwal": {
"status": "ok",
"data": {
"ashar": "15:26",
"dhuha": "06:21",
"dzuhur": "12:10",
"imsak": "04:28",
"isya": "19:31",
"maghrib": "18:20",
"subuh": "04:38",
"tanggal": "Selasa, 07 Feb 2017",
"terbit": "05:54"
}
}
}
I want convert jadwal -> data -> ashar and other into Date format
I have this code,
extension String {
func convertToDate() -> Date? {
let arr = self.split(separator: ":")
guard
let hour = Int(arr.first ?? ""),
let minute = Int(arr.last ?? "")
else { return nil }
let component = DateComponents(hour: hour, minute: minute)
var cal = Calendar.current
guard let timezone = TimeZone(identifier: "Asia/Jakarta") else { return nil }
cal.timeZone = timezone
let date = cal.date(from: component)
return date
}
}
But i get this result on my console debug
ashar: 0001-01-01 08:27:48 +0000
i expect for example on asr/ashar = 2021-01-12 15:26:00 for Indonesia Time, do you guys have advice or something?
You can create an extension for string that returns a full date object by passing only the time string as follows.
extension String {
func createDateObjectWithTime(format: String = "HH:mm") -> Date? {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = format
guard let dateObjectWithTime = dateFormatter.date(from: self) else { return nil }
let gregorian = Calendar(identifier: .gregorian)
let now = Date()
let components: Set<Calendar.Component> = [.year, .month, .day, .hour, .minute, .second]
var dateComponents = gregorian.dateComponents(components, from: now)
let calendar = Calendar.current
dateComponents.hour = calendar.component(.hour, from: dateObjectWithTime)
dateComponents.minute = calendar.component(.minute, from: dateObjectWithTime)
dateComponents.second = 0
return gregorian.date(from: dateComponents)
}
}
You can call the extension by:
let date = "15:26".createDateObjectWithTime()
Just get the current date and set hour and minute to the extracted values
extension String {
func convertToDate() -> Date? {
let arr = self.split(separator: ":")
guard
let hour = Int(arr.first ?? ""),
let minute = Int(arr.last ?? "")
else { return nil }
var cal = Calendar.current
guard let timezone = TimeZone(identifier: "Asia/Jakarta") else { return nil }
cal.timeZone = timezone
return cal.date(bySettingHour: hour, minute: minute, second: 0, of: Date())
}
}

Swift 4: validating credit card expiration date

I am writing code to check whether the credit card has expired or not.
Here is what I have
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/yyyy"
let enteredDate = dateFormatter.date(from: expiryDate.text!) /* line 3 - set to first day of month */
let now = Date()
if (enteredDate! < now) {
//expired
// does not work if current month and year
// is the same as the expiration date,
// because expiration day is set to the first day of the month on line 3
} else {
// valid
print("valid - now: \(now) entered: \(enteredDate)")
}
Any ideas on how I can change the initialized date to be the last day of the month instead of the first day?
enteredDate will be midnight local time on the first of the month of the expiry date. Since you want that whole month to be valid, add 1 month to that value and then compare Date() to that updated value.
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/yyyy"
let enteredDate = dateFormatter.date(from: expiryDate.text!)!
let endOfMonth = Calendar.current.date(byAdding: .month, value: 1, to: enteredDate)!
let now = Date()
if (endOfMonth < now) {
print("Expired - \(enteredDate) - \(endOfMonth)")
} else {
// valid
print("valid - now: \(now) entered: \(enteredDate)")
}
Please note that I left proper handling of optionals as an exercise for the reader.
Instead of comparing the dates, compare month of the dates using compare(_:to:toGranularity:)
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/yyyy"
if let enteredDate = dateFormatter.date(from: "05/2019") {
let result = Calendar.current.compare(Date(), to: enteredDate, toGranularity: .month)
if result == .orderedSame {
print("valid")
} else if result == .orderedAscending {
print("valid")
} else if result == .orderedDescending {
print("expired")
}
}
rmaddy's answer is perfect. Here is how I thought of using Calendar to handle the validation. Perhaps, I wrote it in more complex way.
enum ExpiryValidation {
case valid, invalidInput, expired
}
func validateCreditCardExpiry(_ input: String) -> ExpiryValidation {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/yyyy"
guard let enteredDate = dateFormatter.date(from: input) else {
return .invalidInput
}
let calendar = Calendar.current
let components = Set([Calendar.Component.month, Calendar.Component.year])
let currentDateComponents = calendar.dateComponents(components, from: Date())
let enteredDateComponents = calendar.dateComponents(components, from: enteredDate)
guard let eMonth = enteredDateComponents.month, let eYear = enteredDateComponents.year, let cMonth = currentDateComponents.month, let cYear = currentDateComponents.year, eMonth >= cMonth, eYear >= cYear else {
return .expired
}
return .valid
}
let invalidInput = validateCreditCardExpiry("hello")
let validInput = validateCreditCardExpiry("09/2020")
let expiredInput = validateCreditCardExpiry("04/2010")
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MM/yyyy"
let enteredDate = dateFormatter.date(from: expiryDate.text!)
//Also convert the current date in the entered date format and then checks that the date is valid if enteredDate month or year is greater than current date.
let currentDate = dateFormatter.date(from: dateFormatter.string(from: Date()))
if enteredDate.compare(now) != ComparisonResult.orderedAscending {
print("Valid")
} else {
print("Not Valid")
}
As this only compares the month and year so it will resolve your issue of first or last date of month.
Here is the full answer for Expiry date calculation from textfield formatted as MM/YY. Use this in textfieldshouldchangecharactersinrange method
var cleanString = string.replacingOccurrences(of: "/", with: "")
if cleanString.rangeOfCharacter(from: unsupportedCharacterSet) != nil {
return ""
}
let dateString: String
if cleanString.count == 0 {
return string
} else if cleanString.count > 4 {
// trim the string down to 4
let reqIndex = cleanString.index(cleanString.startIndex, offsetBy: 4)
cleanString = String(cleanString[..<reqIndex])
}
if cleanString.hasPrefix("0") == false && cleanString.hasPrefix("1") == false {
dateString = "0" + cleanString
} else {
dateString = cleanString
}
let currentYear = Calendar.current.component(.year, from: Date()) % 100 // This will give you current year (i.e. if 2019 then it will be 19)
let currentMonth = Calendar.current.component(.month, from: Date()) // This will give you current month (i.e if June then it will be 6)
var newText = ""
for (index, character) in dateString.enumerated() {
print("index: \(index)")
if index == 1 {
let enterdMonth = Int(dateString.prefix(2)) ?? 0 // get first two digit from entered string as month
print("enterdMonth at 1:\(enterdMonth)")
if (1 ... 12).contains(enterdMonth){
if enterdMonth < 10 {
newText = "0" + "\(enterdMonth)" + "/"
}else {
newText = "\(enterdMonth)" + "/"
}
}else{
}
}else if index == 2 {
if (2 ... 99).contains(Int(String(character))!) { // Entered year should be greater than 2019.
newText.append(character)
}else{
}
}else if index == 3 {
print("index---: \(index)")
let enterdYr = Int(dateString.suffix(2)) ?? 0 // get last two digit from entered string as year
let enterdMonth = Int(dateString.prefix(2)) ?? 0 // get first two digit from entered string as month
print("enterdYr: \(enterdYr)")
print("currentYear: \(currentYear)")
if (2 ... 99).contains(enterdYr) { // Entered year should be greater than 2019
if enterdYr >= currentYear {
if (1 ... 12).contains(enterdMonth) {
if enterdMonth < 10 {
newText = "0" + "\(enterdMonth)" + "/" + "\(enterdYr)"
}else {
newText = "\(enterdMonth)" + "/" + "\(enterdYr)"
}
return newText
}
}
}
}
else {
newText.append(character)
}
}
return newText
}
if enteredDate.compare(now) == ComparisonResult.orderedDescending
{
print("valid")
}
else{
print("not valid")
}

How to hide next day with FSCalendar based on week

Hi I want to hide the next business day, if user registration date and current date both are same. I need to hide next working day.
Sunday and Saturday are holidays.
I write code following if user is register on Friday I need to hide Monday, how to resolve this problem.
I write like this how to hide businessday
func calendar(_ calendar: FSCalendar, shouldSelect date: Date, at monthPosition: FSCalendarMonthPosition) -> Bool {
let joingdate = "2019-01-30" //modeldata.joindate
let currentdate = date.toString(dateFormat: "yyyy-MM-dd")
let currentDate = date
let currentdayweek = date.toString(dateFormat: "EEEE")
if joingdate == currentdate
{
if currentdayweek == "Friday"{
let businessday = Calendar.current.date(byAdding: .day, value: 3, to: currentDate)
return false
}
else if currentdayweek == "Saturday"{
let businessday = Calendar.current.date(byAdding: .day, value: 2, to: currentDate)
return false
}
else if currentdayweek == "Sunday"{
let businessday = Calendar.current.date(byAdding: .day, value: 1, to: currentDate)
return false
}
else
{
let businessday = Calendar.current.date(byAdding: .day, value: 1, to: currentDate)
return false
}
}
}
func minimumDate(for calendar: FSCalendar) -> Date {
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss"
let myString = formatter.string(from: Date())
let yourDate = formatter.date(from: myString)
formatter.dateFormat = "yyyy-MM-dd"
let strCurrentDate = formatter.string(from: yourDate!)
var addDay = 0
if let model = modeldata{
if let joiningdate = "2019-01-31"
{
if strCurrentDate == joiningdate
{
addDay = 2
}
let currentdayweek = yourDate!.toString(dateFormat: "EEEE")
if currentdayweek == "Friday"{
addDay = 4
}
else if currentdayweek == "Saturday"{
addDay = 3
}
else if currentdayweek == "Sunday"{
addDay = 2
}
}
}
let tomorrow = Calendar.current.date(byAdding:
.day, // updated this params to add hours
value: addDay,
to: formatter.date(from: strCurrentDate)!)
return tomorrow!
}
}

How to get day and month from Date type - swift 4

I have variable with data type Date, where I have stored date in this format
2018-12-24 18:00:00 UTC
How can I get from this day or month?
Details
Xcode Version 11.0 (11A420a), Swift 5
Links
How to convert string to Date
Solution
extension Date {
func get(_ components: Calendar.Component..., calendar: Calendar = Calendar.current) -> DateComponents {
return calendar.dateComponents(Set(components), from: self)
}
func get(_ component: Calendar.Component, calendar: Calendar = Calendar.current) -> Int {
return calendar.component(component, from: self)
}
}
Usage
let date = Date()
// MARK: Way 1
let components = date.get(.day, .month, .year)
if let day = components.day, let month = components.month, let year = components.year {
print("day: \(day), month: \(month), year: \(year)")
}
// MARK: Way 2
print("day: \(date.get(.day)), month: \(date.get(.month)), year: \(date.get(.year))")
Is that String or Date type?
if it is date you can do so:
let calendarDate = Calendar.current.dateComponents([.day, .year, .month], from: date)
that will give you the day, year and month
If you saved "2018-12-24 18:00:00 UTC" as a String, you can try this:
let dateString = "2018-12-24 18:00:00 UTC"
let formatter = DateFormatter()
formatter.dateFormat = "yyyy-MM-dd HH:mm:ss 'UTC'"
guard let date = formatter.date(from: dateString) else {
return
}
formatter.dateFormat = "yyyy"
let year = formatter.string(from: date)
formatter.dateFormat = "MM"
let month = formatter.string(from: date)
formatter.dateFormat = "dd"
let day = formatter.string(from: date)
print(year, month, day) // 2018 12 24
Best Wishes!
The date string is not ISO8601 compliant, nevertheless you can use ISO8601DateFormatter if you replace <space> + UTC with Z and use custom format options.
Create a Date from the string and get the DateComponents for day and month
let string = "2018-12-24 18:00:00 UTC"
let formatter = ISO8601DateFormatter()
let trimmedString = string.replacingOccurrences(of: "\\s?UTC", with: "Z", options: .regularExpression)
formatter.formatOptions = [.withFullDate, .withFullTime, .withSpaceBetweenDateAndTime]
if let date = formatter.date(from: trimmedString) {
let components = Calendar.current.dateComponents([.day, .month], from: date)
let day = components.day!
let month = components.month!
print(day, month)
}
This is how I did in swift 4. I have a Billing class with "billDate" a field that contains the date.
extension Billing:Identifiable{
func month() -> String {
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "MMM"
if let inputDate = billDate {
let month = dateFormatter.string(from: inputDate)
return month.uppercased()
}
return ""
}
Extention Date All Time RETURN String:
extension Date {
func dateDistance(_ startDate: Date = Date()) -> String {
var remainingTime = ""
let dateValue = Calendar.current.dateComponents([.year,.month,.weekOfMonth,.day,.hour,.minute,.second], from: startDate, to: self)
remainingTime = String(format: "%2d Y - %2d M - %2d W - %2d D\n%02d:%02d:%02d",
dateValue.year ?? 0,
dateValue.month ?? 0,
dateValue.weekOfMonth ?? 0,
dateValue.day ?? 0,
dateValue.hour ?? 0,
dateValue.minute ?? 0,
dateValue.second ?? 0
)
return remainingTime
}
}
extension Date {
func getRemainingDayMonthYear (_ startDate: Date = Date()) -> String
{
var remainingTime = ""
let dateValue = Calendar.current.dateComponents([.year,.month,.weekOfMonth,.day,.hour,.minute,.second], from: startDate, to: self)
if dateValue.year ?? 0 > 0 {
remainingTime = dateValue.year ?? 0 > 1 ? String(format: "%2d Years",dateValue.year ?? 0) : String(format: "%2d Year",dateValue.year ?? 0)
} else if dateValue.month ?? 0 > 0 {
remainingTime = dateValue.month ?? 0 > 1 ? String(format: "%2d Months",dateValue.month ?? 0) : String(format: "%2d Month",dateValue.month ?? 0)
} else {enter code here
remainingTime = dateValue.day ?? 0 > 1 ? String(format: "%2d Days",dateValue.day ?? 0) : String(format: "%2d Day",dateValue.day ?? 0)
}
return remainingTime
}
}
If you have a date variable you can also use this solution for SwiftUi:
let date = Date()
Text(date, style: .time) // January 8, 2023
Text(date, style: .date) // 12:08 PM

Error:XPC connection interrupted

I am scheduling notification by running the loop in an array.Notification is working fine.But my application is crashing with this error.I know that the OS stops working and os gets rebooted but if i dont call this scheduleNotification function then everything works fine so i know that the error is generation because of this function when i pops to other screen and comes back again my app gets crashg but i dont know why?? please help me out.
func scheduleNotification() {
if dictInfo.object(forKey: "name") !as AnyObject as ? String == "zzz" {
} else {
let calendar = Calendar.current
var calendarComponents = DateComponents()
let strDay = (dictInfo["dd"
as NSString] !as AnyObject).doubleValue
let strMonth = (dictInfo["mm"
as NSString]) !
let dayy = strDay
let monthh = strMonth
calendarComponents.hour = 11
calendarComponents.minute = 04
calendarComponents.day = Int(dayy!) - 2
print(calendarComponents.day)
let df = DateFormatter()
df.dateFormat = "MM" // if you need 3 letter month just use "LLL"
let datee1 = df.date(from: String(describing: monthh))
print(datee1)
let monthh11 = (Calendar.current as NSCalendar).component(.month, from: datee1!)
print(monthh11) // 5
//calendarComponents.year = Int(yearr!)
calendarComponents.month = Int(monthh11)
let newComponents = DateComponents(calendar: calendar, timeZone: .current, month: calendarComponents.month, day: calendarComponents.day, hour: calendarComponents.hour, minute: calendarComponents.minute)
if# available(iOS 10.0, * ) {
let strName: String = String(describing: dictInfo["name"] !)
let str2: String! = "Today is \(strName)\'s Birthday!"
let trigger = UNCalendarNotificationTrigger(dateMatching: newComponents, repeats: false)
let content = UNMutableNotificationContent()
content.title = "Alert Me!"
content.body = str2
content.sound = UNNotificationSound.default()
content.categoryIdentifier = "myCategory"
content.userInfo = ((dictInfo as Any) as ? [AnyHashable: Any]) !
print(content.userInfo.count)
let request = UNNotificationRequest(identifier: strName, content: content, trigger: trigger)
// UNUserNotificationCenter.current().removeAllPendingNotificationRequests()
UNUserNotificationCenter.current().add(request) {
(error) in
if let error = error {
print("Uh oh! We had an error: \(error)")
} else {
}
}
}
}
MBProgressHUD.hide(
for: self.view, animated: true)
tableVieww.isHidden = false
//self.navigationItem.leftBarButtonItem?.isEnabled = true
}