Created
April 7, 2018 21:08
-
-
Save zats/4a8b365be47648d08aff7d43dff363be to your computer and use it in GitHub Desktop.
This is a sample of how to add actionable button for Reminders (private API); demo video: https://youtu.be/q7LrO3VhI64
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import PlaygroundSupport | |
PlaygroundPage.current.needsIndefiniteExecution = true | |
let reminderListName = "Test" | |
import EventKit | |
extension EKCalendarItem { | |
var hack_action: AnyObject? { | |
set { | |
setValue(newValue, forKey: "action") | |
} | |
get { | |
return value(forKey: "action") as AnyObject? | |
} | |
} | |
} | |
let modifier: (EKReminder) -> Void = { reminder in | |
guard let title = reminder.title else { | |
return | |
} | |
if let action = reminder.hack_action, !(action is NSNull) { | |
print("Action for reminder \"\(title)\" already set to \"\(action)\"") | |
return | |
} | |
let types: NSTextCheckingResult.CheckingType = [.address, .phoneNumber, .link, .date] | |
let detector = try! NSDataDetector(types: types.rawValue) | |
let range = NSRange(location: 0, length: title.count) | |
let results = detector.matches(in: title, options: [], range: range) | |
guard let result = results.first else { | |
return | |
} | |
let match = String(title[title.index(title.startIndex, offsetBy: result.range.location) | |
..< | |
title.index(title.startIndex, offsetBy: NSMaxRange(result.range))]) | |
if result.resultType == .phoneNumber { | |
let phone = match.filter { CharacterSet.decimalDigits.contains(UnicodeScalar("\($0)")!) } | |
print("Updating phone number: \(phone)") | |
reminder.hack_action = URL(string: "tel://\(phone)") as AnyObject | |
} else if result.resultType == .address { | |
let address = match.addingPercentEncoding(withAllowedCharacters: CharacterSet.urlFragmentAllowed)! | |
print("Upading address \"\(match)\"") | |
reminder.hack_action = URL(string: "https://maps.apple.com?address=\(address)") as AnyObject? | |
} else if result.resultType == .link { | |
print("Updating url: \(match)") | |
reminder.hack_action = URL(string: match) as AnyObject | |
} | |
} | |
func fetchAllReminders(for store: EKEventStore) { | |
let targetCalendar = store.calendars(for: .reminder).first(where: { $0.title == reminderListName })! | |
let predicate = store.predicateForIncompleteReminders(withDueDateStarting: nil, ending: nil, calendars: [targetCalendar]) | |
store.fetchReminders(matching: predicate) { reminders in | |
guard let reminders = reminders else { | |
assertionFailure("No reminders found") | |
return | |
} | |
if reminders.isEmpty { | |
print("No reminders passed the filter") | |
return | |
} | |
for reminder in reminders { | |
modifier(reminder) | |
do { | |
try store.save(reminder, commit: false) | |
} catch { | |
fatalError("Failed to save reminder \(reminder.title): \(error)") | |
} | |
} | |
do { | |
try store.commit() | |
} catch { | |
fatalError("failed to save reminders \(error)") | |
} | |
print("Saved") | |
} | |
} | |
let store = EKEventStore() | |
store.requestAccess(to: .reminder) { result, error in | |
if let error = error { | |
assertionFailure("\(error)") | |
} | |
if (result) { | |
fetchAllReminders(for: store) | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment