book covers

Indie macOS Dev Bundle, Swift 3 Ed..

Design solid applications and own your distribution process. Learn software architecture principles to make your Swift apps easier to maintain and extend. Set up your app for sale, with a trial mode and license generation all set. Including fully functional sample projects and code templates for your app.

Setting the Line Height of a NSTextView

NSTextView (and UITextView for that matter) have a defaultParagraphStyle attribute where you can set the text’s line height. That works swell – if you display text statically. Once the user can enter something, you can run into trouble:

  • If the user adds text in the middle of an already laid-out line of text, the paragraph style is retained.
  • If the user writes at the beginning of the line, the line height info gets lost.
GIF of the process
This is what happens when you type at the beginning of a line

It’s your usual RTF nightmare. I know this behavior from rich text editors; and I developed my own way to make sense of it in the process. It might not be what is really going on, but it’s a good heuristic: it’s just like the opposite of making a word bold, placing your cursor after that word, type, and get more bold text. There, the “bold text” information is carried on. The cursor inherits this info from the character left to it. But if you start at the beginning of a line, your cursor will not inherit what comes afterward. And since there is nothing before its position, it starts with empty info, and thus empty line height settings. Since the whole paragraph is affected by this, the latest change wins. Beginning to type at the beginning of a paragraph with empty paragraph settings removes them from what comes afterwards.

So this might not be The Truth, but it helps me deal with shitty software. I don’t want to write shitty software, though, so I look for ways out of this. I don’t intend the user to change paragraph settings; I want the text view to have a certain look and feel no matter what gets pasted or typed in it.

Hunting for Core Text/TextKit callbacks, NSTextStorageDelegate seems to provide a good customization point:

func textStorage(
    _ textStorage: NSTextStorage, 
    didProcessEditing editedMask: NSTextStorageEditActions, 
    range editedRange: NSRange, 
    changeInLength delta: Int
) {
    let paragraphStyle = NSMutableParagraphStyle()
    paragraphStyle.lineHeightMultiple = 2.0
    textStorage.addAttributes([NSParagraphStyleAttributeName : paragraphStyle], range: editedRange)
}

Of course it makes sense to store the global paragraphStyle once and re-apply it here. I don’t know if this is the best place to put it, though. Re-applying all the NSAttributedString settings while typing might not perform best.

Also, this is affecting the “rich text representation” of the text. If you copy the result and paste it into TextEdit, say, the text will look the same, line height settings and all.

You can override the pasteboard representation to be “plain text” only in order to remove the style info and thus have it behave like the “Paste and Match Style” command from the “Edit” menu automatically.

Again, I don’t know if this is the best way to do this.

What I’d expect to create instead:

  • a text view with a “plain text” representation by default (the model)
  • typesetting customizations that only affect what is visible on screen (the view)

I imagine this to be like HTML code/browser rendering, not like WYSIWYG. What you type is not what you see. Just what you’d expect a source code editor to be like.

I’ll keep you posed as I dive deeper into TextKit and stuff.

How to Make ReSwift Actions Undoable with NSUndoManager

I wrote about using NSUndoManager + ReSwift with a Middleware. You could think of the resulting UndoMiddleware as some kind of observer of events that pass the ReSwift.Store and which puts the opposites of incoming actions on the undo stack. Why a Middleware? Because making an action undoable is a side effect.

The Middleware wasn’t exactly straightforward. It took a bit of type wrapping and boilerplate code. A “context provider” made parts of the current app state and model available to compute the opposite action. Without this context, the Middleware couldn’t know what was expected.

Today I came up with something shorter: an action creator. It works well because the actions in my current project are simple and easily reversed.

Instead of dispatching an action like this:

store.dispatch(CreateFoo(id: 1))

… you create the opposite of the action where you already do have knowledge about the context:

let creation     = CreateFoo(id: 1, content: "...")
let undoCreation = DeleteFoo(id : 1)
store.dispatch(undoable(creation, opposite: undoCreation))

Of course it takes more effort to compute it the other way around because you have to fetch the current content before you delete (that’s what the context provider of my Middleware did, too):

let id = // ...
let oldContent = foo(withId: id).content
let deletion =     DeleteFoo(id: id)
let undoDeletion = CreateFoo(id: id, content: content)
store.dispatch(undoable(deletion, opposite: undoDeletion))

Chances are you have a lot of the pieces of the puzzle to assemble the “undo” action in the service object that dispatches the original action. In contrast, the Middleware knew nothing and thus had to depend on another source of information for everything.

The undoable action creator is very simple, after all:

public func undoable(_ action: Action, opposite: Action?)
    -> (AppState, DefaultStore)
    -> Action?
{
    return { (appState: AppState, store: DefaultStore) -> Action? in
    
        if let undoAction = opposite,
            
            // Replace this with something useful in your app:
            let undoManager = getUndoManagerFromSomewhere() {
            
            undoManager.registerUndo(withTarget: store) { store in
                store.dispatch(undoable(undoAction, opposite: action))
            }
        }
    
        return action
    }
}

This assumes symmetry of actions: inside the registerUndo block, undoable is called again, only the other way around, to setup what “redo” should be like. If your original action is not what you want to use for redoing, then you need to put that in somehow.

This is the case in my app where DeleteFoo creates a pending file change that ends with dispatching DeleteFooCompleted. The opposite of DeleteFooCompleted is CreateFoo – but the opposite of CreateFoo is DeleteFoo, not DeleteFooCompleted, so I put a mapping in between:

public func redoable(basedOn action: Action) -> Action {
    switch action {
    case let action as DeleteFooCompleted:
        return DeletingFoo(id: action.id)

    default:
        return action
    }
}

public func undoable(_ action: Action, opposite: Action?)
    -> (AppState, DefaultStore)
    -> Action?
{
    return { (appState: AppState, store: DefaultStore) -> Action? in
    
        if let undoAction = opposite,
            let undoManager = getUndoManagerFromSomewhere() {

            // Original trigger might be a 'completion' event that has to be
            // "redone" using a 'starting' event.
            let redoAction = redoable(basedOn: action)

            undoRegistrar.registerUndo(withTarget: store) { store in
                store.dispatch(undoable(undoAction, opposite: redoAction))
            }
        }
    
        return action
    }
}

And that’s it!

The biggest problem is to obtain a reference to the main window’s UndoManager instance. You can inject it once but not replace it later, which I would have needed, so I end up with a getter here, which I don’t like a lot; you might be fine with providing a custom UndoManager instance during setup, though! In NSDocument-based apps, where each document has its own window with its own undo manager and probably its own store instance, it’ll be pretty easy.

Handle Pending File Changes with ReSwift as Your Backbone

Automatic saving of changes in some user interface component to a file should be handled differently when you employ ReSwift.

sketch of the information flow
Your UI tells the app it finished editing. Then a reducer enqueues a pending file change.

In short, you have to extract the state information from the “save file” action and store it in the overall app’s state somehow. I append these to a collection of PendingFileChanges, a ReSwift.StateType that is part of my overall app state.

(This is a follow-up to my previous musings on the topic.)

“Save file” has the state info of “which file” and “what contents”. In order to enqueue multiple changes to the same file, add a UUID to each file change to identify the event:

struct FileContentChange {
    let url: URL
    let content: String
    let uuid: UUID
}

Instances of this type are enqueued with the PendingFileChanges substate. I used real FIFO queues at first and solely relied on the order, but later switched to simple arrays. I’ll tell you why in a minute.

struct AppState: StateType {
    var pendingFileChanges: PendingFileChangesState = .empty
}

struct PendingFileChangesState: StateType {

    static var empty: PendingFileChangesState {
        return PendingFileChangesState(fileContentChanges: [])
    }
    
    public fileprivate(set) var fileContentChanges: [FileContentChange]

    public var nextFileContentChange: FileContentChange? {
        return fileContentChanges.first
    }

    public mutating func insert(fileContentChange: FileContentChange) {
        fileContentChanges.append(fileContentChange)
    }

    public mutating func remove(fileContentChange: FileContentChange) {
        guard let index = fileContentChanges.index(of: fileContentChange) 
            else { return }
        
        fileContentChanges.remove(at: index)
    }
}

Some long-running service object will listen to changes to this substate and perform the necessary writes periodically.

To simplify the setup, make the resulting ChangeFileContents service object a ReSwift.StoreSubscriber; now it’ll receive updates to the app state from your store. Cache the last received (and thus pending) change in the service so you can make the operation asynchronous and allow more incoming state updates without issuing to overwrite the file time and again with the same stuff.

When it finishes a write operation, it in turn dispatches an action so the fitting item is removed from the queue.

Here’s a sample service wireframe:

typealias DefaultStore = Store<AppState>

class WriteFileChanges: StoreSubscriber {
    let store: DefaultStore

    public init(store: DefaultStore) {
        self.store = store
    }

    fileprivate(set) var lastChange: FileContentChange?

    public func newState(state: PendingFileChangesState) {
    
        // If the state update shows the queue is empty, 
        // reset the cache.
        guard let change = state.nextFileContentChange else {
            lastChange = nil
            return
        }
        
        // Skip identical, unprocessed updates.
        guard change.uuid != lastChange.uuid else { return }
        
        // Set the cached value to prevent duplicate changes.
        lastChange = change

        delegatePerformingTheFileWriteOperation() { success in
            if !success { 
                // TODO: handle error :)
            }
            
            store.dispatch(CompletingFileContentChange(change))
        }
    }
    
    fileprivate func delegatePerformingTheFileWriteOperation(
        completion: (Bool) -> Void) {
        
        // Use FileManager or String.write(toURL:) etc.
        
        completion(true)
    }
}

So you end up with WriteFileChanges as a service that processes a part of the PendingFileChangesState queue. To let the app know when it has finished, i.e. let a reducer remove the entry from the pending changes queue, it dispatches a CompletingFileContentChange action. I leave this simple wrapper as an exercise to the reader.

Now that you know all of the code, here’s why I ditched queues at first. With queues, I relied on the order of elements and used equality checks in the WriteFileChanges service to prevent doing the same operation twice. Before the UUID, enqueuing 2 equal FileContentChange objects resulted in the first one being processed, then popped from the queue after completion – but then the second, identical object would never be processed. Now that I think of it, since adding the UUID, the issues I had with queues is solved. So it’d actually be a better idea to use a queue instead of an array to be clear about the order. Plus make it a UniqueElementQueue or similar and add equality checks before adding elements so you don’t end up enqueuing the same thing twice while lifting this detail into the type itself.

A queue-based approach is a good idea because most apps will rely on the order of file change events. If you have write and delete operations, you will want to put both into the same queue, too, by the way, and introduce a common type for FileContentChange and FileRemoval, say.

Having written that down, I’m going to change my implementation from array to queue with the UUID and see where this leads first thing next week.

Recording of Wednesday’s Webinar

Sadly, the recording doesn’t contain the webcam stream. I find videos like this pretty hard to follow. Especially if the speaker isn’t native English, then it can be quite a pain sometimes. I hope it’s not too bad in this case!

I promised not to distribute any follow-up material until next week so that webinar attendees have early access.

But you can get a 20% discount on “Make Money Outside the Mac App Store” by using the coupon code MORECONTROL, good until March 31st!

Make Money Outside the Mac App Store – Swift 3 Update

book cover

Ladies and gentlemen, I have finished updating my e-book about ditching the app store and selling your apps with the help of FastSpring for Swift 3!

I also got annoyed by “$x.99” prices and lowered the price from $24.99 to $22. The Indie Mac Developer Book Bundle is now just $27, too.

The code was updated a while ago already. I just never finished editing the manuscript because I wanted to add so many things. Now I’ve only done minor edits, but that’s better than having an outdated book, right?

(The print version is still being proofed and will take another day or two until it’s ready.)

If you bought the book from my store already, you can download an updated version using the same download link. Lost the link? E-mail me and I’ll re-send you the details.

Register for My Webinar About Ditching the Mac App Store

I will be hosting a webinar about distributing your app outside the Mac App Store next week. It’s free, and you’re very welcome to attend!

Wednesday, February 15th, 2017 @ 10:00 AM PST (will run 1 hour)

→ Register Now

Topics include:

  • Ditching the Mac App Store – Why, and what it means.
  • Is There Life After the Mac App Store? – Choosing an e-commerce provider.
  • Getting Back to Development – How you change your code to work outside the Mac App Store.

Plus you’ll see me live. That alone should be motivation for you to visit, no matter the topic :)

Disclaimer, aka Code of Honesty and Transparency

Maybe you just arrived on my blog and don’t know how I do things around here, yet, and how much I value morality over profit anytime. So let me erase your doubts about this webinar cooperation.

I use FastSpring to sell my stuff. I really like their service, so I wrote a guide in 2015. FastSpring in turn was impressed by my initiative and helped me spread the word a bit and provide background info whenever I needed anything. I am not getting paid by FastSpring for the book. It’s my own creative work. I maintain it because I think it helps you, fellow developer, to set up your own indie business.

Then late 2016, FastSpring approached me as their go-to expert for Mac app development. They plan to show how easy it is to use FastSpring to distribute Mac apps. In other words, they want to own part of the good news for obvious marketing reasons.

With the recent Out-of-App-Store Success Stories by Rogue Amoeba and Kapeli, it may even be a growing market.

I was skeptical at first. I will not violate my strong ethics; teaching people the One True Way™ is more important than easy money. But I came to find FastSpring values delivering useful content over running a 60-minute ad show. From the get go, FastSpring wanted me to create the content. Not even once did they suggest I add something to my slides. They totally risk I go live on Wednesday and tell people to use a competing service. But I won’t, because I know no better service provider. I liked the concept, so I agreed. I am getting paid by FastSpring for this webinar gig. But it’s 100% my webinar.

To stay true to myself, I will give genuinely helpful advice to empower the attendees to become independent. Of course I’ll show FastSpring’s features, just like the screenshots I put in my book. All because I believe in their service, not because they bought my loyalty with the speaker fee.

In short, this is not an advertisement for FastSpring. I haven’t sold my soul. It’s a cooperation out of mutual respect.

Hope to be seeing you around on Wednesday!

Express Either–Or-Relationships as Enums

If you want to encapsulate the notion of “either A or B” (also called “lifting” in functional parlance), an enum type in Swift is the best fit:

enum EitherOr {
    case A, B
}

You can use associated values to wrap types with enums, too:

enum Fruit {
    case banana(Banana)
    case apple(Apple)
}

These things seem to be expressible through a common ancestor type or protocol. But bananas and apples can be modeled in totally different manners (apart from sharing nutritional value, for example).

A real-life example is the result of a network request. Instead of a classic Cocoa callback, express the strict either-or through a type:

/// Only one is ever supposed to be present, but you never
/// know at the call site.
typealias ResultCallback<T> = (result: T?, error: Error?) -> Void

/// Only one can ever be used, never both.
enum Result<T> {
    case success(T)
    case error(Error)
}
typealias ResultCallback2<T> = (Result<T>) -> Void

How to Unit Test Dispatching ReSwift Actions from RxSwift Observables

Say you are like me and work with ReSwift for unidirectional data flow goodness and employ RxSwift for your reactive cravings. You want to dispatch a ReSwift.Action when an RxSwift.Observable signal produces a new value. How do you write tests for that wiring?

In code, it could look like this:

someObservable
    .map { SomeAction($0) }
    .subscribe(onNext: store.dispatch($0))
    .addDisposableTo(disposeBag)

Testing that code requires a bit more understanding of both libraries than their respective documentation reveals.

Here’s an example:

import ReSwift

struct AppState {
    var count = 0
}

typealias DefaultStore = Store<AppState>

struct ChangingCount: ReSwift.Action {
    let value: Int
    init(value: Int) {
        self.value = value
    }
}

Imagine a reducer that takes incoming ChangingCount.value and replaces AppState.value with that. Simple.

Now you want to dispatch a ChangingCount action when some signal produces a new value. You need to create an observer for the signal and store it somewhere. So a friendly helper class comes to mind:

import RxSwift

class Dispatcher {
    let store: DefaultStore
    let disposeBag = DisposeBag()
    
    init(store: DefaultStore) {
        self.store = store
    }
    
    func wireSignalToAction(signal: Observable<Int>) {
        signal.map(ChangingCount.init(value:))
            .subscribe(onNext: { [weak self] in
                self?.store.dispatch($0) })
            .addDisposableTo(disposeBag)
    }
}

And that’s it. Now how can you make sure it does what it should do? What do unit tests look like?

Taking a look at RxSwift’s TestObserver and TestScheduler, you can record incoming events yourself and perform checks. To record events, you hijack a ReSwift.Store’s dispatchFunction with a closure that captures the incoming action. Using a TestScheduler, you can time production of events in the signal and keep track of the time the action comes in:

import RxSwift
import RxTest

class DispatchingTests: XCTestCase {
    /// Replacement for your real reducer because you won't need it.
    struct NullReducer: ReSwift.Reducer {
        func handleAction(action: Action, state: AppState?) -> AppState {
            return AppState(value: -1)
        }
    }
    
    func testDispatchesValueChanges() {
        
        // Set up a signal. Subscription per default starts at time=200
        let scheduler = TestScheduler(initialClock: 0)
        let signal = scheduler.createHotObservable([
            next(300, 98),
            next(600, 12)
            ])
        
        // Set up the store and record matching actions.
        let storeDouble = Store(reducer: NullReducer(), state: nil)
        var actions = [Recorded<Event<ChangingCount>>]()
        storeDouble.dispatchFunction = {
            guard let action = $0 as? ChangingCount else { return () }
            // `return f()` where `f() -> Void` is just like `return ()`.
            // We need to return something to satisy the `-> Any`.
            return actions.append(Recorded(time: scheduler.clock, value: .next(action)))
        }

        // Configure the object under test and start emitting
        // values in virtual time.
        let dispatcher = Dispatcher(store: storeDouble)
        dispatcher.wireSignalToAction(signal)
        scheduler.start()

        XCTAssertEqual(actions, [
            next(300, ChangingCount(value: 98)),
            next(600, ChangingCount(value: 12))
            ])
    }
}

Core Data is Invasive. You Can Hide It, Or You Can Embrace It

Found this nice post about using Swift protocols to expose read-only properties of Core Data managed objects so you don’t couple your whole app to Core Data.

Using Swift protocols in Core Data NSManagedObjects is a great way to limit the visibility of properties and methods. In my 1st book on Mac app development I talked about this, too, and this is a lot easier to handle than a custom layer of structs that you have to map to NSManagedObject and back again. Core Data is designed to be invasive and convenient. It’s not designed to be used as a simple object-relational mapper.

If you have more than just data, like a fairly complex Domain Model with rich behavior and nested sub-components, then using protocols to expose read-only data won’t cut it, though. This approach is super useful to expose properties for reading when their types are built-in or part of Foundation – in other words, when Core Data knows what to do with them. But if you create your own Tree type with a [Banana] property, this again won’t help.

When you plan your app, think hard about the real use of your database/persistence layer. Core Data can do a lot of the tedious work and you can easily sync stuff over iCloud. But if you really need a custom database, don’t try to force Core Data into the equation.

The age-old “pro tip” of deferring decisions about persistence frameworks is sound, but you may have a harder time adding Core Data late in the project than adding it early. As I said, Core Data is invasive, but you can use this to your advantage if you know that the amount of coupling to Core Data is just what you need.

If all you can think about is clean code and awesome software architecture, this will feel like betraying your own principles. It’s an utterly pragmatic choice, based on the principle of “does this help us ship?”


→ Blog Archive