This is where we start getting into the nuts and bolts of the infrastructure for the project by building models, extensions to make the use of these models easier as I put together date utilities to make my life easier.
Luckily, TaskManager makes things a little easier in terms of dates. We only have two major date formats to convert to and from a Date foundation class: "yyyy-MM-dd" and "yyyy-MM-dd HH:mm" (ie; "2024-10-31" and "2024-10-31 10:31").
We want to make this as simple for users of these functions as possible, so the API we’d like is this:
And the converse:
Gotchas
An optimistic idea of how to handle this might be to use convert via DateComponents. We can split the formatted strings into integer components and then convert to a date.
Looks good, right?
Any programmer that has worked with dates will tell you about the perils of dealing with Date conversions. Leap Day and the Daylight Savings Time are edge cases that need to be dealt with.
Apple’s DateFormatter handles both the string parsing and the validation.
By setting up the internal enum in a Date extension, we are able to leverage this model to keep track of our known format strings and ensure that using the TMDateFormat in the initializer and string conversion functions for a nice and clean API usage.
Reminder Needs
With an eye on what we want to do next, we know that we’ll need to convert dates into appropriate Set<Calendar.Components> for creating reminder entities within EventKit (this is how it will handle the difference between a day of reminder and a day with time reminder) similar to our .date and .dateTime formats.
Adding the components property to the TMDateFormat ensures the API will continue to be clear and easy to use.
Then in out Date extension, we can add a function to do the conversion:
Other Conversions
The universe of TaskManager date formats is small and while our .date and .dateTime formats are the heart of it. There are some other formats:
This is relatively straightforward, if a bit repetitive.
But there’s some infrastructure work that will need to convert our various parameter models into formatted strings and our TMDateType model.
By leveraging protocols and ensuring that each of the parameter models conform to them, we can easily create a uniform API for our parameter models.
Then we can convert to formatted strings with a useful extension to Int for string formatting them with fixed digits:
Looking at our DateParameters and TimeParameters models:
Converting to TMDateType will use this formatted strings and our Date extensions. By throwing errors for the failure cases, we can alert callers to incorrect data in our parameter models, when we try to convert them into Date objects.
That’s the bulk of the logic. The rest is just stitching parameter models together with some edge case logic to ensure that end dates are always after begin dates.
With that under our belts, we can add to Date extension, so that we can convert to and from our parameter models.
Unit Tests
One way to ensure that your code is at the level that you want it to be and reduce your bug count is to ensure that you test your code.
Unit tests will also set your code apart if perspective employers are looking over your code sample repos, because they’re an integral part of the day to day business of coding.
Let’s look at some of the unit tests in this sample project:
I’d recommend against trying to test everything. Just going with the main logic and verify the likely unhappy paths.
For example, in DateParameters and the DateParametersTests, I can do the end to end tests to verify that parameter models convert to and from dates properly and that tests the whole flow.
Flipping through the sample code, you’ll see we also test our formatted data to ensure that functionality as well.