Ten Years

September 6th, 2016 — 7:00am

Today marks the 10th anniversary of Hazel. On September 6, 2006, Hazel 1.0 was released and after all this time, it’s still going strong. I’ve already shared my thoughts on why I took this journey and how I managed to stay on this road so I won’t rehash that here. What started as a product developed in my bedroom is, well, still a product developed in my bedroom, but now I can say it’s a successful one.

Hazel back then was a much more basic product. You can check out the release notes page, which documents everything back to the beginning. It’s interesting to see what features were added later, many of which are probably considered essential now.

As for the future, who knows? While the list of big features I want to implement is much shorter now, there are still a bunch of smaller things I want to put in. I can’t say whether I’ll still be doing this in another ten years but I have no intention of quitting now.

And with today being the anniversary, I need to celebrate in some way so, from today until 3am (EDT) tomorrow (Sept. 7), I am offering Hazel at its original price from 10 years ago. That’s $16 (50% off). The family pack and upgrade didn’t exist back then but they’re 50% off as well. No special code or link. Just go to the store and buy. If you don’t have a copy yet, here’s your chance.

2 comments » | Business, Hazel, Noodlesoft, Software

On Dynamism

May 23rd, 2016 — 7:15pm

I’ve kept my hat out of the discussions of Swift vs. Objective-C for several reasons, but the main one is that frankly, I’ve been busy. I ship a product, one that I would consider successful, and recently shipped a major release. What does that mean? It means that debating these types of issues and playing with the latest thing is a luxury. Because I have a shipping product with a lot of users, I can’t just change things unless I find that it adds value to my users. My job is to solve my users’ problems. Now that I have a little bit of time to do so, this is one lens I’m hoping to add to this discussion

In addition, years ago, when Java was introduced, I jumped on that bandwagon, leaving behind Objective-C. It started out writing replacements for Foundation and AppKit in Java and ended with writing large scale servers. I was on that wagon for a good 10 years before returning, so I wasn’t a tourist there. This is yet another lens I also want to add to the discussion as history seems to be repeating itself and many younger people seem to be missing out on this part.

But let’s start with the current issue about dynamism since that’s the hot topic.

One thing many people seem to overlook about the dynamism of Objective-C is that it enabled NeXT (and Apple) to provide better GUI tools. Using dynamism, they were able to make GUI building declarative in nature. Connect this to that. Call this method. All stored in a file that was (and still is) data, not code. Competitors at the time (and today) resorted to code generation which is fragile and, ironically, unsafe. Yes, you could have a more declarative file format, but implementing that in using a static language required a lot of hard-coding and switch statements. Not the elegance that many people claim to be moving towards.

I’m not saying that a language has to be purely dynamic but it shouldn’t be purely static either. It think it’s spurious not to credit a level of dynamism for the quality of apps on Apple platforms over the years, and to be pedantic, the NeXT ones as well – many of which were considered the best on any platform at the time. To deny that, I feel, shows a lack of understanding of what has made the platform great all these years.

Moving from a dynamic language to a more static one seems refreshing at first. I went through the exact same thing when switching to Java. Yes, I can hear some of you grumbling “Java” out there but the irony is that there are more similarities in the evolution between Java and Swift than you may want to admit.

Everything seemed so much “tighter” in Java. Interfaces (Java’s version of protocols) everywhere! No more pointers! Checked exceptions! It’s all so new and exciting. You might just have to take my word on this, but any new language that you really like will always seem to make you “more productive”. A colleague and I (plus maybe one, or one and a half, devs who were on the periphery) implemented our own version of Foundation and AppKit in what I would consider a pretty short amount of time. Other people at Lighthouse can correct me on this but my recollection was that it was on the scale of months, not years. You can read up more on it here.

Was it the language? At the time we might say yes but in hindsight, I’d say it was just that we were just super excited and into the project. I was working with an exceptional team and being a new platform, we were excited about making our mark on it. Yeah, there were bits about Java that were better than Objective-C. But there were bits that were worse. The point here, though, is that until the honeymoon is over and you are maintaining a project over several years, it’s hard to make real proclamations about productivity. Truth be told, I’d say the biggest impact on software quality and productivity is the quality of engineers regardless of any other factors.

Looking back on my years with Java, I find the biggest problems with the platform was this idea that everything can be done by the compiler. It sounds great on paper but when implemented, it resulted in all sorts of contortions to work around not letting any dynamism in. It resulted in a lot of glue being written that I wouldn’t have to write in a more dynamic platform. It resulted in cascading changes across code to make trivial modifications just to appease the compiler. Maybe in a future post I’ll go into specific language features that I found problematic and show parallel trends/features in Swift but let’s just say that less is more. It’s an aesthetic that I always felt that put Apple above the rest and something which I feel like in some ways is being abandoned.

Now, I’m sure many of you will just paint me as an old, stubborn Obj-C luddite. And it’s true on some level but that doesn’t mean you should discount my experience. Many of us old fogeys are working on products that have shipped for years. That means that (1) we are solving user problems (2) have immense experience with doing app development (3) really understanding the frameworks (4) have been maintaining a code base.

Let’s go through these points:

1. We are solving user problems.

We have real problems to solve for people outside of ourselves. We need to be practical to survive. Users don’t care about the tech underneath. We have to ship them solutions. This oftentimes requires rapid response. This also oftentimes involves working around Apple bugs, bugs that never get fixed and will only grow in number as Apple maintains their yearly release schedule (something to think about for proponents of the “final” keyword). Agility and flexibility are paramount here. Reality is not pretty. The user could care less how beautiful your code is if you aren’t solving their problems.

2. We have immense experience with doing app development

I want to emphasize the “app” part of that. A common argument for very strict type safety are for cases which aren’t apps. The predominant software in the Apple ecosystem is end user applications. Let’s try and not lose focus here and argue the case for categories of software that aren’t Apple’s forte. If you are writing some life-critical software on an Apple platform, god help you. Even with Swift, you are stuck with Xcode and Apple’s insistence on yearly new OS releases.

3. We really understand the frameworks

I would take many of the arguments for Swift more seriously if there weren’t strawman examples set up on the Obj-C side. You’ll have to dig up Brent Simmons’ posts on this, but when I see the kooky examples of horrible old Obj-C way, what it tells me is more that you don’t understand the frameworks. It resembles informercials where people are fumbling opening a jar and then are presented with some useless gadget to solve a non-existent problem. I’m sure things can be done better than they are now but providing bad examples indicating ignorance of what it is you are trying to replace doesn’t help your argument. And I have seen plenty of examples of new people replacing what they don’t understand. Please don’t be one of those people.

4. We have been maintaining a code base for a while

I have been writing Hazel for ten years. Many of us old-time app developers are in similar positions. Speaking with many other devs, most of whom write iOS apps, I’d be lucky to find those that have maintained a code base for a third of that time. Sure, there are parts of the code that are ugly, but that happens on any platform. Same thing happened on Java. No matter what you do, time is always against you. When I was working with Java, I found the technical debt grew in a different fashion. It was hard to change anything. This was because changes in stricter languages tended to cascade. Because everything has to declare everything about itself, changing code turns into a Facebook feed, except where you and everyone else are obligated to respond to every stupid pic and forward of Snopes-bait. With a more dynamic code base, I can say I have less code to maintain and that I can be more agile. Not that it’s without it’s own problems. But let’s not act like type-safety is a panacea.

And to give more of my personal perspective on the language debate, I’d love to have something better than Objective-C. But I’d also like to have something better than Swift. My personal pet peeve is that we are in the 21st century and we still have to give a shit about the storage type of numbers. I’d be happy to get rid of scalar primitives entirely.

Ultimately, though, I need something that solves my problems. My problems aren’t performance. They aren’t type-safety (maybe it’s just me, but I rarely have issues with it and when I do, the time it takes to fix it is far less than the time specifying everything to a tee everywhere else). They aren’t being able to write clever VDLs. For me, it’s writing apps that solve my users’ problems and getting them out in a timely fashion. As it stands now, Swift (at least pure-Swift, or even current Swift as a non-ABI-stable moving target) does not do that for me.

Now maybe these same problems can be solved in a static way but what I’m not seeing from the static-camp are (decent) solutions. What I’m seeing are either hand-waving or the same crufty code-generation, write tons of repetitive boilerplate type of solutions that I had hoped we had left behind in the 90s.

What I do see makes me worry that it’s not the experienced app-writers that are being heard. Unfortunately, many of us are too busy to sit in the various mailing lists and forums. Yes, ok, we lose. But we all lose if those creating the platform don’t draw from the experience of those who have built upon it successfully. As someone who has done both (write frameworks and apps) and seen what happens when the former ignore the latter (the rise and fall of Java), I’m hoping history doesn’t repeat itself.

That’s my somewhat meandering brain dump for the moment as I do have to get back to working on my next patch. Sure, these are just my opinions but my hope is that we can at least look for more balance in our approaches to solving actual problems.

36 comments » | Cocoa, OS X, Programming

Adventures in Email Hosting 2

May 19th, 2016 — 3:10pm

Yes, another journey into the world of email hosting. This time it’s a bit different. Instead of receiving email, today we talk about sending it.

It seems that during my recent launch I became collateral damage in the war against spam. Between a couple of email campaigns and a bunch of license emails, I had sent out a good amount of email; enough to cause me a ton of headaches as certain mail services, actually, mainly one: Gmail, decided to mark a lot of it as spam (if I was lucky) or make them disappear entirely.

The way things were set up, my email campaigns were sent out by Campaign Monitor, my license emails by my server and my regular “interactive” emails via Rackspace’s email service (as described in a previous installment). The ones I had the most problems with were the first two, as they were repetitive messages sent out in volume.

For a while, I’ve had SPF records set up. What are SPF records? In a nutshell, it’s a way for you to specify which mail servers are the “official” servers your email comes from. This is to help identify mail coming from you as opposed to a spammer posing as you. The way you set it up is to create special DNS TXT records listing out the specific servers for your domain.

Apparently, this isn’t enough. Seems like there’s another layer you can implement: DKIM. With this, you have your mail server sign outgoing emails so mail servers at the receiving end can know that emails are definitely from you and definitely not, once again, from a spammer imposter. So, I went ahead and set up OpenDKIM on my server. You can find various guides out there on how to install it on your OS and integrate it with your MTA (I use postfix and hooking the two up was pretty easy). You also have to add a DNS record listing your public key so other mail servers can verify your signatures.

Even after doing that, it still didn’t seem to appease the Gmail gods. I found this page which recommended yet another thing: DMARC. Here, you specify a policy as a guideline to mail servers on how to handle your email. One of the things you can specify is an email address where mail services can send you reports on the emails you send them. And you guessed it, you implement it by creating a DNS record.

Being desperate, I thought I’d do it, hoping that Gmail would send me a report telling me what I’m doing wrong. Soon after, I started receiving DMARC reports from all sorts of mail services (Microsoft, Yahoo, AOL, etc.). Over ten days later and guess who still hasn’t sent me one.

I’ve been getting fewer reports about not receiving emails lately but that’s mainly because of decreased volume since launch. It’s still unclear whether Gmail is binning my emails at a high rate or not. Nonetheless, if sending out emails are an important of your business, I recommend doing the above. Even if Gmail seems to be hard to please, other mail services are more appreciative of the gesture.

Note that there is also the option of relaying all my mail through Rackspace. It’s still a possibility but (a) I’m afraid of poisoning the well since my email is already being marked and (b) using a shared relay opens you up to being blacklisted because of someone else’s misdoings. All in all, I feel that some level of redundancy is ok here.

When implementing the above, you can check the headers of an email received at the other end to make sure everything is set up properly. Here’s one from an email sent from my server to my Gmail account:

Authentication-Results: mx.google.com; dkim=pass header.i=@noodlesoft.com; spf=pass (google.com: domain of www-data@noodlesoft.com designates 2001:4801:7824:103:be76:4eff:fe11:5179 as permitted sender) smtp.mailfrom=www-data@noodlesoft.com; dmarc=pass (p=NONE dis=NONE) header.from=noodlesoft.com

As you can see, it shows that my SPF and DKIM passes. That doesn’t guarantee anything but it helps.

You can also check out Google’s Postmaster tools site. It will give you feedback on various metrics concerning email from your site. To set it up, you have to create a DNS record (see a running theme here?) with text it supplies you so that it can see that you control the domain. After that, it will track your site.

Also, yes, DNS once again: make sure all your regular DNS records are set up properly. Not only do you want an A and PTR record for IPv4, but also a AAAA and PTR record for IPv6 as more mail servers nowadays are checking for that.

Until next time, here’s hoping I don’t have to resort to human sacrifice to get Gmail to accept my messages.

Comment » | Business, System Administration, Tools, Web

Cleaning Out Turd Files

May 17th, 2016 — 12:37pm

A little pet peeve of mine are the turd files that Emacs leaves behind all over the place.

For those that don’t use Emacs, when you edit files with it, it will keep a backup of the original file in a file with the same name, except with a tilde (~) at the end. The problem is that you have to manually clear them out. When you are jumping around in Terminal editing config files left and right, trying to get something working, you tend to forget where those files are. And yes, you can turn that off, but I like having the backups and yes, you can specify that it store its backup files in a common place but it makes it harder to recover files should I need to check the backup version, especially when I’m editing a bunch of files with the same name (index.php anyone?).

Since Hazel before version 4 only operated on actual folders/directories in the filesystem, it couldn’t really handle cases like this where the files are strewn all over the place. Now with Smart Folder support, you can create one to match all the turd files and have Hazel clean them up for you. Here’s a Smart Folder and Hazel rule you can use yourself:

Turd Cleanup

One important bit about the Smart Folder conditions is that I use “Filename” and not the more readily available “Name” or “File extension” attributes as the ~ is tacked onto the end regardless of whether there is an extension or not.

And of course, no reason to limit it to Emacs turds. Go ahead and edit the Smart Folder to include whatever other poops you have on your system.

Comment » | Hazel, Software, System Administration

Moving Off Of Feedburner

May 10th, 2016 — 9:37am

I’m finally going to start transitioning off of Feedburner. I haven’t done this before so I stand a good chance of screwing it up. From what I understand, when I switch things over, Feedburner should do a redirect to the new feed. If you find that it doesn’t work, you can always re-subscribe by going to the site.

As for where I’m taking the feed, I’m going back to hosting it myself via WordPress. There seem to be plugins that do the minimal statistics collection that I care about and I’m tired of using services that just end up shutting down.

Hopefully, you won’t notice a thing but I thought I’d post just in case something goes wrong.

Comment » | System Administration

Hazel 4 Development/Launch Post-Mortem

May 5th, 2016 — 7:37pm

It’s been a bit hectic because of the launch yesterday but I finally have a moment to post. Yes, Hazel 4 is finally out. You can find the release notes here.

Development was a bit rocky. I played with a bunch of different features but some of them didn’t quite pan out in a way that I liked. It felt like wasted effort in that that work didn’t result in a usable feature but many of them were only shelved temporarily. Oftentimes I end up having that eureka moment which would allow a shelved feature to be finally realized so something to look out for in future point releases.

That said, I’m happy with the features that I did get to work. They seem simple on paper but involved a bit more thought than would be expected. Sync is always tricky and getting the preview feature down to something as simple as it is now took a little doing.

Along with that was the site re-design (courtesy of the folks at Brotherhood). The previous site was mostly static. Adding content involved editing raw html pages and adding them. It was enough to discourage me from doing it often and discourage me it did, as I ended up leaving the site very outdated. The new site is backed by WordPress which will hopefully remedy that. The point here being that content can be added more easily using tools like MarsEdit or WPs web editor. I’ve already added a few posts (a review and a couple of knowledge base articles) since the launch.

Also, the new site design is a bit more stripped down and streamlined. I’ve tried to reduce navigation in favor of search. Most of the site is searchable via the form on the support page so I recommend going there first and doing a search if you ever have a question about Hazel.


The launch itself went relatively smoothly (except one incident – more details below). I can credit most of this to using a VPS (virtual private server). VPSes are great as you can clone, rebuild and resize them as needed. It gives you an amazing amount of agility when deploying servers.

Before the launch, I set up a clone server so I could set up and test the new site. Since it’s a clone, no need to reinstall and reconfigure everything (though you do have to make some changes in places where the IP address or hostname is stored). You end up with pretty much an identical server to play with which went a long way towards making sure things were working properly.

When I launched, I transferred the new stuff over to the live server. That part went with little drama but then disaster happened: I underestimated the load from tons of eager customers. The problem was that I had sent two email campaigns. One to those on my mailing list and another to those who purchased recently. The latter group received a message with instructions on how to get their free upgrade license. And guess what all those people decided to do immediately upon seeing that.

The result was that the site got slammed. More specifically, apache was overloaded. Enter VPS awesomeness #2: I was able to resize the server on the fly. It took a little while (maybe 15 minutes though it felt much longer) but the old server was still able to run, albeit very sluggishly, until the last minute when the conversion finished and it rebooted. After that, the site ran like butter and it was smooth sailing (at least as far as the server went).


Aside from some minor glitches (version 4.0.1 released this morning should address some of them), the launch has been pretty great. I just had the best day in sales in Hazel’s history so I’m pretty happy about that. My thanks to everyone who contributed, including Brotherhood, Jono Hunt for his icon and UI work, my beta testers, my friends in the Mac dev community and of course, all my customers who’ve been very supportive of Hazel all these years.

Comment » | Business, Hazel, Software, System Administration, Web

Hazel 3.3.8: Getting Past The Gates

April 5th, 2016 — 12:13pm

In case you haven’t been keeping up with the Twitter feed or the forums, Apple introduced a glaring bug in Gatekeeper in 10.11.4. Any non-app bundles (that includes preference panes, screensavers, plugins, etc.) are rejected by Gatekeeper regardless of whether they are signed or not. For those of us that ship such bundles, this is what I’d consider a big deal.

The workaround itself isn’t so bad: either right-click and select “Open” or drag the pref pane into System Preferences. For users curious enough to email support, it’s not such a bad thing as I can suggest one of the workarounds. The problem it gives new users installing it for the first time a bad impression of the software, not realizing that it’s actually Apple’s fault. I can’t really measure this type of thing but I think it’s safe to say a good number of them just nope out of there without contacting me. The result is that my software has an association with being untrusted.

Daniel Jalkut gives a more detailed account of the mechanics of it here so I won’t go retread that ground.

After filing a DTS incident, Apple confirmed it as a bug (I’ve filed rdar://25466753). There was no information about when a fix would be made available and given that Apple is not known for being nimble about these things and that I was losing customers, I followed the advice of some colleagues and took matters into my own hands.

The result is that starting with Hazel 3.3.8, it will ship with an installer app. The installer app still goes through System Preferences as I still think that its installation process works well. Note that an installer package was also an option but I couldn’t figure out if/how to make it use a previous install location if the software was already installed (If anyone knows, I’d be happy to hear about it just in case I have to resort to it in the future). Hopefully now I can direct my efforts back to getting Hazel 4 shipped.

One can debate how much Apple cares about non-app-bundled software but when the workaround is to suggest people bypass Gatekeeper, they should be very concerned. False positives only erode the confidence people have in your security systems and you don’t want them to get in the habit of casually bypassing them.

4 comments » | Hazel, OS X, Software


November 9th, 2015 — 1:30pm

I’ve had it on my list to convert all my NSCoding classes to use NSSecureCoding and this recent post reminded me so I figured now is as good a time as ever.

How do you go about using secure coding? Here are the Apple docs. Unfortunately, those docs don’t give the whole picture. There are a few details you need to be aware of.

When you subclass an object that supports NSSecureCoding, if you override -initWithCoder, you must also implement +supportsSecureCoding

Even if you don’t call -decodeObject:… in your -initWithCoder: you still have to implement +supportsSecureCoding  and return YES in your class, even if a superclass already did it.

When decoding collections, you must specify the classes of the objects it contains

This is actually documented elsewhere in Apple’s XPC docs. Suppose you have an NSArray. You can’t just say -decodeObjectOfClass:[NSArray class]…. That’s because when NSArray unarchives itself, it can’t specify the class of the objects within it, since they are typed as id  Therefore, you need to specify the classes of those objects for it. In this case, you would use -decodeObjectOfClasses:…, specifying the classes contained by the array as well as NSArray itself.

Some objects will be disabled after secure decoding

Objects like NSPredicate and NSSortDescriptor can take in key paths or selectors making them potentially unsafe. As a result, they are disabled after being securely decoded. To re-enable them, you have to call -allowEvaluation (presumably after doing some sort of check).

You have to turn on secure coding on the coder instances

Sounds a bit obvious except that most of the time you are using the class, not instance, methods to archive/unarchive. Those do not use secure coding nor are there similar class methods for secure coding. Instead, you’ll have to create an instance, set it to secure coding and encode/decode the root object yourself. Of course, you can bundle this up into convenience methods and stick it into a category like so:


@implementation NSKeyedArchiver (NoodleExtensions)


+ (NSData *)noodleSecurelyArchivedDataWithRootObject:(id)rootObject;


    NSMutableData   *data;

    NSKeyedArchiver *archiver;


    data = [NSMutableData data];

    archiver = [[NSKeyedArchiver alloc] initForWritingWithMutableData:data];

    [archiver setRequiresSecureCoding:YES];

    [archiver encodeObject:rootObject forKey:NSKeyedArchiveRootObjectKey];

    [archiver finishEncoding];


    return data;





@implementation NSKeyedUnarchiver (NoodleExtensions)


+ (id)noodleSecurelyUnarchiveObjectOfClass:(Class)classObject withData:(NSData *)data


    NSKeyedUnarchiver       *unarchiver;


    unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];

    [unarchiver setRequiresSecureCoding:YES];

    return [unarchiver decodeObjectOfClass:classObject forKey:NSKeyedArchiveRootObjectKey];



+ (id)noodleSecurelyUnarchiveObjectOfClasses:(NSSet *)classes withData:(NSData *)data


    NSKeyedUnarchiver       *unarchiver;


    unarchiver = [[NSKeyedUnarchiver alloc] initForReadingWithData:data];

    [unarchiver setRequiresSecureCoding:YES];

    return [unarchiver decodeObjectOfClasses:classes forKey:NSKeyedArchiveRootObjectKey];





Hopefully this will make your conversion to NSSecureCoding go a little smoother.

Comment » | Cocoa, Downloads, OS X, Programming

Hazel on El Capitan

October 6th, 2015 — 1:05pm

For those who haven’t updated to El Cap yet or don’t follow the Twitter feed, yes, Hazel runs great on El Capitan (OS X 10.11). There are no known significant issues or bugs at this point and the current version runs fine as is. The only problem that I’ve found so far is an obscure and extremely minor UI feature. I will be issuing a patch at some point but there is no hurry on that front as I’d rather wait for some real El Cap bugs to surface.

If you do find problems, please report them to me. You can (in decreasing order of effectiveness) email support, post in the forums, or tweet. Those are the places I check and can confirm and fix the bugs I see there. I won’t see posts on other sites so please consider posting to one of the above places first. Whatever you do, please do not give credence to random statements about compatibility on other sites, especially when those statements were made months ago on an early beta of El Cap, and even more especially on a site with “rumors” in the name. I’ve received enough emails referencing such posts that I feel the need to weed those out specifically. Remember, this is the internet so choose your sources carefully.

In the meantime, work is continuing on version 4. I can’t say when it will happen but I will be doing a beta at some point so keep an eye on the aforementioned official channels for updates.

5 comments » | Hazel, Noodlesoft

Unpredictable Date Formatting

May 27th, 2015 — 5:24pm

Daniel Jalkut posted about his recent issues with NSDateFormatter. Having run into the same issue last month (rdar:/20512790 for you Apple folks keeping score at home), I’ve found that the issue is more confusing than that.

For one, it’s not just the 12/24-hour clock affecting HH in date formats. It seems that in some circumstances, in some locales, literal characters, like period (.), will be changed to colon (:). Not only that, this happens (at least in the case I found it) depending on whether you use hh or HH in your pattern.

Observe this output from my test program:


FORMAT: MMMM d, hh.mm.ss a

January 1, 13:00:00

FORMAT: MMMM d, HH.mm.ss a

January 1, 13.00.00 PM

“en_UA” is English as the language, but Ukraine as the region. It’s important to note that there are two components here, the language and region, encoded in the locale. This is important because Apple’s suggestion to use “US_en_POSIX” is incorrect. That only works if English is your language. To do it correctly for the user’s language (not every date format uses numerical months), you need to get the locale for their current language, grab the identifier, tag “-posix” to the end and get a new locale from that. I’m not sure if this type of scheme works for all languages (anyone have documentation to this effect?) so it’s possible this won’t work for some language out there. All in all, a bit of a pain and a bit fragile for something that worked properly before.

And before some of you bring up some argument about respecting the user’s preferences, in the case where I use it, the user is specifying the pattern in the UI and wants to specify a different pattern than the one that they used in their preferences. Also, this API is at a much lower level and should respect the literal pattern provided the developer. I’d suggest the current behavior only mangle the format string when you explicitly use +dateFormatFromTemplate:options:locale: leaving format strings that don’t use that method as-is. In the very least, provide a “literal” mode when it comes to interpreting the format string.


1 comment » | Cocoa, OS X, Programming

Back to top