NSSecureCoding

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;

}

 

@end

 

@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];

}

 

@end

 

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:

LOCALE: en_UA

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

Delegates vs Blocks

May 15th, 2015 — 12:58pm

This was something that came up at last night’s Cocoaheads so I thought I’d share my thoughts on it here. I’m way overdue for a post anyways.

Both delegates and blocks allow for callbacks, a way to allow an API user to provide custom logic to what is otherwise a black box. With the advent of blocks, there’s a temptation by some to use them everywhere. But as with anytime you have a new toy (ok, not so new in this case but newer), you must resist the temptation to stop using old stuff when is still better suited for the situation.

So, when do you use delegates? Delegates are for when there will be a closer, long term relationship between objects. There is usually a well defined interface for the delegate to follow. The delegate itself is probably maintaining state and consolidating logic for this purpose. From the point of view of the object using the delegate, it is a one-to-one relationship (the delegate might not actually be monogamous in this relationship and be a delegate to several objects). You can rely on one object to know how to handle these cases and expect some level of self consistency.

How about block callbacks? These are best suited for more ad hoc cases. Usually these blocks are called immediately or up to a few times in the not too distant future. An example of the former is having a block called on the elements in an array, after which, control is returned to the caller. For the latter, completion or progress handlers are typical examples, where you initiate some longer running process and want to specify behavior for certain events. The reason blocks are good for this situation is that they can capture the immediate context and use that in the custom code. This is also why blocks are less well suited for longer term relationships as they end up creating undesired or unintended dependencies that are harder to extricate.

The rules are not cut and dry but hopefully you’ll see that there’s a need for both. It’s both fortunate and unfortunate that blocks came into the Cocoa world as late as they did. While it did allow for the creation of some solid and clean use of delegates, it also allowed for some APIs which should have used blocks from the beginning (basically anything which takes a user-supplied context parameter). Hopefully Apple will clean these up over time but for your own APIs, be mindful of the differences.

Comment » | Cocoa, OS X, Programming

Hazel 3.3

October 20th, 2014 — 2:41pm

Hazel 3.3 is finally here. It has a bunch of neat additions that I think experienced users will find handy (in particular, all the things you can do with custom attributes now). I won’t list all the features here but check out the release notes if you want to learn more. Also, while 3.2.7 for the most part works fine on Yosemite, this version fixes all those pesky cosmetic issues, including the dark mode menubar icon which a lot of people seem to be fixated on. I think this is a great release as it ties up a bunch of loose ends and really opens Hazel up to workflows that weren’t able to be accomplished before without the aid of custom scripts. Speaking of which, Yosemite introduced JavaScript  alongside AppleScript as a way to automate apps, so JavaScript is now an option in Hazel as well. So for those of you who find AppleScript unpalatable, now you have an alternative.

While I will continue to do maintenance releases, my thinking at this point is that 3.3 will be the last of the version 3 series as I start shifting into thinking about version 4. I think version 3 has had a phenomenal run and I hope to make version 4 a worthwhile upgrade. It’ll be a while so for those fearing version 4 around the corner, you should’t worry. I’d be quite surprised if I get it out before the end of 2015.

Many thanks to all the testers who banged on 3.3 during its beta. Got a lot of great bug reports including some at the last minute which helped to make Hazel not explode (at least not yet – if yours does, blame it on tester #347).

Enjoy.

1 comment » | Hazel, Noodlesoft, OS X, Software

My NSConference talk

October 14th, 2014 — 1:51pm

Just found out this weekend, somewhat belatedly, that my talk at NSConference 6, “Life Outside the Mac App Store”, is available on Vimeo. I don’t have the nerve to watch it myself but given recent events, you might find it interesting.

Comment » | Business, Conferences, Hazel, Noodlesoft, NSConference, OS X, Software

The Long and Winding Road

August 29th, 2014 — 4:13pm

There have been a bunch of posts lately about the feasibility of being an indie dev in the Apple world. Brent Simmons has been doing a good job of linking them all so I suggest checking out his blog.

I’ve been doing Hazel for over seven years and I think it’s fairly safe to say that I’ve been successful doing it. I’m making more money than I did employed at other companies and I’m much happier with my job.

But it wasn’t easy and it wasn’t overnight. A common sentiment I see among a lot of new devs is that if they aren’t living full-time off their app in the first year, it’s a failure. I know when starting out there is the hope that your app will be an immediate success. It’s fine to hope that; after all, its our dreams that drive us to succeed. But you shouldn’t expect it. I would say it took about three years before I could be comfortable living off of it full-time. Yes, I did quit my job to do Hazel full-time from the get-go but I was living off of substantial savings in the beginning. 

During that initial period where you are struggling to get your app recognized, you try all sorts of things. You keep coming up with that one thing that you think will cause your app to break through. Maybe it’s some killer feature. Or lowering the price. Or maybe some promotion. Those things may or may not help but in reality, there’s no panacea. Things did explode for me after my version 3 but there was no one thing that did it, but instead it was a culmination of all the hard work that went into the product up to that point.

One thing that I did learn is to have a healthy respect for randomness. Luck plays a huge role and you can’t always attribute one’s success or failure solely on their decisions and actions. I highly recommend reading Leonard Mlodinow’s book, The Drunkard’s Walk: How Randomness Rules Our Lives. I won’t recap the points therein but I consider it pretty mind-blowing as it debunks many notions of the correlation between success and performance. The point is that a good deal of your app’s success depends on luck. That doesn’t mean you sit back and just let fate decide; you still need to work to improve your chances. Just realize that there’s a big chunk you can’t control and that on some level, you need to be ok with that.

And speaking of sitting back and waiting, don’t rely on the App Store. Many devs I talk to seem to think that their job is done once they submit their app. I did a talk at NSConference earlier this year about the Mac App Store and I think a particular point from that is relevant here: Apple is not your marketer. I’m not the best person to speak about marketing and whole books can be written on the subject but, in short, you need to do your own. Looking at the top grossing apps in the Mac App Store, most of the third party apps there I first heard about about outside the App Store. Many of them were even big before the App Store and I’d say they’d still be successful without the App Store. Why? Because they marketed their product and built up the userbase themselves.

In the end, realize it’s a marathon and not a sprint. It’s a long, hard road and not everyone makes it. But whether the journey takes you to the promised land or not, value it for what it is. For me, I’ve learned a ton and am still having fun working on my app. Some day, I will stop developing it but until then, I am enjoying the ride.

Comment » | Business

Hazel in Books

June 27th, 2014 — 12:24pm

It’s nice to see Hazel in books. Even in digital form, the book format gives me more of a sense of permanency than say articles on some site. So, when I see Hazel mentioned in a book, I can’t help but think of Hazel as being a more permanent fixture in people’s computing lives. The most recent entry is Joe Kissel’s book “Take Control of Automating Your Mac” which has a little section talking about Hazel. While most readers of this blog are already familiar with Hazel and Mac automation in general, this book is a great introduction for the novice in your life who is ready to delve deeper into their Mac. It also contains coupons for many of the apps mentioned in the book.

And on the topic of Hazel in books, I’m way overdue in mentioning “Mac At Work” by David Sparks. This came out a while back but is a great intro to using your Mac in a work environment. Also, it’s available as a physical book, which is always useful for propping up an uneven couch leg (after you’ve read it, of course).

I’m still waiting for Hazel to be incorporated into some work of fiction. Too bad Stieg Larsson isn’t around anymore as he had a tendency to go into way too much detail about characters’ computer configurations.

Comment » | Books, Business, Hazel, Software

Understanding OS X Betas

June 3rd, 2014 — 12:41pm

With OS X Yosemite going public beta, I feel that it’s important that people understand what that entails. This happens almost every year but it seems worthy of a reminder.

Beta is the development phase where the product is not finished but ready to be tested. Operative term: NOT FINISHED. What does this mean to you?

  • There Will Be Bugs
  • Many apps will not work.
  • Things may be slow.
  • You may lose data.
  • Issues will be addressed on the developer’s schedule, not yours. Even until some time after Yosemite is released, apps may not be ready yet.

In short, if you use your machine for any type of important work, DO NOT INSTALL THE BETA. It’s not our, or Apple’s, fault or responsibility if you are unable to get anything done because you didn’t heed this advice. If you must install it, in the very least install it on another drive/partition or virtual machine (VMware, Parallels, etc.) so it doesn’t impact your main installation.

And remember the purpose of the beta is for the developer to test their product, not for you to get a sneak peek at what’s new. If you find a bug, report it to the developer. And I mean REPORT it. Do not instead:

  • Talk about the bug on some site that the developer will not see.
  • Review an app running on a beta OS or is in beta itself.
  • Tweet a complaint with few details (Twitter is horrible for bug reporting).
  • Stay quiet about it and assume the bug will be magically fixed.

If you really care about the bug getting fixed, then tell the developer directly, providing necessary details. If possible try and figure out if the bug is Apple’s or ours and email the appropriate party. In general, during the early beta stages, the bug is probably Apple’s. Near the end, it’s probably ours. We do want to fix things but not reporting them properly is counterproductive to that.

With everyone’s cooperation, hopefully the transition to Yosemite will be a smooth one. Ok, maybe not, but at least I tried.

1 comment » | Business, OS X, Software

Hardcoding Classes

May 15th, 2014 — 12:58pm

When referring to class object, you can refer to it directly by name. When creating an object, you can do:

[[Foo alloc] init]

There are cases though where you want it to be a bit more dynamic. Say you want a class method which creates an autoreleased instance. You might do something like:

+ (id)fooWithName:(NSString *)name
{
   return [[[Foo alloc] initWithName:name] autorelease];
}

The problem with this, of course, is that if you have a subclass, SubFoo, then +fooWithName: will return Foo objects, not SubFoo. To fix this, instead of hardcoding the class, refer to it dynamically:

+ (id)fooWithName:(NSString *)name
{
  return [[self alloc] initWithName:name] autorelease];
}

Since this is a class method, self refers to the class object. Now, no matter which subclass you are in, the proper type of instance will be created. And if you are calling class methods from other class methods in the same class:

+ (void)doBlah
{
  [self doBlech];
}

Hardcoding the class here would call that specific class’s version of the method, even in subclasses, which is usually undesirable.

If you are in an instance method, you can likewise do something like:

- (id)copyWithZone:(NSZone *)zone
{
    return [[[self class] alloc] initWithName:[self name]];
}

Now, this is all well and good. In general, it’s a good idea to to use [self class] instead of hardcoding the name. But let’s take this example:

bundle = [NSBundle bundleForClass:[self class]];

Seems ok. You want the bundle where this class comes from. Or do you? Suppose Foo is in a Framework and SubFoo is in an app using that framework. The above code will return a different bundle for SubFoo than it does for Foo. Depending on what you want to do with that bundle, that can be a bad thing. For instance, finding a nib. Unless it is expected that the subclasses provide their own nib, chances are, the nib you are looking for only exists in Foo’s bundle and the above will fail. The fix? Hardcode the class:

bundle = [NSBundle bundleForClass:[Foo class]]

Here’s an odd case that I ran into some time back. Suppose Foo implements a protocol that has an optional method -bingo. In SubFoo, you implement -bingo but you’re not sure if the superclass (Foo) implements -bingo itself as part of the protocol. If it does, we want to call super but if it doesn’t we can’t. The documentation is silent on the subject so we can’t rely on any particular behavior. How do we check it at run-time?

- (void)bingo
{
   ...
    if ([[[self class] superclass] instancesRespondToSelector:@selector(bingo)])
   {
       [super bingo];
   }
}

Besides the compiler warning that Foo may not implement -bingo, it looks like it works. If you are a SubFoo instance, it gets the Foo class and asks it if its instances can receive -bingo. Ah, but what happens when we have a subclass of SubFoo called ReallySubFoo. Let’s also say that Foo doesn’t implement -bingo. When the above code is called, from a ReallySubFoo instance, we get a SubFoo class which then gets asked if it responds to -bingo. It does because we implemented it in SubFoo. Problem is there is no super implementation of -bingo and we get a crash. Again, the fix is to hardcode the class:

- (void)bingo
{
  ...
   if ([Foo instancesRespondToSelector(bingo)])
   {
       [super bingo];
   }
}

In most cases, dynamically getting the class is the right thing to do but don’t do it indiscriminately. Take a moment to think about how it will play out in subclasses. You may be surprised.

Comment » | Cocoa, OS X, Programming

Back to top