macosswift3xcode8nsundomanager

Using NSUndoManager and .prepare(withInvocationTarget:) in Swift 3


I am migrating an Xcode 7 / Swift 2.2 mac OS X project to Xcode 8 / Swift 3, and I have run into a problem using undoManager in my view controller class, MyViewController, which has a function undo.

In Xcode 7 / Swift 2.2, this worked fine:

undoManager?.prepareWithInvocationTarget(self).undo(data, moreData: moreData)
undoManager?.setActionName("Change Data)

In Xcode 8 / Swift 3, using the recommended pattern from https://developer.apple.com/library/content/documentation/Swift/Conceptual/BuildingCocoaApps/AdoptingCocoaDesignPatterns.html

this should be changed to:

if let target = undoManager?.prepare(withInvocationTarget: self) as? MyViewController {
    target.undo(data, moreData: moreData)
    undoManager?. setActionName("Change Data")
}

However, the downcast to MyViewController always fails, and the undo operation is not registered.

Am I missing something obvious here, or is this a bug?


Solution

  • prepareWithInvocationTarget(_:)(or prepare(withInvocationTarget:) in Swift 3) creates a hidden proxy object, with which Swift 3 runtime cannot work well.

    (You may call that a bug, and send a bug report.)

    To achieve your purpose, can't you use registerUndo(withTarget:handler:)?

    undoManager?.registerUndo(withTarget: self) {targetSelf in
        targetSelf.undo(data, moreData: moreData)
    }