iosobjective-cdesign-patternsmbcalendarkit

How can I improve this implementation involving NSDate comparisons?


I'm the author of a calendar library for iOS, and I'm thinking about how I can improve it, because it's become obvious that people aren't understanding it.

Right now, I have a datasource protocol that consists of just one method, and it returns the events to show on a given date:

- (NSArray *)calendarView:(CKCalendarView *)calendarView eventsForDate:(NSDate *)date;

But, I've found that in the two years of the library being available, most of the feedback I've gotten has been, "how do I make events appear on the calendar?"

I always thought it was pretty intuitive:

  1. Create some events. The framework has an "event" object to work with.
  2. Keep your events sorted by date in some data source, like a dictionary, or Core Data.
  3. Return the appropriate event objects in the datasource.

What else can I do to make it clearer to other developers how I intended this to work?


Solution

    1. Your user wants code she can copy and paste and then edit to fit her problem. She doesn't want to read about an interface and then implement it from scratch. So put a toy implementation of CKCalendarViewDataSource in your documentation. The toy implementation needs to work out of the box. Your user should be able to paste it into her project, compile, and see events in the calendar view. The toy can just use a hardcoded array of events (with calls to [NSDate now] as needed to ensure the default calendar month shows events).

      I think provide a working sample implementation in the README will do the most to alleviate your support headaches.

    2. Your user doesn't want to implement callbacks. She doesn't think in terms of waiting to answer a question; she thinks in terms of telling your library what to do.

      Add an events property to the view that is an array of CKCalendarEvent. Let the user set either the events or the dataSource. Note in the documentation and header comments that you recommend using the dataSource if there are more than a few dozen events. Raise an exception if the user sets both.

    3. If you keep getting asked “How do I make events appear on the calendar?” then put that language in your documentation. It's okay to be redundant in your documentation! Documentation is for humans, and humans need redundancy.

      You can have big section headers that say “How to Add Events to the Calendar”, and “How to Show an Event on the Calendar”, and “How to Make an Event Appear on the Calendar”, and they can all say the same thing (ideally with sample code) or just link to the “Showing Events” section.

    4. Explain in your documentation, near the top, that CKCalendarView needs a data source just like UITableView. Be explicit! You and I recognize the pattern, but apparently your users don't.

    5. Provide a better data source interface. Looking up events by NSDate isn't really ideal, because an NSDate specifies an instant in time, not a whole day. You want events that fall on a given calendar day, but you don't care about the time of day of the event. Are you passing midnight of the day of interest? What if the event is at 8 AM? Worse, what if the event is the next day at 12:01 AM, and daylight saving time starts on the day of interest? Not all days are 86,400 seconds long! Don't make your user muck around with NSCalendar; do it for her.

      I would prefer a message like this:

      - (NSArray *)calendarView:(CKCalendarView *)calendarView
          eventsOnOrAfterDate:(NSDate *)startingDate
          beforeDate:(NSDate *)endingDate;
      

      Pass midnight (or 1 AM on certain days in certain time zones) of the date of interest for startingDate. Pass midnight of the following day for endingDate. Note that you should allow the user to specify your calendar's time zone for computing these NSDates (but default to [NSTimeZone systemTimeZone]).

      It might be nice to specify the following message also, in case your user stores her events by calendar day and not with precise timestamps:

      - (NSArray *)calendarView:(CKCalendarView *)calendarView
          eventsForYear:(int)year month:(int)month day:(int)day;