RxSwift: Typing/Idle Event Stream for NSTextView

To improve my note editing app The Archive's responsiveness, I want to find out when the user is actively editing a note. Here's how to create the idle switch in RxSwift/RxCocoa. A simple enum of .typing and .idle will do for a start. Of course, NSTextViewDelegate provides a textDidChange(_:) callback to hook into; it's based on a notification, so you can also subscribe to the NSText.didChangeNotification directly if you don't want to use a delegate. That's the input signal I'm going to use.

Continue reading …

Broken Help Menu Breaks Rather Unpredictably

Last week, I wrote about the Help menu bug that seems to "freeze" apps inasmuch as it makes them not handle any keyboard input. The thing is, I cannot always reproduce this issue. I rebooted my system and just couldn't break the behavior anymore. Which sounds an awful lot like running the system for a while and using it in a specific way might affect the Help menu's behavior. But I don't know which steps you need to take to reproduce this. It's puzzling me. With Daniel Jalkut and a beta tester of my latest app having experienced the same issue, I know I'm not alone. Or crazy. Just clueless.

Continue reading …

Plain Controllers to Ease the Burden of View Controllers

I like to see that Soroush and I think among the same lines. For one, that makes me feel less like a loony. And since he writes a lot more in recent months, it means my sloppy blogging schedule does not affect spreading ancient wisdom. In February, Soroush blogged about writing "Just Controllers": service objects, usually reference types, that you use to orchestrate processes and offer a simpler API. Requiring a delegate to be plugged-in is one way to design a simpler API: the delegate procotol defines all the output events from the service object, or controller. That's all there is going to happen as far as outside code is concerned. But on its inside, the controller can do the fanciest of things to accomplish its goal. These implementation details are hidden from code that uses these service objects. It's object-oriented programming 101 all over again – encapsulation, information hiding, stuff like that. But, and this is a big But, the go-to massive view controllers in iOS apps are a symptom of not encapsulating properly. Go and delineate a few more boundaries inside your app. As with raising children, boundaries help your code grow properly, too!

Put Test Files Next to Production Code for Better Discoverability?

The Kickstarter engineering team posted an interesting proposal: put your XCTestCase files into the same directory and group as your production code. Do keep the separate target ownership, of course.

One clear benefit is directory tree or file management, and discoverability of tests for people browsing the code: In most cases, you have FooService in FooProject/FooApp/FooService.swift, and FooServiceTests in FooProject/FooAppTests/FooServiceTests.swift. With nested folders, you need to navigate around even more. I got used to this Xcode default. But I also got annoyed by this split at the root level when I browsed code on GitHub. Multiple tabs in a modern browser to the rescue, this was never that problematic, given the test directory is structured in a similar way. If not, good look finding the file you're looking for. (I hate that the RxSwift project, which I often had to browse to look up an implementation detail, has files that contain only a path reference to the real file, similar to ../../Core/SomethingSomething.swift, making tests pretty hard to find).

I will try this approach with a library module that I develop as a sub-project of The Archive and report back.

NSTextView's Default Insertion Point and Selected Text Colors

Teaser image

NSTextView can be customized to display different colors for the insertion point (aka caret, aka text cursor) and for selected text. This is especially useful when you add themes to your editor and the default settings don't fit anymore. The default values are not exposed anywhere, so I had to log them and reconstruct convenient accessors to reset a text view if needed:

Continue reading …