Replacing More Reference Objects with Value Types

I was removing quite a few protocols and classes lately. Turns out, I like what’s left.

I relied on classes for example because they can be subclassed as mocks in tests. Protocols provide a similar flexibility. But over the last 2 years, the behavior I was testing shrunk to simple value transformations.

Take a ChangeFileName service, for example. It is used by another service during mass renaming of files. It is also used when the user renames a single file. The updatedFilename(original:change:) method is the only interesting part of it. You call this method from other objects and verify this behavior in your tests. Then, one day, you look at the method body of updatedFilename again and find it can be shortened to:

func updatedFilename(original: Filename, change: FilenameChange) -> Filename {
    return original.changed(change)
}

That’s similar to what I was looking at. Filename and FilenameChange are value types (structs) and their transformations are well-tested. The updatedFilename method only makes this call mock-able. But what for? You can change the tests just as well!

This is a typical object-based test with mocks and assertions on transformations:

  1. Is the other object called? (didUpdateFilename)
  2. Is the other object’s result used? (testUpdatedFilename)
/// The test double/mock object
class ChangeFileNameDouble: ChangeFileName {
    var testUpdatedFilename: Filename = Filename("irrelevant")
    var didUpdateFilename: (original: Filename, change: FilenameChange)?
    override func updatedFilename(original: Filename, change: FilenameChange) -> Filename {
        didUpdateFilename = (original, change)
        return testUpdatedFilename
    }
}

func testMassRenaming_UpdatesFilename() {
    // Given
    let changeFileNameDouble = ChangeFileNameDouble()
    let service = MassFileRenaming(changeFileName: changeFileNameDouble)
    /// The expected result
    let newFilename = Filename("after the rename")
    changeFileNameDouble.testUpdatedFilename = newFilename
    /// Input parameters
    let filename = Filename("a filename")
    let change = Change.fileExtension(to: "txt")

    // When
    let result = service.massRename(filenames: [filename], change: change)

    // Then
    XCTAssertNotNil(changeFileNameDouble.didUpdateFilename)
    if let values = changeFileNameDouble.didUpdateFilename {
        XCTAssertEqual(values.original, filename)
       XCTAssertEqual(values.change, change)
    }
    XCTAssertEqual(result, [newFilename])
}

It’s a bit harder to test for an array with 2+ elements, because then you have to collect an array of didUpdateFilename and prepare an array for testUpdatedFilename that’s to be used by the MassFileRenaming service.

All you test is the contract between MassFileRenaming and ChangeFileName. Which is a lot, don’t take me wrong! But then you also have to test ChangeFileName itself to make sure it does the right thing and doesn’t just return some bogus values once you stop using a mock.

This test case can be changed to the mere value transformation itself without the “class behavior wrapper”. And since you don’t rely on a mock, it’s easier to test collections of values:

func testMassRenaming_UpdatesFilename() {
    // Given
    let service = MassFileRenaming()
    /// Input parameters
    let filenames = [
        Filename("first filename"),
        Filename("another filename"),
        Filename("third filename")
    ]
    let change = Change.fileExtension(to: "txt")

    // When
    let result = service.massRename(filenames: , change: change)

    // Then
    let expectedFilenames = filenames.map { $0.changed(change) }
    XCTAssertEqual(result, expectedFilenames)
}

But isn’t this an integration test now?” Well, as always, it depends on your definition of a “unit”! You don’t write adapters and wrappers for all UIKit/AppKit method calls, either, and treat the Foundation.URL, Int, or String as if there’s nothing to worry about.

The Filename type, in this example, is reliable, too. If it’s in another module, like MyAppCoreObjects, and thus even more isolated from your other app code, wouldn’t you treat it the same as URL?

I still have to rethink some things with regard to value types. But one nice thing is that you can use self-created value types like any built-in types. No need to wrap in a class/reference object.

Maybe I need more time to adjust to this kind of thinking. I do know that the talk by Ken Scambler I mention time and again was very helpful to make the transition with confidence, like when I began changing RxSwift view models to free functions.

Do Not Apply Code Heuristics When You Need a Broader Perspective

You can only improve things inside the frame you pick. If your frame is too narrow for the problem you try to solve, you cannot properly take everything into perspective. That’s a trivial statement as it’s only re-stating the same thing, but it’s worth stressing.

Apply this to code.

If you focus on code heuristics to improve your code base, you cannot improve the structure of your program. Even though the structure is manifested as code, it’s not code you should be thinking about. It’s concepts. Code is just the textual representation you stare at all day. Structure is what the imaginary entities of your invention bring forth.

What are “code heuristics”?

In general terms, it’s thinking inside the terms of the solution space or implementation: Functions, classes, libraries. An opposite of the solution space is the problem space or problem domain. Your code to maintain air traffic contains methods that are called; the problem is about airplanes and collision courses. Your implementation is a translation of the problem domain, of what real people talk about, into machine-readable commands; that’s the translation from problem to solution space.

For example, the “Don’t Repeat Yourself” principle (DRY) is a code heuristic. It states that you should avoid pasting the same code all over the place. Instead, the DRY principle advises you to extract similar code into a shared code path, e.g. a function or method that can be reused.

Please note that “extract” is also a common refactoring term and “reuse” generally has a positive connotation. Doesn’t mean it’s a good idea to follow it blindly. Because when all you know are code-level tricks and heuristics to improve the plain text files, you sub-optimize for code. This endangers the problem–solution-mapping as a whole.

sub·​op·​ti·​mi·​za·​tion:
especially : optimization of a part of a system or an organization rather than of the system or organization as a whole

You endanger the solution you are trying to improve when your criteria of “better” and “worse” are only relative to the level of code – like “do these parts of code look the same?”

One problem with following DRY blindly is you end up with extracting similar-looking code instead of encapsulating code that means or intends the same. This is sometimes called “coincidental cohesion”. You wind up reducing code duplication for the sake of reducing code duplication.

If you confuse mere repetition of the same characters in your source text files with indenting the same outcome, you can end up with 1 single function that you cannot change anymore without affecting all the wrong parts of the program. This becomes a problem when you need to adjust the behavior in one place but not another. You cannot adjust this if the code is shared. (If you’re lucky, you duplicate the code again; if you’re unlucky, you change the shared code and break another part of the app.)

Take this code:

let x = 19 * 2
let y = 19 * 4

A DRYer example:

func nineteenify(num: Int) -> Int {
    return num * 19
}

let x = nineteenify(2)
let y = nineteenify(4)

But what if one of the 19 are about the German VAT and Germany’s tax legislation changes the rate to 12, and the other 19 is just your lucky number used for computing an object hash, or whatever? (Both similar-looking “magic numbers” represent concepts, albeit different ones in this case!)

Another code heuristic is called symmetry. The lines that make up your methods should look similar to increase readability.

Take the following code for example. You are not satisfied with it anymore because each line looks different than all the others:

func eatBanana(banana: Banana) {
    let peeledBanana = banana.peeled
    guard isEdible(peeledBanana) else { return }
    self.assimilateNutrients(peeledBanana)
}

To improve symmetry of the code and thus increase readability, you figure that this sequence is essentially about a transformation in 3 steps. Also, you like custom operators for their brevity and how code reads with the bind or apply operator: |>. Think of it as applying filters here. You increased terseness to reduce noise and end up with this

func eatBanana(banana: Banana) {
  self.prepare(banana: banana)
    |> self.checkEdibility
    |> self.assimilateNutrients
}

// Extracted:

private func prepare(banana: Banana) -> PeeledBanana? {
    return banana.peeled
}

private func checkEdibility(peeledBanana: PeeledBanana) -> PeeledBanana? {
    guard isEdible(peeledBanana) else { return nil }
    return peeledBanana
}

Looks better, maybe? If you understand how the operator works, this could be useful to state the intent of eatBanana more clearly as a step of value transformations.

Questions not asked in the meanwhile:

  • What does the rest of the class look like? Are there 100 other methods in the affected type? Does this one method become harder to read but the type becomes harder? (Another code-level heuristic.)
  • Whose responsibility is the preparation of bananas? Do these methods fit together into a single object? Is the receiver of these methods the best or just whatever is accidentally owning eatBanana? Should this be separated into multiple objects with more focused responsibilities instead? If you did that, would the original problem of symmetry vanish already? (This is not a code-level decision anymore.)

I hope this illustrates what a focus on the solution space brings forth. To take your code as a piece of text you can run grep or sed transformations on to improve it may work, but if all you do is extracting similar strings of characters from text files manually, do you ask the right questions?

In a similar fashion, Design Patterns are code templates. They help write code that solves a particularly intricate problem. Design Patterns are complex solutions, but they are confined to the solution space nevertheless. This includes the popular MVVM as well as the Factory or Visitor design pattern.

To implement MVVM, you need a Model type, a View type, and a View Model object/protocol/… that is based on the Model but exposes an Interface tailored to the View.” – This is a recipe for writing code. It does not and it cannot answer the tough question: what is the best abstraction for your model? What components do you need to translate the problems of air traffic control into an iOS app, for example?

Is an Aircraft class a good abstraction?

This question cannot be answered by reading more code. The answer cannot be sped up with recipes.

This question can only be answered by thinking about the problem.

I think part of software architecture is modeling the problem domain, and isolating your translation of the original problem from e.g. UIKit code properly. That’s when architectural heuristics come into play, like “use layers to separate different parts of your app and avoid a mess.” The VIPER pattern is a higher-level code pattern that affects your architecture.

And this is what bugs me about most of the conversation in our community at the moment: the conflation of Design Patterns (code heuristics) with “Application Architecture” (which goes way beyond that). When we keep these concepts separate, we can continue to focus on one at a time and get better. It’s like the DRY problem from above all over again: if your frame is too narrowly focused on visible repetition on your screen, you cannot have the bigger picture in mind.

If we continue to fail to distinguish these concepts, we end up losing the ability to ask some kinds of questions.

And I argue some of these are the really important ones that help your software as a solution to a real-world problem continue to thrive.

Refactoring The Archive’s ReSwift Underpinnings

While I was refactoring my app’s state recently, I noticed that there are virtually no dev logs about The Archive and how I manage state and events in this app. It’s my largest app so far, still the amount of reflection and behind-the-scenes info is pretty sparse.

This post outlines my recent changes to the app’s state. It’s a retelling of the past 5 weeks or so.

ReSwift: Central State and Control Flow

For The Archive, I am using ReSwift to control the flow of information. Every user interaction and every external file change on disk are handled by a service that emit a ReSwift-compatible Action. This action is processed in a central place, the Store. The previous app state plus the incoming action result in a new app state, which is then broadcast to all subscribers to take action. That’s the basic flow.

I am working with ReSwift for years now. I use it in TableFlip, too, for example. I really like how you end up with a state representation of the whole app (or document): every new feature you add results in a state change, triggered by an action. Action names like CreateNote, ChangeFileEncoding and SortResults are very easy to understand when you encounter them in code.

Partitioning the App State of The Archive

It took a couple of days, but I am about to finish a major refactoring to The Archive that would’ve been much more painful if I had used a less orderly approach to state management. Over the last 18 months of development, I changed my mind about what actions do, how to handle some side effects, and also had to solve a bug here and there. Some initial design decisions have proven to be outdated. That’s the way software development works, of course: You iterate and change things, and then refactor to accommodate to new insights.

One such insight was related to my partitioning of the app’s state. Some folks work with a completely flat hierarchy of state; in ReSwift, that means 1 single struct with a ton of properties. I was partitioning the state into various groups and created a hierarchy like this (lots of details omitted):

  • AppState, the root state type
    • SettingsState for global app settings,
    • LicenseState to manage the trial and license key info,
    • PendingFileChanges, a queue I wrote about before,
    • AppRoute, an enum representing if the editor is visible or the initial launch screen;
      • .launch is a case without sub-states, because there’s no state to manage in the launch screen;
      • .main(MainRoute) is a container just like AppState, but for the active editor;
        • WorkspaceContents contains the list of search results for further processing (the model),
        • ControlState groups UI contents like the current editor contents or search result selection (more like a view model, or ”UI Input ports”),
        • CommunicationState contains pending requests by the user/UI, like “select note X” (the opposite of ControlState, in a way; think ”UI output port” or commands).[^dist]

[^dist]: I adopted the naming convention of ControlState and CommunicationState from James Nelson, “The 5 Types Of React Application State”. I had more sub-states inside MainRoute before, but was able to rearrange most of them into control/communication, or UI input/output. Lesson learned: Keep it simple, and don’t use what you don’t need, until you need it. Top-down hierarchies always bit me, this included, and I always end up having to simplify things later.

Don’t Put Service Objects Into Your App State

I already rearranged some components of MainRoute. Struggling to fix a memory leak, I changed the layout of MainRoute. Previously, MainRoute contained MainRouteState (which grouped all the sub-states mentioned above) plus a ServiceObjects reference. That was a container for classes like DirectoryMonitor, for UI presenters, ultimately referencing NSWindowController and NSViewController objects, and user event handlers. I designed it this way a year ago so I could switch the AppRoute and thus remove all service objects from memory in one swoop.

The rationale for the initial design: initializing the service components with their lifetime closely tied to the active route sounds like the route should ship with the services.

The rationale for the change: service component references don’t belong into the state. This also causes memory problems somewhere down the road in managing subscriptions.

The motivation for my initial design made sense, but is impractical. Cannot recommend.

Put Service Objects in the Layer that Provides the Services

So I extracted the ServiceComponents part from the state completely. Instead of the state module, it now is only known to the outermost layer of the assembled app itself. I put it into a global variable at first and it didn’t cause problems, yet, so there’s no reason to change this now.

Extracting the reference to the service objects wasn’t too hard. I had to flip a couple of things onto their heads, though, because previously the state would ship with its services, which made some things easy, and now I have to obtain them separately from the global variable. Overall, it was an easy change.

Introducing “Workspace” as a Meaningful Partition of State

In the process, I simplified MainRoute and got rid of the MainRouteState container. New ideas popped up, and one of them was to rearrange and rename a couple of MainRoute states. The WorkspaceContents I mentioned above contain what is accessible in the current editor. I like the term “Workspace”. It’s a good name, because it conveys that this is not all data available, but stuff that is currently in use by the user.

That’s when I decided I wanted to give the term “Workspace” more importance. It was better than “main route”, and especially better than “main state”, which is basically one big code-smell.

Then it occurred to me that when I group most MainRoute contents in a Workspace sub-state, I could magically introduce multiple workspaces. In terms of the app, that would be multiple windows or tabs. I only have to identify Workspaces, e.g. using a WorkspaceID, and make sure all user events are routed so they affect the correct workspace only.

The changes affect the main app route only, so this is what it looks like now:

  • .main(MainRoute) is still a container for the active editor window;
    • MainRoute.workspaces: [Workspace] contain a list of all active workspaces (only 1 at the moment),
      • Workspace groups everything that’s contained in an editing session,
        • workspaceID identifies the workspace,
        • WorkspaceContents still contains the list of search results for further processing (the model),
        • ControlState is mostly unchanged (UI input port),
        • CommunicationState also stays the same (UI output port).

Making Actions Workspace-Relative

This change affects how I write ReSwift.Actions.

Think about the in-process action SelectingNote, which ultimately displays the selected note and changes the search result list selection. It is not an app-global action anymore, but relative to a workspace.

To help me during the change, I removed the ReSwift.Action compliance from SelectingNote. This means I cannot send this as an action to the ReSwift.Store for processing directly anymore. The compiler will complain, and I will have to fix all outdated usages. I am using the type system and “lean on the compiler” to avoid making mistakes. But if SelectingNote is not an action anymore, I need to wrap it in an action so I can send the event.

Introducing WorkspaceTargeted<T>:

/// Type marker for the actions of `WorkspaceTargeted`, to be used instead of `ReSwift.Action` so the
/// compiler complains when you try to dispatch them directly.
public protocol WorkspaceTargeting {}

public struct WorkspaceTargeted<T: WorkspaceTargeting>: ReSwift.Action {

    public let wrapped: T
    public let workspaceID: WorkspaceID

    public init(_ action: T, workspaceID: WorkspaceID) {
        self.wrapped = action
        self.workspaceID = workspaceID
    }
}

extension WorkspaceTargeted: CustomStringConvertible {
    public var description: String {
        return "\(wrapped) @ \(workspaceID)"
    }
}

extension WorkspaceTargeted: Equatable where T: Equatable {
    public static func ==(lhs: WorkspaceTargeted<T>, rhs: WorkspaceTargeted<T>) -> Bool {
        return lhs.workspaceID == rhs.workspaceID
            && lhs.wrapped == rhs.wrapped
    }
}

I now send the event by dispatching WorkspaceTargeted(SelectingNotes(...), workspaceID: targetID).

This made me rearrange actions, separating workspace-targeted actions from app global actions. This helps find things in the project. It’s a more useful grouping of code files than I had before.

I had to change about 40 actions to make use of this new distinction. Took a while.

In the process, I also found actions that were doing multiple things, or the wrong thing completely.

For example, when you create a note in the app, that means you create an in-memory Note and write its contents as a file onto the disk. These two processes are marked as completed by dispatching CompletingNoteCreation, which is affecting the workspace, and CompletingFileCreation, which is affecting the file system.

Previously, CompletingFileCreation contained a copy of the Note that was created. This was a code-motivated addition: I needed to know when a particular Note was created so I could display it automatically. Because file creation finishes after the workspace change, I decided to attach the info to the latest action in the sequence.

Last week, I was moving CreatingNote and its CompletingNoteCreation counterpart into the Workspace-targeted actions. The resulting Note should be displayed in the workspace where the action originated. But the object was attached to the file creation action, not the note creation action. That was wrong, because file creation happens in an app-global queue. (PendingFileChanges, remember?) This didn’t cause problems before, but now it wouldn’t work anymore.

I removed the Note reference from CompletingFileCreation and attached it to CompletingNoteCreation. Then I figured, why is the sequence structured this way anyway? If creating a note starts the sequence of events, shouldn’t the completion event come last? If you think about nested events like layers of an onion, my previous sequence broke the layering. I changed the order of events a bit and now have a much neater start–finish pairing.

I probably wouldn’t have noticed this if I hadn’t changed the actions to become workspace-relative. And I wouldn’t have made them workspace-relative if the notion of a “Workspace” as a model term hadn’t appeared.

Again, this is how software development oftentimes works: you discover new concepts (here, a “workspace”) and suddenly the meaning of a lot of things going on in your app change.

The result is a code base that better conveys the intent of your actions, and improves the cohesion of states. It’s easier for me to decide if something should be part of a Workspace than it was to figure out if it affects the bland MainRouteState.

Replace RxSwift View Model Types with Free Functions

Instead of writing view model types that expose the input and/or output ports as properties, consider writing free functions that are the transformation.

This reminds me of Ken Scambler’s proposal to get rid of test doubles through side-effect free function composition. One of his examples, simplified, goes like this: Instead of testing what customer.pay(amount, wallet) does in your tests by mocking the wallet object, you rewrite Wallet as a value object and change pay to return the effect.

Before:

class Wallet {
    private var cash: Int

    func takeOut(amount: Int) {
        self.cash -= amount
    }
}

extension Customer {
    func pay(amount: Int, wallet: Wallet) {
        // Need to mock `wallet` reference type in your tests
        wallet.takeOut(amount: amount)
    }
}

After:

struct Wallet {
    var cash: Int
}

func pay(amount: Int, wallet: Wallet) -> Wallet {
    var result = wallet
    result.cash -= amount
    return result
}

This way, the original Wallet isn’t affected and you can test the payment action in isolation. Ken’s examples are more sophisticated than that, but it should get the idea across.

Now DJ Mitchell proposes a similar approach to RxSwift view models: make them mere transformations of one set of observables into another, e.g. of this form: (Observable<T1>, Observable<T2>, ...) -> (Observable<U1>, Observable<U2> ...)

Usually, in your view code, you end up with PublishSubject/PublishRelay as to generate output events that you expose as Observable. This dance is a well-established pattern to bridge between the non-reactive and reactive world. It’s useful to expose an OOP message input (e.g. accept changeUserName(_:) messages) and expose a reactive observable sequence output (e.g. userName: Observable<String>).

But when you write reactive code, you can end up with input events as observable event sequences anyway. There’s no need to use non-reactive messages in some cases.

This is where direct transformations kick in.

Look at the example by DJ:

func loginViewModel(
    usernameChanged: Observable<String>,
    passwordChanged: Observable<String>,
    loginTapped: Observable<Void>
) -> (
    loginButtonEnabled: Observable<Bool>,
    showSuccessMessage: Observable<String>
) {
    let loginButtonEnabled = Observable.combineLatest(
        usernameChanged,
        passwordChanged
    ) { username, password in
        !username.isEmpty && !password.isEmpty
    }

    let showSuccessMessage = loginTapped
        .map { "Login Successful!" }

    return (
        loginButtonEnabled,
        showSuccessMessage
    )
}

You have 3 input streams and return a tuple of 2 output streams. That pretty easy to test thorougly thanks to RxTest and its timed event recording. And it reduces the actual amount of ugly RxSwift binding code.

You can get from a lot of input/output bindings like this:

disposeBag.insert(
    // Inputs
    loginButton.rx.tap.asObservable()
        .subscribe(onNext: viewModel.inputs.loginTapped),

    usernameTextField.rx.text.orEmpty
        .subscribe(onNext: viewModel.inputs.usernameChanged),

    passwordTextField.rx.text.orEmpty
        .subscribe(onNext: viewModel.inputs.passwordChanged),

    // Outputs
    viewModel.outputs.loginButtonEnabled
        .bind(to: loginButton.rx.isEnabled),

    viewModel.outputs.showSuccessMessage
        .subscribe(onNext: { message in
            print(message)
            // Show some alert here
        })
)

To a transformation call and a lot less bindings here:

let (
    loginButtonEnabled,
    showSuccessMessage
) = loginViewModel(
    usernameChanged: usernameTextField.rx.text.orEmpty.asObservable(),
    passwordChanged: passwordTextField.rx.text.orEmpty.asObservable(),
    loginTapped: loginButton.rx.tap.asObservable()
)

disposeBag.insert(
    loginButtonEnabled
        .bind(to: loginButton.rx.isEnabled),

    showSuccessMessage
        .subscribe(onNext: { message in
            print(message)
            // Show some alert here
        })
)

I’m constantly refactoring my RxSwift-based code as I learn new things. I found view model types to be pretty useless in the long run. Writing tests for them is cumbersome because they are the transformations and expose outputs as properties, accepting inputs in the initializer. I end up with lots of object setup boilerplate. They don’t need to be actual objects since they expose to actual behavior. They are already mere value transformations, only wrapped in a type.

I guess this is a natural step when you start with OOP in mind and think about sensible encapsulations in types. But sometimes, like in DJ’s example, free functions work just as well to express your value transformation intent.

Update 2019-03-08: I continued this line of thought and refactoring in Replacing More Reference Objects with Value Types.

Programmatically Add Tabs to NSWindows without NSDocument

The Cocoa/AppKit documentation is very sparse when it comes to tabbing. You can make use of the native window tabbing introduced in macOS Sierra with a few simple method calls, though.

Conceptually, this is what you will need to do:

  • Set NSWindow.tabbingMode to .preferred so the tab bar will always be visible.
  • It suffices to call NSWindow.addTabbedWindow(_:ordered:) to add a window to the native tab bar and get everything tabs do for free.
  • Once you put NSResponder.newWindowForTab(_:) into the responder chain of the main window, the “+” button in the tab bar will be visible.

However, there are some caveats when implementing these methods naively. The plus button may stop working (no new tabs are added when you click it) and all default shortcuts are broken, their main menu items greyed out.

How to Implement newWindowForTab

First, where to add @IBAction override func newWindowForTab(_ sender: Any?)? That’ll be the event handler to create new tabs.

  • If you use Storyboards, then put this into a NSWindowController subclass you own. That’s the simplest way to get to an NSWindow to call addTabbedWindow for.
  • If you use Xibs, the AppDelegate will have a reference to the main window. You can put the method here.
  • If you use a programmatic setup, put it wherever you know the main NSWindow instance.

We’ll stick to NSWindowController for the rest of this post, no matter how you create it:

class WindowController: NSWindowController {
    @IBAction override func newWindowForTab(_ sender: Any?) {
        // Implementing this will display the button already
    }
}

How to Call addTabbedWindow

Once you have newWindowForTab(_:) in place, add functionality to it: create a new NSWindow and add it to the tab bar.

  • If you use Storyboards, grab the instance via NSWindowController.storyboard; then instantiate a new window controller instance, for example using self.storyboard!.instantiateInitialController() as! WindowController.
  • If you use Xibs with a NSWindowController as the File’s Owner, create an identical window controller using NSWindowController.init(windowNibName:). Use its window property, discard the controller.
  • If you use Xibs with a NSWindow only and no controller, get the window from there.
  • If you use programmatic setup, well, instantiate a new object of the same window type as usual.

When you have the new window object, you can call addTabbedWindow. Using the Storyboard approach, for example, turns the implementation into this:

class WindowController: NSWindowController {
    @IBAction override func newWindowForTab(_ sender: Any?) {
        let newWindowController = self.storyboard!.instantiateInitialController() as! WindowController
        let newWindow = newWindowController.window!
        self.window!.addTabbedWindow(newWindow, ordered: .above)
    }
}

Fix the “+” Button and Main Menu

TL;DR: When you initialize a new window, set window.windowController = self to make sure the new tab forwards the responder chain messages just like the initial window.

Take into account how events are dispatched. Main Menu messages are sent down the responder chain, and so is newWindowForTab. NSApp.sendAction will fail for standard events if the source of the call doesn’t connect up all the way – that means, at least up to your NSWindowController, maybe even up to your AppDelegate.

You have to make sure any additional window you add is, in fact, part of the same responder chain as the original window, or else the menu items will stop working (and be greyed-out/disabled). Similarly, the “+” button stops to work when you click on it.

If you forget to do this and run the code from above, it seems you can’t create more than two tabs. That’s the observation, but it’s not an explanation. You can always create more tabs, but only from the original window/tab, not the new one; that’s because the other tab is not responding to newWindowForTab.

Remember: “The other tab” itself is just an NSWindow. Your newWindowForTab implementation resides in the controller, though. That’s up one level.

class WindowController: NSWindowController {
    @IBAction override func newWindowForTab(_ sender: Any?) {
        let newWindowController = self.storyboard!.instantiateInitialController() as! WindowController
        let newWindow = newWindowController.window!

        // Add this line:
        newWindow.windowController = self

        self.window!.addTabbedWindow(newWindow, ordered: .above)
    }
}

Now newWindow will have a nextResponder. This will fix message forwarding.

Using Multiple Window Controllers

The solution above shows how to add multiple windows of the same kind, reusing a single window controller for all of them.

You can move up newWindowForTab one level to another service object, say the AppDelegate. Then you could manage instances of NSWindowController instead of instances of NSWindow. I don’t see why you would want to do that if you can share a single controller object.

I haven’t tried to do anything fancy besides, but you should be able to use different windows and different window controllers and group them in the tab bar of a single host window. You will then need to keep the window controller instances around, too.

NSAppearance Change Notifications for Dark Mode and RxSwift Subscriptions

Last year, I had a full-time job from May until November and haven’t had much time to prepare for Mojave. Then it hit hard, and I am still tweaking my apps to look properly in Mojave’s Dark Mode. I welcome that macOS offers two modes now, .aqua and .darkAqua in programmer parlance. Because then I don’t have to implement a homebrew solution in all my writing applications.

One troublesome change is NSColors adaptability: system colors like NSColor.textBackground (white in light mode, jet black in dark mode) or NSColor.gridColor (an 80%ish grey color in light mode, a transparent 30%ish gray in dark mode) are very useful, but some custom interface elements need different colors to stand out visually. But you cannot create adaptive NSColor objects programmatically, only via Asset Catalogs. And these don’t even work on macOS 10.11. So you limit yourself to macOS 10.13 High Sierra and newer if you use Asset Catalogs at all, even though only the light variant will be exposed there. Increasing the deployment target from 10.10 to 10.13 just for the sake of easy color changes for 10.14’s dark mode is lazy. (If you have other reasons, go for it! It’s always nice to drop backwards compatibility and use the new and cool stuff where possible.)

So what options do you have?

First, check out Daniel Jalkut’s excellent series on dark mode changes. He covers a lot of ground. Chances are you don’t need to know more than what he explains in his posts.

But how do you react to changes to the appearance? Daniel uses KVO on NSApp.effectiveAppearance, and I had mixed results with observing changes to this attribute. Also, it’s macOS 10.14+ only.

Still I want a NSNotification. Since macOS 10.9, you have NSAppearanceCustomization.effectiveAppearance, which NSView implements. So even if you cannot ask your NSApplication instance for its effective appearance, you can ask your window’s main contentView. That’s what we’ll do.

If you want to see all code, take a look at the accomanying GitHub Gist.

NSAppearance Convenience Code

First, a simple boolean indicator isDarkMode would be nice:

// Adapted from https://indiestack.com/2018/10/supporting-dark-mode-checking-appearances/
extension NSAppearance {
    public var isDarkMode: Bool {
        if #available(macOS 10.14, *) {
            if self.bestMatch(from: [.darkAqua, .aqua]) == .darkAqua {
                return true
            } else {
                return false
            }
        } else {
            return false
        }
    }
}

Now either use KVO on your main view, or expose a reactive stream of value changes.

My latest app, The Archive, uses RxSwift and other reactive patterns wherever possible. I found this to be a very nice way to decouple parts of my program, so I wanted to stick to that approach. RxSwift adds “Reactive eXtensions” to types in the .rx namespace of objects. That means I want to end up using window.contentView.rx.isDarkMode and get an Observable<Bool> instead of the momentary value. Notice the “rx” in the middle: the idea is that your own extensions use the same attribute names but inside the reactive extension to wrap changes in an Observable sequence.

Here, look at this. You write your own reactive extension for NSView in extension Reactive where Base: NSView { ... }, like that:

import Cocoa
import RxSwift
import RxCocoa

extension Reactive where Base: NSView {

    /// Exposes NSView.effectiveAppearance value changes as a
    /// never-ending stream on the main queue.
    var effectiveAppearance: ControlEvent<NSAppearance> {
        let source = base.rx
            // KVO observation on the "effectiveAppearance" key-path:
            .observe(NSAppearance.self, "effectiveAppearance", options: [.new])
            // Get rid of the optionality by supplementing a fallback value:
            .map { $0 ?? NSAppearance.current }
        return ControlEvent(event: source)
    }

    /// An observable sequence of changes to the view's appearance's dark mode.
    var isDarkMode: ControlEvent<Bool> {
        return base.rx.effectiveAppearance
            .map { $0.isDarkMode }
            .distinctUntilChanged()
    }
}

Now I can subscribe to any of the window’s view changing.

Post Notifications from Main Window Changes

As I said, I want to have notifications, e.g. in places where there is no associated view or view controller. That’s where the settings are wired up and the current editor theme switches from light to dark or vice-versa in accordance with the app’s appearance.

First, I define some convenience constants for the notification sending:

extension NSView {
    // Used as Notification.name
    static var darkModeChangeNotification: Notification.Name { return .init(rawValue: "NSAppearance did change") }
}

extension NSAppearance {
    // Used in Notification.userInfo
    static var isDarkModeKey: String { return "isDarkMode" }
}

To post the notification, I have to subscribe to any view in the view hierarchy and ask for it’s observable stream of dark mode changes:

window.contentView.rx.isDarkMode
    .startWith(NSAppearance.current.isDarkMode)
    .subscribe { isDarkMode in
        NotificationCenter.default.post(
            name: NSView.darkModeChangeNotification,
            object: nil,
            userInfo: [NSAppearance.isDarkModeKey : isDarkMode])
    }.disposed(by: disposeBag) // Assuming you have one around

It’s important to note that the view has to be in the view hierarchy (and, I think, visible) to be updated at all. I tried observing a stand-alone NSView() in a service object that had no knowledge about the app’s UI, but to no avail. Doesn’t work. So I resort to the main window’s contentView which will be around while the app is running.

Subscribe to the notification as usual.

Or employ RxSwift’s NotificationCenter extensions: `swift

let darkModeChange = NotificationCenter.default.rx .notification(NSView.darkModeChangeNotification) .map { $0.userInfo?[NSAppearance.isDarkModeKey] as? Bool } // Equivalent to a filterNil(): ignore incomplete notifications .filter { $0 != nil }.map { $0! }

darkModeChange .subscribe(onNext: { print(“Dark mode change to”, $0) } .disposed(by: disposeBag) `

So that’s what I use and I’m content with the solution for now. I can switch colors manually where needed and reload the editor theme for the app’s current appearance.

Thanks again to Daniel Jalkut and his research!

To see all the code in one place, have a look at the GitHub Gist that goes with this post.

WordCounter v1.4.0 Released with Mojave in Mind

Word Counter v1.4.0 screenshot
Screenshot of v1.4.0 in Mojave’s Dark Mode

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.

But probably most noteworthy, this is the first public release since 2016! Publishing updates stalled because of … managerial problems. I introduced the Swift programming language to this Objective-C project a couple years back, and didn’t keep up with language updates. So I had to take a couple of days to update the Swift code base. And then fix crashing tests, key–value-observation issues with Swift objects – in short, I failed to maintain this project on a regular basis, and once it became harder to get back to speed, I deferred touching the app even longer, making matters worse.

And all that while a new feature lay dormant, 3/4th done.

This updates means serious business. I’m back to make the app even cooler. Expect nice things to follow early 2019.

Also expect the latest update not to work on macOS 10.9, 10.10, or 10.11 anymore. You will need macOS 10.12 Sierra or newer from this day forward.

Check out the release notes for a breakdown of the changes.

Find Swift Classes from Objective-C Tests

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:

#import "MyProject-Swift.h"

Problem is, this generated code doesn’t reside in the project directory, but in the build directory. More specifically, it resides in

$CONFIGURATION_TEMP_DIR/MyProject.build/DerivedSources

Once you add this line (and replace “MyProject” with the appropriate name) to your unit test target’s Build Settings > Header Search Paths, importing your app’s generated Swift header file will work.

Let’s hope neither you nor I will ever forget how this works again.

Fix NSSegmentedControl Action Forwarding from Your NSToolbar

Two years ago, I wrote about how I implemented a toolbar with NSSegmentedControl, much like Apple’s own apps have them since macOS 10.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.

  1. Original Approach
  2. Enabling/Disabling Segments
  3. Fixing the action dispatching bug (this post)

NSToolbarItems are not activated without labels

montage of two toolbars
NSToolbarItem handles clicks only when the label is visible

My interpretation of the situation is this: when you show labels, the NSToolbarItem is responsible for providing the label below the segments of the control. It also is responsible for dispatching an action on click. This works with “Icon and Text” and “Text Only”. But once you hide the labels, the NSToolbarItems do not do anything at all – neither display, nor action dispatching.

The nice thing about splitting a single NSSegmentedControl into multiple NSToolbarItems was that you were able to bind each item to its own action, which magically corresponded to the segments of your control. Unlike other NSView-based controls, NSSegmentedControl’s segments are not NSView based themselves, and thus cannot be NSControl types which handle their own action dispatching. Instead, they are NSCell subclasses that mostly handle drawing. NSSegmentedControl is the only one in the setup that has an action property that takes a selector. It’s the only one responding to events.

Change the setup to make the NSSegmentedControl respond to events

So let’s change everything and let the NSSegmentedControl do all the work.

Why transfer ownership of action dispatch instead of mix both approaches? – Because then you have two approaches to maintain. And I don’t want to manually test all buttons in all toolbar configurations.

The course of action is thus:

  1. Make the universally functional NSSegmentedControl.action the main click handler.
  2. Leave NSToolbarItem.action = nil so the control doesn’t get overridden.
  3. From the NSSegmentedControl.action handler, dispatch the real segment’s action, depending on which the user did click.

If you’re lazy, you’d be implementing the toolbar actions in your NSWindowController since that’s guaranteed to be somewhere in the responder chain.

But we ain’t no lazy folk in this here web space!

Also, the app I’m using this for, TableFlip, was creating the old setup in a factory outside of any view or window controller. And I wasn’t going to sacrifice this one good design choice by pasting everything back into the window controller.

Questions raised:

  • Where to put the general-purpose action?
  • How to delegate to a single segment’s real action?

There was no meaningful name I could give a general-purpose action that was going to respond to clicks to any segment. segmentSelected(_:) was the best name I could come up with: it is not an expression of a user intent, it’s an implementation detail. The user intent, like ”addRow(_:) to this table”, is bound to the segments; the NSSegmentedControl grouping does not carry any meaning except the grouping in the UI. Again, an implementation detail. That’s a good reason to stick to a method name that would otherwise be considered code smell.

Which object shall become the dispatcher? In my opinion, this setup is a deficit of AppKit’s current API. So I think the best place to handle this is an NSSegmentedControl itself:

class DispatchingSegmentedControl: NSSegmentedControl {
    func wireActionToSelf() {
        self.target = self
        self.action = #selector(segmentSelected(_:))
    }

    @IBAction func segmentSelected(_ sender: Any) {
        // Dispatch according to `self.selectedSegment`
    }
}

The implementation for this could instead be part of a controller object. But this is not an event I want to respond to; it’s an event the view component should transform in accordance with its internals, with its setup of segments, into a more specific event.

You could have a Array<(target: Any?, action: Selector)> that corresponds to the segments, and get to it using selectedSegment inside of segmentSelected(_:). Or you whip up a more elaborate configuration. I did the latter, and will show you my stuff in the remainder of this post.

My approach to define the segments

Note: I am still using Interface Builder to configure the NSSegmentedControl. So I do start with instances of these, with icons and segment widths pre-configured.

I do have to overlay the target–action-mechanism, though, similar to the overlay by NSToolbarItem used before.

Representation of the segment configurations

The view model type I introduces is called ToolbarSegmentedControlSegment. It represents the configuration of a segment:

struct ToolbarSegmentedControlSegment {

    var toolbarItemIdentifier: NSToolbarItem.Identifier
    var label: String
    var action: Selector
    var menuTitle: String?
    var menuImage: NSImage?

     init(toolbarItemIdentifier: NSToolbarItem.Identifier,
          label: String,
          action: Selector,
          menuTitle: String? = nil,
          menuImage: NSImage? = nil) {
        self.toolbarItemIdentifier = toolbarItemIdentifier
        self.label = label
        self.action = action
        self.menuTitle = menuTitle
        self.menuImage = menuImage
    }
}

It offers a factory method to get to a NSToolbarItem representation. This is still needed to display the labels in the toolbar, and to handle the toolbar overflow menu. This is what I was doing in the original NSToolbarItem setup, now moved here:

extension ToolbarSegmentedControlSegment {
    func toolbarItem() -> NSToolbarItem {
        // Do not set the item's action and rely on the ToolbarSegmentedControl instead.
        // This makes it easier to always be running into the same bugs, if any,
        // and not have 2 paths :)
        let item = NSToolbarItem(itemIdentifier: toolbarItemIdentifier)
        item.label = label
        item.updateMenuFormRepresentation()
        item.menuTitle = menuTitle
        item.menuImage = menuImage
        return item
    }
}

It can also execute the action dispatch:

extension ToolbarSegmentedControlSegment {
    func dispatchAction() {
        NSApp.sendAction(action, to: nil, from: nil)
    }
}

And that’s it for that!

Wiring the segment configurations into the NSSegmentedControl

Lastly, the changes to the view component. Remember that I am using Interface Builder, so I rely on configuration after initialization. If you create your control programmatically, you can change things up a bit, like wire the action to self in the initializer, and take an array of segment configurations as initializer parameter as well.

I’m storing the segment configurations in an array and mutate it using the addSegment method, which acts as a factory.

class ToolbarSegmentedControl: NSSegmentedControl {

    var segments: [ToolbarSegmentedControlSegment] = []

    func addSegment(toolbarItemIdentifier: NSToolbarItem.Identifier,
                    label: String,
                    action: Selector,
                    menuTitle: String? = nil,
                    menuImage: NSImage? = nil) {
        guard !segments.contains(where: { $0.toolbarItemIdentifier == toolbarItemIdentifier }) else { return }

        let segment = ToolbarSegmentedControlSegment(
            toolbarItemIdentifier: toolbarItemIdentifier,
            label: label,
            action: action,
            menuTitle: menuTitle,
            menuImage: menuImage)
        segments.append(segment)
    }

    func toolbarItems() -> [NSToolbarItem] {
        return segments.map { $0.toolbarItem() }
    }

    func wireActionToSelf() {
        self.target = self
        self.action = #selector(segmentSelected(_:))
    }

    @IBAction func segmentSelected(_ sender: Any) {
        segments[selectedSegment].dispatchAction()
    }
}

With these convenient wrappers in place, I can setup a whole NSToolbarItemGroup:

// Get the view component from Interface Builder
let toolbarSegmentedControl: ToolbarSegmentedControl! = ...

// Configure segments
toolbarSegmentedControl.addSegment(
    toolbarItemIdentifier: .init(rawValue: "alignLeft"),
    label: "Left",
    action: #selector(TableInteractions.alignColumnLeft(_:)),
    menuTitle: "Align Left",
    menuImage: Alignment.left.image)
toolbarSegmentedControl.addSegment(
    toolbarItemIdentifier: .init(rawValue: "addCenter"),
    label: "Center",
    action: #selector(TableInteractions.alignColumnCenter(_:)),
    menuTitle: "Center",
    menuImage: Alignment.center.image)
toolbarSegmentedControl.addSegment(
    toolbarItemIdentifier: .init(rawValue: "alignRight"),
    label: "Right",
    action: #selector(TableInteractions.alignColumnRight(_:)),
    menuTitle: "Align Right",
    menuImage: Alignment.right.image)

toolbarSegmentedControl.wireActionToSelf()

let itemGroup = NSToolbarItemGroup(itemIdentifier: .init("alignmentGroup"))
itemGroup.view = toolbarSegmentedControl
itemGroup.subitems = toolbarSegmentedControl.toolbarItems()

// return `itemGroup` from  `toolbar(_:itemForItemIdentifier:willBeInsertedIntoToolbar:)`

Conclusion

Even though I hesitated at first, putting the NSSegmentedControl.action handler into a subclass itself and call it segmentSelected(_:) was the right choice. It’s an implementation detail on the code level, and I want to encapsulate forwarding segment selection to in that type.

Configuring a segment and thus preparing a NSToolbarItem is quite a bit of ceremony – there are just so many properties you have to set. But I prefer this over the standard AppKit approach any day, which would have you write the property settings in a procedural fashion. I like that these configuration object initializers group all the options, and that I get a couple of methods in for free which I don’t have to write elsewhere.

All in all, it’s still surprisingly cumbersome to set up a NSToolbarItemGroup around a NSSegmentedControl in AppKit. But it’s manageable, and now that you know the individual subitems’s action dispatching cannot be trusted, you’re better off with a self-made solution.


→ Blog Archive