Reputation: 283
I'm trying to integrate the iOS 11 drag&drop feature in an app, from Xcode 9 beta. I'm interesting on creating a NSItemProvider that can be understood by the standard calendar app. My drag starts from a UITableView, so only a single dragDelegate method implemented.
So far, I have tried the following:
let text = "Rendez-vous at \(clientName)"
let data = text.data(using: .utf8)
let itemProvider = NSItemProvider()
itemProvider.registerDataRepresentation(forTypeIdentifier: kUTTypeCalendarEvent as String, visibility: .all) { completion in
completion(data, nil)
return nil
}
let dragItem = UIDragItem(itemProvider: itemProvider)
And also tried by using the type identifier kUTTypePlainText. No luck, calendar app does not register the drop.
I can't find any official documentation about this. I'm hoping the calendar app is looking for some standard calendar data, and this is not limited to standard apps communicating with each other. For example, you can drag text from the notes app and drop it in calendar to create an event.
Anyone knows what I could try ?
Thanks in advance.
Upvotes: 3
Views: 947
Reputation: 880
TL;DR: The secret ingredients to get the Default iOS Calendar app to recognize your custom object conforming to the NSItemProviderWriting
protocol are the "com.apple.ical.ics"
type identifier and exporting your custom object as a iCalendar string (vEvent
).
I've been playing around with this for a few days and I finally managed to get it to work! I'll try and break down my steps as much as possible.
"com.apple.ical.ics"
after.loadData
function, mentioned above, will attempt to find an identifier that the drag destination (the Calendar app in this scenario) supports and that your app also supports, trying to get the highest fidelity first. For example:
public static var writableTypeIdentifiersForItemProvider: [String] {
return ["com.yourcompany.myEvent", "com.apple.ical.ics"]
}
public func loadData(withTypeIdentifier typeIdentifier: String, forItemProviderCompletionHandler completionHandler: @escaping (Data?, Error?) -> Void) -> Progress? {
switch typeIdentifier {
case "com.yourcompany.myEvent": // Your custom handling goes here
case "com.apple.ical.ics": completionHandler(createVEvent(), nil)
default: completionHandler(nil, YourAppError.someCustomError)
}
return nil
}
vEvent
string with your custom event information:
vCard
string for a custom contact. Checkout the demo project from that session. vEvent
s but there's much good information here and here.Here's a suggestion:
private func createVEvent() -> Data? {
let today = Date()
let dateFormatter = DateFormatter()
dateFormatter.dateFormat = "yyyyMMdd'T'HHmmss'Z'"
dateFormatter.timeZone = TimeZone(abbreviation: "UTC")
let startDateString = dateFormatter.string(from: startDate)
let endDateString = dateFormatter.string(from: endDate)
let todayString = dateFormatter.string(from: today)
var vCalendarText = "BEGIN:VCALENDAR\n"
vCalendarText += "VERSION:2.0\n"
vCalendarText += "PRODID:-//Your Company//App Name//EN\n"
vCalendarText += "BEGIN:VEVENT\n"
vCalendarText += "UID:\(identifier.uuidString)\n"
vCalendarText += "SUMMARY:\(name)\n"
vCalendarText += "DTSTAMP:\(todayString)\n"
vCalendarText += "DTSTART:\(startDateString)\n"
vCalendarText += "DTEND:\(endDateString)\n"
vCalendarText += "END:VEVENT\n"
vCalendarText += "END:VCALENDAR"
return vCalendarText.data(using: .utf8)
}
↑ Similar to the createVCard()
function from that session demo I mentioned earlier…
That's it! That's pretty much all you need. The hard part is knowing what identifier the Calendar app supports and how to make a vEvent
. Thanks to quiker for finding the supported identifiers.
One last tip: You can validate your iCalendar string with this nifty tool here.
Notice that this only allows for dropping your custom object. Dragging an event from the calendar to your app is a different story, but you can figure it out from my tips here. :)
Happy coding!
Upvotes: 7