I just published an update to the WordCounter for Mac that modernizes the UI and typography a bit to look rad on Mojave in both dark and light modes. It also fixes pertinent issues with “Launch at Login” not doing what it’s supposed to do, and fixes a couple of small bugs.
When you write a Swift type, you should prefer to write its tests in Swift, too. In a mixed-language code base, you will run into the situation where your Objective-C tests need to reference Swift types to initialize and pass to the object under test. That’s a different story. But you cannot import the generated -Swift.h out-of-the-box:
Two years ago, I wrote about how I implemented a toolbar with NSSegmentedControl, much like Apple’s own apps have them since macOS10.11 Yosemite. Last week I discovered my implementation was buggy: it did not work at all when you customize the toolbar to show “Icon Only”, i.e. hide the label text.
Ever wanted to implement a full-text search in your app? Didn’t find a boolean search expression parser that works? Look no further! For the initial release of my note-taking app The Archive I had to create a couple of open source libraries that I have yet to talk about. Today, I want to show you my search expression parser. It powers the app’s Omnibar to find notes quickly using simple boolean expressions.
A little side-project of mine is a role-playing game written in Ruby that runs in the terminal and uses Unicode/ASCII characters instead of bitmap pixel graphics. In my personal tradition of these kinds of side projects, this is called TermQuickRPG. It’s a work-in progress, so there’s not a lot to do in the sample game at the moment.
I am upgrading the code base of the Word Counter to Swift 3. Yeah, you read that right. I didn’t touch the Swift code base for almost 2 years now. Horrible, I know – and I’m punished for deferring this so long in every module I try to convert and build. One very interesting problem was runtime crashes in a submodule I build where URLs were nil all of a sudden. This code from 2015 (!!) used to work:
Say you have a collection of radio buttons. They’re NSButton instances, and NSButton inherits from NSControl. Radio buttons’s mutual exclusivity is implemented by … With the correct setup, you can set 1 out of 100 radio buttons to .on and have the previous selection turned off for you automatically. That’s neat.
I still use TextMate for some things: editing documents quickly, scripting in Ruby, navigating project folders of foreign code bases (especially when they’re not using my main language so I could use Xcode, e.g. Java projects), and finding and replacing text. But it always bugged me that when I move around code and indent and outdent and whatnot, that sometime lines with nothing but whitespaces would be saved. Or I’d combine stuff and have 10 trailing spaced all of a sudden. I do show invisible characters, but I don’t want to pay attention to that kind of stuff when I’m coding.
Vinh Nguyen found that his ReSwift status updates became slow.
There were too many subscribers.
Objects would react to state updates by dispatching a new action immediately. (ReSwift action dispatching happens synchronously.)
His app state ends up containing a lot of objects in a 3-level hierarchy that mimicks the hierarchy of view components on screen. In a drawing or otherwise canvas based graphics app, it seems. It doesn’t make sense to have each objects on the canvas responds to state updates when one other object updates on screen. Instead, you’ll want to at least minimize the amount of updates that get passed through.
Vinh implemented a custom diff or “delta update” for the 2nd level in his 3-level hierarchy of objects because they were few enough to be performant during state updates, and could easily manage their child objects.
Read about his discovery of state update bottlenecks on his blog.
He solved the second problem, newState callbacks triggering the dispatch of another action, by enqueuing the dispatch in an asynchronous block on the main queue, which is the queue ReSwift uses:
Sure, this enqueues the action dispatch until the current execution is finished. But you have to take care about other actions being dispatched in between now, and if that is a problem. (E.g. another subscriber type reacting to the same state update with another action.)
I had prefered another solution initially: subscribe to updates in the top level Canvas object, then delegate down the view hierarchy as needed. Every sub-component that wants to fire an action tell the Canvas about this, which enqueues the actions, and then processes the queue after all sub-component updates are finished. A bit like in game development where the game loop ensures there is just 1 point of action handling per run. But then again, Vinh’s approach does exactly that: it enqueues action dispatching until later, ensuring the current run loop run isn’t interrupted. Also, my approach to delegation would make everything just so much more complicated in the app code.
I wonder is it’d be beneficial if the ReSwift store operated on a high priority queue that is not the main queue all the time. Then you can dispatch actions synchronously from view components on the main queue, waiting for the result, or asynchronously.
I will have to think more about the consequences of an approach like this before I suggest anything to anybody, though. I don’t do a lot of concurrent programming in my apps, and when I do, I contain it very strictly; on the downside, I don’t have developed any instinct regarding implications of using multiple queues.
You can make two NSScrollViews scroll in concert quite easily because every scrolled pixel is broadcasted to interested parties. In TableFlip, the main table is a NSTableView contained in a NSScrollView. You can view and hide row numbers in TableFlip; but I didn’t want to reload the whole table and mess with the table model to insert and remove the first column. Instead, I use a second table view with a single column. The upside of this approach: I can animate hiding the whole scroll view with the row numbers inside easily without affecting the main table.
Today I learned why my NSTextField permits pasting of newline characters even though I set usesSingleLineMode properly. It’s because I made it conform to NSTextViewDelegate to cache changes. When you edit text inside of an NSTextField, you actually type inside a field editor of the window. That’s a shared NSTextView instance. Most of the hard work of an NSTextField is done by its cell, which is an NSTextCell. NSTextCells implement at least the delegate method NSTextViewDelegate.textView(_:shouldChangeTextIn:replacementText:) – and when you set usesSingleLineMode, this is actually set for the cell, not the view itself. You can use textView(_:shouldChangeTextIn:replacementText:) to sanitize input text, and I suspect that’s where the usesSingleLineMode implementation happens. If your NSTextField subclass implements this method, the NSTextCell implementation isn’t called. And since that one isn’t public (it was called “implicit protocol conformance” back in the day), you cannot delegate up in Swift because the compiler knows it isn’t there.
Earlier this month, I wrote about validating temporary models for forms. The validation returned .complete or .incomplete, which doesn’t help much when you want to show what did go wrong. So I came up with a richer validation syntax.
I wanted to handle a file loading error in a consumer of an RxSwift Observable sequence. But if the sequence itself produces an .error event, it completes. After fiddling around for a while, I decided to simply use the Result enum and ended up with Observable<Result<Theme, ThemeError>>.
Ian Keen posted an article about type-safe temporary models. You would use them like scratch pad contexts in Core Data: in forms, you collect information into these temporary models and then generate your real objects from them.
WWDC people noticed that Panic Inc. are coming back to the Mac App Store with their beloved file transfer app, Transmit. This puzzled a lot of people because they moved away from the MASstarting with Coda 2.5 in 2014. Sandboxing was just too restrictive. But now, it seems, the new Mac App Store’s Sandboxing rules will be different enough for Transmit to work. See Panic’s tweets on the topic. The details:
Watched Robert “Uncle Bob” Martin talk about “The Future of Programming” and wholeheartedly recommend it. In the talk, Uncle Bob brings up interesting points about the growth of the programming profession:
Roughly every five years, the number of programmers double.
Conversely, half of the programmers at any point in time have less than 5 years of experience.
The industry lacks an appropriate amount of teachers, so all the new people will make all the same mistakes over and over again.
Programming does not grow up this way. On top of that, programmer mistakes in everyday devices now are causing lethal damage to real people. Self-regulation inside the profession (which, like traditional crafts, I guess could reduce the onslaught of newcomers) and proper teaching are essential to keep growing and making code more reliable a foundation of modern societies.
That’s part of why I write, too. Because I found it was extremely hard to learn creating applications properly, not just hacking together something that barely works. I want everyone else to have a head-start compared to me. While educating myself, I discovered ancient wisdom in books from the 1970s – stuff you can still tell (or should I say: sell) people today, like how to decouple parts of your system. As if we, as a profession, suffer from collective amnesia. Old wine in new skins.
Now that I know some rough estimates about programming’s exponential growth, this makes sense. There just isn’t enough time and care put into teaching these practices properly. It’s hard enough to equip students with sufficient knowledge to become somewhat dangerous in front of a programming environment. The rest then is delegated to on-the-job training, which I imagine is pretty disappointing for all parties involved.
In my quest of total immersion into Emacs, I am trying to write blog posts in Emacs instead of TextMate. That means my TextMate macros are gone for good, including insertion of YAML header information. On this very blog and on Zettelkasten.de, I used to type cthead and zkhead respectively, then expanded the YAML header with tab. TextMate’s great template feature even allows you to specify multiple points inside the templates to jump to by tabbing forward. Emacs works a bit differently, so I adapted the templates to request information up front instead and fill in the values.
Happy GDPR day! With the recent EU law stuff taking effect, I reconsidered my use of tracking information from you. So I just removed Google Analytics tracking on this website, on our productivity blog at zettelkasten.de, and on all my app websites completely. I find the numbers interesting, but not actionable anyway. I don’t do A/B testing. I just write for fun and with the intent to help you do stuff. There’s no benefit in tracking how you use my site. Your visit is much appreciated, and that’s it!
I noticed that my Emacs didn’t maintain the current line’s indentation when editing code blocks in Markdown (markdown-mode). I indent the first line with 4 spaces like any sane person would. When I hit enter to continue in the next line, thanks to the markdown-mode defaults, I am presented with a new line that’s properly indented visually. Only when committing to git did I notice that Emacs inserted tabs instead of spaces. Gasp!
I released an update to Move!, my work break timer, the other day. It fixes a timing bug and rare memory leaks. It’s a free update, and the app should notify you about the download automatically when it’s running.
I am using GNU Emacs for a while now to do all kinds of stuff. I’m thinking about migrating away from OmniFocus as my task manager and use Org mode instead. What I like so far is the free-form list nature of Org files. I can have an outline of notes and sprinkle TODO items inside them. This way I can take notes on puzzling problems I’m working on and mark things I need to do later. This is super useful to remind myself what to clean up before a commit, for example write or amend tests or remove debug code somewhere. I like it. I got used to a lot of shortcuts already, so most of the pain of daily use is gone.
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.
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.
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!
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.
If you run macOS10.13 High Sierra, try not to use the Help menu. It will appear to freeze the app for which you invoke the Help menu: it will not accept keyboard input anymore, emitting the NSBeep “invalid action” sound in most circumstances. The reason seems to be that the nifty Search bar inside the Help menu will acquire focus so you can search for a menu item or help book entry. This is taking away focus from the app window. You notice this when the blue focus ring around an active text field in the window goes away; instead, the Help menu’s Search bar obtains focus and the accompanying blue focus ring. All this is supposed to happen. This process just isn’t reverted for some reason since at least macOS10.13.3, maybe earlier. When you click back into the app outside of the Help menu, the key window status will not be assigned back to the window you click on. That means keyboard input will not work at all. (As a non-native English speaker, I asked if “key window” was supposed to mean “window that responds to key events” a while back. While people agreed this wasn’t the intention, I still cannot help to think that way. The app has no key window, so key events are not handled, resulting in NSBeep.)
I use the macOS color well to let users of The Archive tint vector icons in their favorite color. But setting NSColorWell.isEnabled hardly produces a visual change in the color editor. Can you easily tell which is enabled and which is disabled if it weren’t for the checkbox? The little frame color change is too subtle for my taste. I added a high-contrast strike-through line to my color well:
My next macOS app project has launched: It’s a note-taking app for macOS to help writers write more. In our typical German stoic way, we just call it The Archive.Check out The Archive! Its choice of features is closely tied to a few core principles: the user is more important than your app; plain text comes first, so avoid proprietary features and vendor lock-in, thus furthering replicability of the workflow in other apps. I guess we’ll write more about that in the future.
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:
I spent the last 15 or so months developing this beast. If you follow my ramblings, you will know that I get to work every day at about 7 a.m. and work until about 5:30 p.m., including workouts and breaks. Since I didn’t use a time tracker like Timing (affiliate link) for most of the time, I cannot say how many hours I really spent coding, sadly.
Anyway. The launch. The Archive will go live March 15th. I’ll publicize the app info page, soon.
And then I’ll spend some leisure time writing a postmortem. The beta testers have been a pleasure to work with, as always. It’s amazing how much love strangers are willing to give for carefully crafted products.
This closes a weird circle for me: my first Cocoa coding experience was adding Markdown preview to Notational Velocity in 2010, which I barely managed, having had zero understanding of Objective-C and how Xcode worked. Now I am able to create an app that exceeds NV’s capabilities all on my own. I’m very happy about this coding journey, and oh boy do I have a lot of cool stuff I am going to add after v1.0 goes live!
I am using ReSwift in my app. I like ReSwift because I can create a representation of my app in a “State” Swift module without any UIKit/AppKit dependency. There, I put all the data that the app is supposed to represent at any given moment in time. To get a feeling for this, have a look at two apps of mine:
When I announced the imminent launch of my latest app project, The Archive, a beta tester asked how many jars of coffee I consumed while coding. “Jar” is no mistake. I drink from a 750ml glass jar, full to the brim. That’s one portion of cowboy coffee, or is it turkish coffee? – I’m not quite sure what to call it: grind the beans, pour water, stir from time to time, let it settle, then drink.
My friend and fellow Mac developer Daniel, known for the productivity tracker Timing 2 (affiliate link), assembled a list of general-purpose productivity apps for Valentine’s Day and offers exclusive discounts. My own app, the Word Counter, is on this list, too.
Check out his post and enjoy your lovely treat of discounted apps for Mac.
For whatever reason, my current app project’s find bar does not make the text view firstResponder again when you hit Escape or click the “Done” button to close it. This is very uncomfortable for users: they type away, hit ⌘F to find a phrase, then hit Esc – and now they’re in limbo. To my astonishment, the NSTextFinderAction called hideFindInterface is not triggered when you make the find bar disappear. Its opposite, showFindInterface, is triggered when the find bar slides back in, though. Intercepting in NSTextView.performTextFinderAction(_:) does not help, then.