Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Best practice to implement canClose(withDelegate:shouldClose:contextInfo:)

Tags:

macos

swift

I am trying with NSDocument based application first time. (Xcode 9.2, Swift 4, macOS 10.12 Sierra, and Cocoa/AppKit)

I would like to know proper way to implement closing Document with No autosaving. Would you please show me the best practice of closing NSDocument?

When user is trying to close document, following NSDocument method is called.

canClose(withDelegate:shouldClose:contextInfo:)

I debug the parameters and find followimng:

delegate = MyApp.Document
shouldCloseSelector = _something:didSomething:soContinue:
contextInfo = libsystem_blocks.dylib `_NSConcreteMallocBlock

Apparently such selector is not available, so I think this can be altered here like:

override func canClose(withDelegate delegate: Any,
                       shouldClose shouldCloseSelector: Selector?,
                       contextInfo: UnsafeMutableRawPointer?) {
    let delegate : Any = self
    let shouldCloseSelector : Selector = #selector(Document.document(_:shouldClose:contextInfo:))
    super.canClose(withDelegate: delegate, shouldClose: shouldCloseSelector, contextInfo: contextInfo)
}

@objc func document(_ document : NSDocument, shouldClose flag : Bool, 
                    contextInfo: UnsafeMutableRawPointer?) {
    if document === self, flag {
        self.cleanup() // my cleanup method
        self.close() // NSDocument.close()
    }
}

deinit {
    Swift.print(#function, #line)
}

It seems to work, but I guess this is not proper way, because of ignroing original parameters (Selector/contextInfo).

like image 961
MyCometG3 Avatar asked Mar 18 '26 02:03

MyCometG3


1 Answers

Update

I have found solution. I hope this would help someone.

private var closingBlock : ((Bool) -> Void)? = nil

override func canClose(withDelegate delegate: Any,
                       shouldClose shouldCloseSelector: Selector?,
                       contextInfo: UnsafeMutableRawPointer?) {
    let obj : AnyObject = delegate as AnyObject
    let Class : AnyClass = object_getClass(delegate)!
    let method = class_getMethodImplementation(Class, shouldCloseSelector!)
    typealias signature =
        @convention(c) (AnyObject, Selector, AnyObject, Bool, UnsafeMutableRawPointer?) -> Void
    let function = unsafeBitCast(method, to: signature.self)

    self.closingBlock = {[unowned obj, shouldCloseSelector, contextInfo] (flag) -> Void in
        function(obj, shouldCloseSelector!, self, flag, contextInfo)
    }

    let delegate : Any = self
    let shouldCloseSelector : Selector = #selector(Document.document(_:shouldClose:contextInfo:))
    super.canClose(withDelegate: delegate, shouldClose: shouldCloseSelector, contextInfo: contextInfo)
}

@objc func document(_ document : NSDocument, shouldClose flag : Bool,
                    contextInfo: UnsafeMutableRawPointer?) {
    if flag {
        self.cleanup() // my cleanup method
    }

    self.closingBlock?(flag)
    self.closingBlock = nil
}

override func close() {
    Swift.print(#function, #line, #file)
    super.close()
}

deinit {
    Swift.print(#function, #line, #file)
}

Reference information

https://developer.apple.com/library/content/releasenotes/AppKit/RN-AppKitOlderNotes/

Advice for Overriders of Methods that Follow the delegate:didSomethingSelector:contextInfo: Pattern

https://github.com/DouglasHeriot/canCloseDocumentWithDelegate/blob/master/canCloseDocumentWithDelegate/Document.swift

Shows how to implement the NSDocument method -canCloseDocumentWithDelegate:shouldCloseSelector:contextInfo: using Swift. Must use Objective-C to perform an NSInvocation.

like image 131
MyCometG3 Avatar answered Mar 19 '26 20:03

MyCometG3



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!