OmniOutliner 4.2

OmniOutliner 4.2 is available direct from Omni. (It’s been submitted to Mac App Store but isn’t up yet.) I don’t think I’ll make anyone too terribly sad if I say that it’s my favorite Omni app. I use it for a bunch of different things — outlining app architecture and features, presentations, articles, and so on. Things that later end up in Xcode or Keynote or BBEdit start out in OmniOutliner. It’s where I think and organize. Highlights: the new release (here are the release notes) updates the look for Yosemite and fixes a bunch of crashing bugs. I’m proudest of the crash bug fixes — crashing sucks, and fixing crashing bugs is fun. (Sincerely. I enjoy the detective work.) My theory about crashing bugs is that they cost more than most developers think they do. They cost you in goodwill (users hate crashes; users don’t think of crashes as acceptable inconveniences) and they cost in support and QA time. In my ideal universe, all developers everywhere would prioritize crashing bugs over everything else, all the time.

Posted by on 17 April 2015 | 1:05 pm

Dave on Silo-Free Software

Scripting News: I guess you could say I believe there are other reasons to make software, other than making money. Some people like to drive racecars when they get rich. What I want is to drive my own Internet, and for you to drive yours too.

Posted by on 27 March 2015 | 2:31 pm

Two Apps

My friends Chris Parrish and Guy English just released Napkin 1.5, a huge update to their already-awesome visual markup app. And cousin Michael and team just released Fantastical 2.0. You already know about it and you’ve bought it. I’m just reminding you that you have good taste. :)

Posted by on 26 March 2015 | 12:21 pm

OmniOutliner 4.2 Public Test

OmniOutliner, on Twitter: OmniOutliner for Mac 4.2 public test builds are now live! Contains Yosemite UI updates and other bug fixes. http://bit.ly/RGN6GC I’ve been helping work on OmniOutliner, mostly on updating it for Yosemite. There’s plenty more to do, but some of the obvious things — vibrant sidebar, new-style toolbar buttons — have been done. OmniOutliner is the first Omni app I started using (many years ago), and it’s still my favorite. If you’re not faint-hearted, and you’re an Outliner fan, then please grab the test version and help us make sure this release will be awesome.

Posted by on 24 March 2015 | 2:10 pm

New Blogging App: MyWord Editor

MyWord Editor, from Dave Winer, is a “a simple silo-free blogging tool that creates beautiful essay pages.” It’s open source. The announcement page has an aside about RSS that I enjoyed: Of course every MyWord user has a great full-featured RSS 2.0 feed. We love RSS and it feeds us and we feed it, it's doing great, and anyone who disses it is a mean rotten silo-lover.

Posted by on 23 March 2015 | 12:24 pm

NSSegmentedControl with Menus

Mac developers: you probably recall that you can set a menu for each segment in an NSSegmentedControl, and the menu appears when you click and hold. The problem with that is discovery — there’s nothing that shows that there’s a menu there. So here’s what I want to do: put a downward-pointing arrow or chevron graphic to show that there’s a menu. And make it so that if you click on the arrow directly, you don’t have to click-and-hold. But I don’t want to an entirely custom thing: I still want to use NSSegmentedControl. What’s the best way to do this? Just place arrow buttons on top of the NSSegmentedControl? I figure I can draw the arrows easily enough by subclassing NSSegmentedCell — have drawSegment:​inFrame:​withView: call super and then draw the arrows. But drawing isn’t enough — I want to have an actual button so you don’t have to click-and-hold. Yet it seems weird to just place buttons on top of a control. How would you do this?

Posted by on 19 March 2015 | 12:53 pm

Three Down, One to Go

I was an Omni fan for many years before joining the company. It’s not just because the apps are so good — it’s also because it works like this: imagine what the right thing to do is, then watch Omni do it. Right now the right thing, for our users and for the good of the apps, is to make them all universal — and as free upgrades for the iPad versions. We just released OmniOutliner universal. It joins OmniGraffle and OmniPlan — with OmniFocus next. (Which people all around me are working on.) We’re on a roll. (Me, I’m not helping at all with this universal stuff. I take no personal credit. I’m in Mac-land — my home! — doing Yosemite updates and fixing bugs.)

Posted by on 19 March 2015 | 12:36 pm

NetNewsWire Status: Everything I Know

People ask me — on Twitter, in person, in chat, via email — how NetNewsWire is coming along. (I just got another email this morning.) Answer: I don’t know. Yes, I do see Black Pixel employees in person once or twice a month, but they don’t tell me. (The employees I see don’t necessarily know. But, if they did, it wouldn’t be right to talk about internal stuff like that, so they don’t.) All I know is what I read on Twitter: We are still working on NetNewsWire, but don’t have any timeline information to share about future updates. NetNewsWire was sold to Black Pixel in June 2011. It has been updated: at least one 3.x update to the Mac version, and a Mac 4.0 beta program. I did, at one point (last Summer, I think), contact them about buying it back, and Black Pixel declined right away (we didn’t get as far as discussing terms). (It would have been a great story, right? NetNewsWire and I go from Ranchero Software to NewsGator; NetNewsWire gets placed safely in Black Pixel’s hands as NewsGator turns into an enterprise company; NetNewsWire returns to Ranchero Software.) And that’s all I know. I’d still be interested in buying it back, but I strongly suspect this is off the table, or so expensive that it wouldn’t make sense. (The expensive part isn’t the code, it’s the name.) What I would have done with it: Mac version only. Syncing would be via Feedly, Feedbin, etc. The idea is that it would be easy to get into — since it would sync with something you’re probably already using — and it would be easy to mix-and-match. You could run NetNewsWire on your Mac and Unread on your iPhone. (I use Unread: I’m a fan. There are other good readers, too.) I’d make it a for-pay app, probably $24.99 or thereabouts — and not really worry about it making money. (Because I wouldn’t need it to, and because I’d be working on it with a more powerful motivation: love.) Black Pixel could take the above as unsolicited advice: go Mac only, and sync with an existing service. One developer and an occasional designer could do this. But I suspect they’ve made other choices, and development is probably pretty far along — so, instead, I’ll just relax and let them surprise me. There are great developers at Black Pixel, and I stand by to be delighted.

Posted by on 18 March 2015 | 12:53 pm

OmniFocus, Vesper

OmniFocus 2.1.1 for Mac shipped yesterday with some crashing bug fixes. And a universal version of Vesper shipped a couple weeks ago. If you’re counting — and of course you’re not — that’s three shipments I’ve been involved in this year so far (counting the earlier OmniFocus 2.1). I’ve got a busy year planned. With perhaps a surprise in there. Feels good. (“Feels good,” I say — I’m trying to give myself a pep talk. I’m home with a head cold, and everything feels bad at the moment.) PS I’m working right now on a Yosemite update for OmniOutliner. OmniOutliner for Mac has been and remains my very favorite Omni app. And that’s in part because I love outliners. My history with outliners goes back to 1995. (Example: 15 years ago I wrote C code for reading/writing OPML. I don’t link to it because there’s something to learn from the code — there isn’t — it’s just remarkable to me that I’ve been doing this kind of thing for so many years. It’s because I like it.)

Posted by on 17 March 2015 | 1:25 pm

Swift Blocker

I did some work to switch Vesper over to frameworks. In a framework I built for FMDB, a public header file has the following line: #import "sqlite3.h" That’s not allowed by default. But there’s an Xcode setting for it: “Allow Non-modular Includes in Framework Modules.” This setting is the user-friendly version of CLANG​_ALLOW​_NON​_MODULAR​_INCLUDES​_IN​_FRAMEWORK​_MODULES. Here’s what Xcode’s Quick Help has to say: Enabling this setting allows non-modular includes to be used from within framework modules. This is inherently unsafe, as such headers might cause duplicate definitions when used by any client that imports both the framework and the non-modular includes themselves. Okay. I understand the issue and I’m willing to press the button — and I did, and everything works. Great. Well, until I added some Swift code that imports that framework. Then I get the error: Include of non-modular header inside framework module 'FMDB.FMDatabase' Swift apparently doesn’t respect the CLANG​_ALLOW​_NON​_MODULAR​_INCLUDES​_IN​_FRAMEWORK​_MODULES setting, while Objective-C does. Filed as rdar://20184784. The bug report includes a small sample project that you can download: NonModularBug.zip. Why I’m blocked The VesperData framework — where VSNote, VSTag, and VSAttachment live — depends on QSDB (similar in purpose to FCModel) which depends on FMDB. The upshot is that I can’t write any Swift code that references the data model. Which doesn’t leave me a whole lot else. (Given what’s already completed or at least started as Objective-C files.) (Half of my friends — Gus included — are shouting at the monitor that this is a feature of FMDB and I should take it as a sign.)

Posted by on 16 March 2015 | 11:23 pm

One Way to Do Surveillance

This idea doesn’t require a hacked Xcode. Create a shell company that pretends to be a small software development company. Write an app for your intended audience — for instance, write a Twitter client for Arabic speakers. (Or Farsi. Or French. Or English.) Add your surveillance framework to the app. Make it a good app, something lots of people would like to use. Make it free. Spend money on marketing. Buy lots of hard drives to store all the text, images, and metadata it captures.

Posted by on 10 March 2015 | 3:55 pm

Russian Jokes

Back when Russian communism was still a thing, sometimes here in the States we’d hear of the jokes Russians told each other. (“In Soviet Union, TV watches you!”) And we’d think that our system was better since we didn’t have to joke that way. And we were right. But today I heard: “It’s not NSApplication — it’s NSA-pplication!” (See today’s news.) We make Russian jokes now.

Posted by on 10 March 2015 | 3:15 pm

Behind the Office Doors

For the latest objc.io I wrote Inside Omni, a quick tour of Omni’s infrastructure for managing large projects. The entire issue — iOS at Scale — is worth reading. As always.

Posted by on 10 March 2015 | 11:09 am

dispatch_once

I’ve been doing this for years when I have a static thing and I want to set its value once: static MyThing *foo = nil; static dispatch_once_t onceToken; dispatch_once(&onceToken, ^{   foo = [some thing]; }); But lately I’ve been doing this: static MyThing *foo = nil; if (!foo) {   foo = [some thing]; } The advantage of dispatch_once is that it’s thread-safe — it’s one less thing to worry about when worrying about concurrency. But here’s the thing: almost all of the code I write runs on the main thread only. And, furthermore, when I use this pattern it’s almost always in UI code (which is main thread code). (For example: initializing a gradient that won’t change and should be kept in memory.) So dispatch_once isn’t valuable in those cases. And, worse, it could hide bugs. (If my main thread code isn’t running on the main thread, I need to see some problems as a result.) I’ll keep using dispatch_once in the ever-more-rare cases where concurrency is actually an issue — but, where it’s not, I’m back to the old-fashioned way. And the old-fashioned way is less code and easier to read, which I don’t mind at all.

Posted by on 5 March 2015 | 4:34 pm

Hard Syncing

I can’t stand to read App Store reviews (mine or anybody else’s) — but I was told earlier today that Vesper had a one-star review where the reviewer suggested we use WebDAV syncing. Sure, we could do that — I thought — if I spent a year on that and did nothing else. I also work on OmniFocus, which has WebDAV syncing, and it lets you use your own server or Omni’s. For free. This is a great feature, especially for companies and institutions required to keep all data in-house. But here’s the thing: syncing is relatively easy if you treat it as a species of web services. That is, if the server side is a smart server with an API and a database, it’s not that bad. (Syncing is still hard, but this is the easiest way.) This is how we do it with Vesper. Syncing by reading and writing files on a generic storage system, on the other hand, is much, much harder. It’s to Omni’s massive credit that they did this. It’s not just harder to write, it’s also harder to support, since servers will have bugs or be misconfigured. But Omni has the resources to handle this. This goes back to the discussion of Sustainable Software. Features are economic decisions. Say I looked at it more closely and decided we could do it in six months instead of a year, including at least a month-and-a-half of concentrated beta testing with lots of different servers. We ship in six months (with no other updates during that six months, and definitely no Mac version). We raise the price to $24.99. We hire a support person. If it were you, would you take that risk?

Posted by on 4 March 2015 | 4:19 pm

Misinformation part two

I updated Beware of Flags with some additional information. (Scroll to the bottom.)

Posted by on 4 March 2015 | 12:34 pm

Misinformation

Marcus Zarra on The Dangers of Misinformation: To this day, there will be at least one conference a year where someone will walk up to me and say, “So about that blog post that Brent wrote…” I bet they say “Brett,” actually. They think it was Brett Terpstra. :) The response to that post continues to amaze me. I come right out and say, multiple times, that you should use Core Data. And yet it’s used as a thing about how Core Data sucks. It doesn’t suck. Since writing that post I’ve shipped at least one app that used Core Data. And: the problems I had with it five years ago aren’t particularly relevant now. Core Data has improved since then with new features that deal with some of the problems I had — and devices have gotten so very much faster since then.

Posted by on 3 March 2015 | 11:35 pm

Beware of _flags

If you’ve done AppKit programming, you’ve noticed in header files (such as NSTableView.h) that Apple sometimes uses a _flags struct to hold a bunch of booleans. Something like this: typedef struct __flags {   unsigned int a:1;   unsigned int b:1;   unsigned int c:1; } _flags; (Usually there are more than three. Usually way more, at least in Apple’s code.) A BOOL is one byte — sizeof(BOOL) returns 1. So, in theory, you could store a bunch of these 1-bit things in the space normally reserved for a single BOOL. But what actually is the size of _flags? In the case above, where it has just three members, it’s four bytes — sizeof(_flags) returns 4. (On my 64-bit Mac, that is. I haven’t checked everywhere, but I wouldn’t be surprised if this case is always 4 on Apple platforms.) This means that declaring those three members as BOOL properties would have saved space. If there were four members you’d break even — but the _flags version is more complex. I think, in fact, that’s there’s only good case for a _flags struct: when you have way more than three booleans, and you expect to have a ton of these objects in memory at once. (A table view, for instance, wouldn’t count.) Otherwise, do the easy, less-complex thing: stick with properties. Update Mar 4 10:30 am: Well, there was a big thing I missed. If you make those unsigned char instead of unsigned int, you can in fact pack a bunch of booleans into a byte. typedef struct __flags {   unsigned char a:1;   unsigned char b:1;   unsigned char c:1; } _flags; sizeof(_flags) returns 1 in this case. Nevertheless, I still maintain that it’s rarely a good idea. Mostly because it adds complexity (you have to support KVC and KVO manually) but also partly because in framework code (for frameworks that aren’t compiled when the app is compiled) it’s fragile.

Posted by on 3 March 2015 | 1:27 pm

Xcode’s View Debugger

At Omni I work on existing apps with large code bases that use a bunch of frameworks. Say I want to make a UI change. I don’t know where to look. Is it implemented in the app or in a framework? Which one? What’s the class I’m looking for? I can make some guesses, of course, and I could hunt around, and I could ask people. But it occurred to me (on day one, thankfully) that I could just use Xcode’s view debugger. Build and run the app, click on the Cyberman icon, and select the thing I want to change. Xcode tells me what class it is, and I know right where to go. This has saved me a ton of time. I’ve used the view debugger for some actual view debugging too, and while it’s pretty good, there are some additional things I wish it did. It doesn’t tell me if a view has a layer or if it’s opaque or not. What I’d most like is the ability to click a pixel and have it tell me who drew it. (I realize this might be tricky.) Or, failing that, at least make it easy to print the description for an object. (Yes, I realize that I can do po (id)(0x618000126400) — it’s just that it seems like an obvious place for a shortcut.) Update 11:00 am: I didn’t know about -[NSView _subtreeDescription]. Very helpful. Does a bunch of what I want.

Posted by on 3 March 2015 | 12:24 pm

Init Question

What’s the best practice for this situation? Let’s say you have an object where initWithSomething could fail due to bad inputs or other error. Let’s also say that, if it fails, an error should probably be presented to the user. If it helps to think about a concrete case, think of a Core Data stack object. (This post is not about Core Data. This is just an example.) Init might look something like this: - (instancetype)​initWithFolder:​(NSString *)​folder modelName:​(NSString *)​modelName databaseName:​(NSString *)​databaseName; There are two reasons it could fail: Inputs are bad. (For instance: folder is nil, or modelName has a typo.) A method called during init fails. For example, -[NSPersistentStoreCoordinator addPersistenStoreWithType:​configuration:​URL:​options:​error:] could fail. I can think of a few options, and I’m not sure I like any of them. So I’d love to know what you think. Option 1: in-out error parameter I don’t think I’ve ever seen a failable initializer with an in-out NSError ** parameter. (Do they exist? Maybe they do and I just haven’t noticed.) The init method would look like this: - (instancetype)​initWithFolder:​(NSString *)​folder modelName:​(NSString *)​modelName databaseName:​(NSString *)​databaseName error:​(NSError **)error; That seems ugly and unconventional to me. But it would reflect reality pretty well. Update 1:15 pm: This is the winner! Look at NSString.h to see some examples. Option 2: error property Don’t fail in the initializer. Instead, create a property for the error: @property (nonatomic) NSError *initError; The caller of initWith… would have to check initError right after to see if there was an error. Ugly and unconventional again. Option 3: initialize lazily The main reason for this object is to provide properties for other objects and hide how they’re created. The example could have a property like this: @property (nonatomic) NSManagedObjectContext *context; All the initialization of the stack could be put off until context is first referenced. But, then, there’s still the issue of what to do with errors. Something like this instead of a property? - (NSManagedObjectContext *)context:(NSError **)error; Ugh. Weird. But maybe it’s less weird if the API is something like this: @property (nonatomic) NSManagedObjectContext *context; - (BOOL)setupManagedObjectContext:(NSError **)error; The caller of initWith… will have to call setupManaged​Object​Context before referencing the context property. This is also weird because setting up context should be completely in the hands of the Core Data stack object. It shouldn’t be something a caller has to remember to do. Option 4: assertions and faith Use NSParameterAssert and NSAssert to catch programmer errors in debug builds — then assume that, once programmer errors are fixed, nothing else that technically can fail will actually fail. This would mean believing that, in this case, -[NSPersistentStoreCoordinator addPersistenStoreWithType:​configuration:​URL:​options:​error:] would never fail as long as the inputs were good. Arguably you should just call abort() if such a method fails with good inputs, on the grounds that the device has probably caught fire. But still, that requires a certain amount of optimism — but optimism means doubt, and our job as programmers is to remove doubt. Option 5: don’t use a separate object for this Give up on the idea of encapsulating all this in reusable code. Instead, have the app delegate (or similar high-level object) do the setup. Presumably that high-level object can show errors to the user and decide what to do if things go wrong. This answer is a bummer, though. I like reusable objects and I don’t like copy-and-paste. But maybe I’ve proven that this option is the only responsible choice. What am I missing?

Posted by on 1 March 2015 | 3:00 pm