Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

New feature: JSON reminder entry, as an independent command to add an entry #94

Open
wants to merge 2 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 15 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,21 @@ $ reminders show Soon
3: Something really important (priority: high)
```

#### Add a reminder via JSON input string

```
json_string='{
"title": "Buy groceries",
"notes": "Milk, eggs, bread, cheese",
"dueDate": "2025-02-15T10:24:00Z",
"priority": "medium",
"listName": "Groceries",
"recurring": "weekly",
}'

$ reminders add-json $json_string
```

#### See help for more examples

```
Expand Down
22 changes: 22 additions & 0 deletions Sources/RemindersLibrary/CLI.swift
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,27 @@ private struct Add: ParsableCommand {
}
}

private struct AddJSON: ParsableCommand {
static let configuration = CommandConfiguration(
abstract: "Add a reminder as JSON")

@Argument(
parsing: .remaining,
help: "The JSON string representing the reminder to add")
var JSONstring: [String]

@Option(
name: .shortAndLong,
help: "format, either of 'plain' or 'json'")
var format: OutputFormat = .plain

func run() {
reminders.addReminderJSON(
string: self.JSONstring.joined(separator: " "),
outputFormat: format)
}
}

private struct Complete: ParsableCommand {
static let configuration = CommandConfiguration(
abstract: "Complete a reminder")
Expand Down Expand Up @@ -282,6 +303,7 @@ public struct CLI: ParsableCommand {
abstract: "Interact with macOS Reminders from the command line",
subcommands: [
Add.self,
AddJSON.self,
Complete.self,
Uncomplete.self,
Delete.self,
Expand Down
77 changes: 74 additions & 3 deletions Sources/RemindersLibrary/Reminders.swift
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,7 @@ public final class Reminders {
}

func showLists(outputFormat: OutputFormat) {
switch (outputFormat) {
switch outputFormat {
case .json:
print(encodeToJson(data: self.getListNames()))
default:
Expand All @@ -100,8 +100,10 @@ public final class Reminders {
}
}

func showAllReminders(dueOn dueDate: DateComponents?,
displayOptions: DisplayOptions, outputFormat: OutputFormat) {
func showAllReminders(
dueOn dueDate: DateComponents?,
displayOptions: DisplayOptions, outputFormat: OutputFormat
) {
let semaphore = DispatchSemaphore(value: 0)
let calendar = Calendar.current

Expand Down Expand Up @@ -334,6 +336,75 @@ public final class Reminders {
}
}

func addReminderJSON(string: String, outputFormat: OutputFormat)
{
// Let's decode the JSON string into a dictionary
guard let jsonData = string.data(using: .utf8) else {
print("Failed to convert JSON string to data")
exit(1)
}

guard let json = try? JSONSerialization.jsonObject(with: jsonData, options: []) as? [String: Any] else {
print("Failed to parse data as JSON")
exit(1)
}

let title = json["title"] as? String ?? ""
let name = json["listName"] as? String ?? ""
let notes = json["notes"] as? String ?? ""

let prior = json["priority"] as? String ?? "none"
let priority = Priority(rawValue: prior) ?? .none

let recur = json["recurring"] as? String ?? "none"

let ddate = json["dueDate"] as? String ?? ""
let dueDateComponents = DateComponents(argument: ddate)

// Let's create a new reminder
let calendar = self.calendar(withName: name)
let reminder = EKReminder(eventStore: Store)
reminder.calendar = calendar
reminder.title = title
reminder.notes = notes
reminder.priority = Int(priority.value.rawValue)

switch (recur) { // see also, e.g. EKRecurrenceRule(recurrenceWith: .weekly, interval: 1, daysOfTheWeek: [.init(.monday),.init(.tuesday)], daysOfTheMonth: nil, monthsOfTheYear: nil, weeksOfTheYear: nil, daysOfTheYear: nil, setPositions: nil, end: .none)
case "daily":
reminder.addRecurrenceRule(EKRecurrenceRule(recurrenceWith: .daily, interval: 1, end: .none))
reminder.addAlarm(EKAlarm(relativeOffset: .zero))
case "weekly":
reminder.addRecurrenceRule(EKRecurrenceRule(recurrenceWith: .weekly, interval: 1, end: .none))
reminder.addAlarm(EKAlarm(relativeOffset: .zero))
case "monthly":
reminder.addRecurrenceRule(EKRecurrenceRule(recurrenceWith: .monthly, interval: 1, end: .none))
reminder.addAlarm(EKAlarm(relativeOffset: .zero))
case "yearly":
reminder.addRecurrenceRule(EKRecurrenceRule(recurrenceWith: .yearly, interval: 1, end: .none))
reminder.addAlarm(EKAlarm(relativeOffset: .zero))
default:
break
}

reminder.dueDateComponents = dueDateComponents
if let dueDate = dueDateComponents?.date, dueDateComponents?.hour != nil {
reminder.addAlarm(EKAlarm(absoluteDate: dueDate))
}

do {
try Store.save(reminder, commit: true)
switch (outputFormat) {
case .json:
print(encodeToJson(data: reminder))
default:
print("Added '\(reminder.title!)' to '\(calendar.title)'")
}
} catch let error {
print("Failed to save reminder with error: \(error)")
exit(1)
}
}

// MARK: - Private functions

private func reminders(
Expand Down