Do you need help on a specific subject? Use the contact form (Request a blog entry) on the right hand side.

2015-09-19

Code sample: Throwing an NSError to OS-X

NSError is used in document based app's to inform the OS about errors that occurred while opening or saving data (and in other places as well).
Since the OS will attempt to inform the user with a popup about the error, it is good practise to provide actual useful information in the thrown error such that the user sees more than "app X has thrown error 16"

Here an example on how to do this for the readFromData function.

override func readFromData(data: NSData, ofType typeName: String) throws {
                        
        if let fileAsString = NSString(bytes: data.bytes, length: data.length, encoding: NSUTF8StringEncoding) as? String {
                        
            // do stuff ....
            
        } else {
            
            let errorDict = [
                NSLocalizedDescriptionKey : "Could not convert the file data to a UTF8 string. (File at path = \(fileURL?.path))",
                NSLocalizedRecoverySuggestionErrorKey: "Try to open the file in TextEdit to see if it does contain text."
            ]

            let error = NSError(
               domain: Globals.errorDomain,
               code: Globals.errorCode_cannotRetrieveFileAsString, userInfo: errorDict)
            throw error
        }

    }

The fileURL is defined in NSDocument and can be used to get the URL of the file that is being openend.

The popup dialogue that is provided to the user uses the two key's in the errorDict to retrieve information from the error. (NSLocalizedDescriptionKey and NSLocalizedRecoverySuggestionErrorKey) There are more key's, but these two are the one's you should provide as a minimum.

It is customary to provide the errorDomain in backward URL notation, eg: "com.mycompany.MyGreatApp.ErrorDomain" the code can be any number within this error domain.

If you do not provide an errorDict, OS-X will create an localized description from the error domain and code number.

Happy coding...

Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH

We don't get the world we wish for... we get the world we pay for.

2015-09-18

Code sample: NSError and Swift 2.0

This is a follow up on a previous post about the NSError (pointer).

In Swift 2.0 the NSError in the NSFileManager calls have been replaced with try blocks.

Here is how Swift 2.0 implements the NSError in try blocks:

    lazy private var applicationSupportLogfileDirectory: String? = {


        let fileManager = NSFileManager.defaultManager()
        
        do {
            let applicationSupportDirectory =
                try fileManager.URLForDirectory(
                    NSSearchPathDirectory.ApplicationSupportDirectory,
                    inDomain: NSSearchPathDomainMask.UserDomainMask,
                    appropriateForURL: nil,
                    create: true).path!
                
            let appName = NSProcessInfo.processInfo().processName
            let dirUrl = NSURL(fileURLWithPath: applicationSupportDirectory, isDirectory: true).URLByAppendingPathComponent(appName)
            return dirUrl.URLByAppendingPathComponent("Logfiles").path

        } catch let error as NSError {
        
            let message: String = "Could not get application support directory, error = " + (error.localizedDescription ?? "Unknown reason")
            return nil
        }

    }()

Or without all the verbose stuff:

        do {
            
            try NSFileManager.defaultManager()...some file manager call that throws an error
                
            ...

        } catch let error as NSError {
        
            print "Could not ... " + (error.localizedDescription ?? "Unknown reason")

        }

Happy coding...

Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH

We don't get the world we wish for... we get the world we pay for.

2015-09-16

Code sample: A runloop observer in Swift.

The problem: I have a text file that must conform to certain syntax rules. The user can edit the file, and the GUI should show if the lexical structure is correct. If not, it should show where the syntax rules are violated and show some information as to which rule is violated. (Very similar to spelling errors in text editing GUI's)

After each editing operation (delete char, insert char, etc) the entire file must be checked again for a correct syntax. Of course when a file gets really large, these checks can take a while. This delay should not impact the user while he is typing.

The solution is to do the syntax check in its own thread, not in the main runloop that processes the GUI. But the results of these checks must be fed back into the GUI processing thread (main runloop). Apple has created runloop observers for such situations.

Once the main runloop has processed all events for the current go-around it is put to sleep (or made waiting) until new events arrive. It is possible to start a process of our own when this happens. That process is called a runloop observer and forms part of the main runloop. As such, the runloop observer can safely update the GUI.

Ok, so far the theory, here is the code:

    private var editCount: Int = 0
    private let parseQueue = dispatch_get_global_queue(DISPATCH_QUEUE_PRIORITY_LOW, 0)
    private var parseResults: Array<Parser.Result> = []

    func lexicalStructureChanged() {
        
        // When the lexical structure changes, the parser must process the entire document again.
        // Since parsing large documents can take some time, parsing is done in a separate thread.
        // This thread is executed asynchronously on the parseQueue. It communicates the results back via the parseResults array.
        // A runloop observer is added to the current runloop to check for the availability of results. That observer will keep observing until a result is found and then dismiss itself.
        // Note that the observer will not try to update a document with a different "editCount" as the editCount the parser was started with. This ensures that old results are discarded.
        // The editCount will always be >0, but the parser editCount may be 0 if there was no error, and thus no action to be taken.
        
        let runLoopObserver = CFRunLoopObserverCreateWithHandler(kCFAllocatorDefault, CFRunLoopActivity.BeforeWaiting.rawValue, Boolean(1), 0, {
            
            (observer: CFRunLoopObserver!, activity: CFRunLoopActivity) -> Void in // This will keep a reference to self alive until all parse results are processed
            
            // Check if a result is available
            
            if self.parseResults.isEmpty { return }
            
            
            // There is a result, this observer can be detached
            
            CFRunLoopRemoveObserver(CFRunLoopGetCurrent(), observer, kCFRunLoopCommonModes)
            
            
            // Process the result
            
            self.processParseResults()
            
        } )
        
        CFRunLoopAddObserver(CFRunLoopGetCurrent(), runLoopObserver, kCFRunLoopCommonModes)
        
        editCount++

        dispatch_async(parseQueue, { // This will keep a reference to self alive until the parser is done
            self.parseResults.append(Parser.parse(self.lines, editCount: self.editCount))
        } )
    }

    private func processParseResults() {

        let (count, lineIndex, elementIndex, message) = parseResults.removeAtIndex(0)

        if count < editCount { return }
        ...
    }

The first function lexicalStructureChanged is called for every lexical change to the document.

The second function is only shown to illustrate that the oldest entry in the parseResults array is removed when the runloop observer is called.

The runloop is first created (let runLoopObserver =) then added (CFRunLoopAddObserver) and after the runloop observer is in place, the parser is started as an asynchronous process using GCD (dispatch_async).

The parser does nothing with the editCount parameter except returning exactly the same value as it was started with. Thus allowing the process processParseResults to ignore results that were returned for older edits of the document. This will probably only happen when the user types very fast and the document is sufficiently large. In my tests so far the parsing was always complete in less than a millisecond, thus the updates to the GUI were faster than my typing!

Note that both code blocks (closures) retain "self". This ensures that Self is available when the parseResults and the runloop observer are called. The "weak" or "unowned" modifiers should not be used. Otherwise a runtime error could occur when the user closes the document before the parser is ready.

PS: Make sure that you do not update the GUI from a process in the parseQueue, you will get very strange error messages from CoreAnimation that way. If there is a need to update the GUI from that thread use:

                dispatch_async(dispatch_get_main_queue(), { guiUpdate() })

(and yes, you could do all GUI updating that way, but it gets very difficult to know which update is still applicable and which is not)

Happy coding...

Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH

We don't get the world we wish for... we get the world we pay for.

2015-09-15

Swift Gotcha: Unexpected observer behaviour for inout arguments

I recently ran into an observer problem: it was being called without updates to the associated variable.

Since I was using the associated variable as an inout argument, that was my first suspect. So I wrote a little test in playground to investigate this:

So, ... indeed. The observer of 'str' is called even though the property is not updated in "testDidSetObserver". Note that even when the if statement is removed from the function the observer is still called. It seems that using a property in an inout argument will always trigger the property observers after the function is finished.

I also ran a test that prints the value of "countDidSet" inside the function, and the count is 0 inside the function. Thus the observer is indeed triggered after the function finishes, whether the property is accessed or not.

So observers and inout arguments don't mix well.

PS: I also ran the test for the willSet observer, same results.

Note: As of Swift 2.2 this behaviour is now documented in the manual with a little explanation as to why.

Happy coding...

Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH

We don't get the world we wish for... we get the world we pay for.

2015-09-09

Adding tooltips to changing NSView content

In the App I am currently working on I have an array of strings in an NSView and some of these lines may have one or more errors in them. The error's are red underlined, but I needed a way to inform the user about the nature of the error. Tooltips are the best way imo, the user hovers the mouse over the underlined word, pauses a little, and up pops the error reason.
Next the user may edit the line and remove the cause of the error, the line is displayed again without the underlining, and of course the tooltip must disappear also.

So how to add and remove those tooltips?

It turns out that each NSView already the ToolTip management build in. We only have to create tooltips and add them to the view when we need them. And of course remove them when no longer needed.

This does mean that we need way to associate the tooltip with error no N in line X, and when that error disappears, the tooltip must be removed.

I found the easiest solution for me was to add the tooltip to the data model. Yes, Ugh.. I can hear you..

But the alternative is to build a paralel data structure in my subclassed NSView to (the lines in) the data model and somehow keep those two synchronised. A double Ugh..

So I added the tooltip to the data model.

In objective-C the tooltip can consist of an NSString, in Swift that did not work for me, probably because String is bridged to NSString, but the tooltip needs to inherit from NSObject. Or maybe there is another reason. Anyhow this issue is moot as I needed to track the tooltips and thus had to create a class that encompasses the tooltip:

    class ToolTip: NSObject {
        
        var tip: String
        
        var tag: NSToolTipTag?
        
        init(tip: String) {
            self.tip = tip
            super.init()
        }
        
        override func view(view: NSView,
            stringForToolTip tag: NSToolTipTag,
            point: NSPoint,
            userData data: UnsafeMutablePointer<Void>) -> String {
                return self.tip
        }
    }
    

    typealias ToolTips = Array<ToolTip>

In each line in the datamodel I added some storage for the tooltips

    // Note: Never ever handle the tooltips from within the data model.
    // The tooltips are managed by the NSView that displays this line.
    

    var toolTips: ToolTips = ToolTips()

In MyDocumentView (that is subclassed from NSView) I added the following code to the code that draws a single line. Note that drawLine is called from drawRect.

    /// Draws the line for the given index.
    
    private func drawLine(index: Int) {
        
        // Note: the index can refer to a non-existing line!
        
        // Strategy:
        // 1. Determine the background color.
        // 2. Draw the backgroud color.
        // 3. Add the text.
        // 4. ...
        // 5. ...
        // 6. Add error marker and tooltip.
        // 7. ...
        
        
        let context = NSGraphicsContext.currentContext()!.CGContext
        
        
        // 1. Set the background color
        
        if ((index % 2) == 0) {
            CGContextSetFillColorWithColor(context, normalBackgroundColor)
        } else {
            CGContextSetFillColorWithColor(context, alternateBackgroundColor)
        }
        
        
        // Create the rectangle for the background color
        
        let backgroundRect = rectForLine(index)
        
        
        // 2. Draw the background
        
        CGContextBeginPath(context)
        CGContextFillRect(context, backgroundRect)
        CGContextDrawPath(context, kCGPathFill)
        
        
        // Draw the text, insertion point or selection only for existing lines
        
        if index < document.lines.count {

            
            // 3. Add the text
            
            let xOffset = MARGIN_BEFORE_LINE
            let yOffset = lineHeight * CGFloat(index) - MARGIN_BELOW_LINE
            (document.lines[index].string as NSString).drawAtPoint(CGPointMake(xOffset, yOffset), withAttributes: document.fontDict)
            
            
            // 4...
            
            
            // 5...
            
            
            // 6. Add error markers and tooltips.
            
            // Remove old tooltips associated with this line
            
            for toolTip in document.lines[index].toolTips {
                self.removeToolTip(toolTip.tag!)
            }
            document.lines[index].toolTips.removeAll(keepCapacity: true)

            for err in document.lines[index].errorData {
                
                let y = lineHeight * CGFloat(index) - MARGIN_BELOW_LINE
                
                let x = document.lines[index].offsetForIndex(err.range.startIndex)
                        + MARGIN_BEFORE_LINE
                
                let width = (document.lines[index].offsetForIndex(err.range.endIndex)
                            - document.lines[index].offsetForIndex(err.range.startIndex))
                
                
                // Do the red underlining
                
                CGContextSetAllowsAntialiasing(context, false); // Ensure hairline drawing
                CGContextSetRGBStrokeColor(context, 1.0, 0.0, 0.0, 1.0)
                CGContextSetLineWidth(context, 1.0)
                CGContextMoveToPoint(context, x, y + lineHeight - 1.0)
                CGContextAddLineToPoint(context, x + width, y + lineHeight - 1.0)
                CGContextDrawPath(context, kCGPathStroke)
                CGContextSetAllowsAntialiasing(context, true) // Disable hairlines
            
            
                // Add the new tooltip
            
                let toolTip = ToolTip(tip: err.reason)
                let toolTipRect = NSRect(x: x, y: y, width: width, height: lineHeight)
                toolTip.tag = addToolTipRect(toolTipRect, owner: toolTip, userData: nil)
                document.lines[index].toolTips.append(toolTip)
            }
        }
        
        
        // 7...

    }

Obviously this is not the complete code to draw the line, but everything related to the tooltips is in here.
Of note is the following: Before drawing the red underlining and registering the tooltips, the old tooltips for the line are removed from the view and the data model. Then new tooltips are created and added to the view and the datamodel. The data model only stores the tooltips in order to be able to remove them later. This way, when the user edits the line we can be sure that any old error related tooltips are gone from the view. The user thus gets the feedback that the error was corrected.
The other thing to note is that the tag value generated by the view is later used to remove the tooltip from the view.
Oh, one last thing: this view uses the flipped Y coordinates: override var flipped: Bool { get { return true }}

Happy coding...

Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH

We don't get the world we wish for... we get the world we pay for.

2015-09-03

Building a popup NSMenu structure programatically in Swift

OK, so the title is a little misleading, while this example does show how to build a popup menu structure programatically I do use the interface builder to associate a menu with the view.

The later can be done in the interface builder by opening up the xib file with the view and adding a NSMenu to the xib file. It will have three menu items by default, I removed two and renamed the only remaining menu item "popup menu for the view" simply as a reminder to myself to know why the menu is in there.

This popup menu needs a name that is unique for the view, I called mine "Popup":


Next, connect the menu to the view by assigning the delegate and outlet (via drag, drop, select):


Now the popup menu is connected to the view and the view is set as the delegate we can override validateMenuItem,  menuNeedsUpdate and menuDidClose of NSView to handle menu operations.

In order to build the popup menu hierarchy we need menuNeedsUpdate.

In the view implement this method like this:


func menuNeedsUpdate(menu: NSMenu) {
    
    switch menu.title {
        
    case "Popup":
        
        // Check if the menu is already correctly initialized
        
        if menu.itemArray.count == 1 { // When 1, there is the item that was defined in the XIB
            
            
            // Get rid of the item that is defined in the XIB
            
            menu.removeAllItems()
            
            
            // Add the default "remove" item
            
            menu.addItemWithTitle("Remove", action: "removeItem:", keyEquivalent: "")
            
            
            // Add a submenu to add items
            
            let addMenu = NSMenu(title: "Add")
            addMenu.delegate = self
            let addMenuItem = NSMenuItem(title: "Add Item ...", action: nil, keyEquivalent: "")
            
            menu.addItem(addMenuItem)
            menu.setSubmenu(addMenu, forItem: addMenuItem)
            
            
            // Add a submenu to convert items
            
            let convertMenu = NSMenu(title: "Convert")
            convertMenu.delegate = self
            let convertMenuItem = NSMenuItem(title: "Convert Item ...", action: nil, keyEquivalent: "")
            
            menu.addItem(convertMenuItem)
            menu.setSubmenu(convertMenu, forItem: convertMenuItem)
            
        }
        
        
    case "Add":
        
        // Build this menu if there are no items yet
        
        if menu.itemArray.count == 0 {
            
            menu.addItemWithTitle("Null", action: "addNull:", keyEquivalent: "")
            menu.addItemWithTitle("Bool", action: "addBool:", keyEquivalent: "")
            menu.addItemWithTitle("Number", action: "addNumber:", keyEquivalent: "")
        }
        
        
    case "Convert":
      
        // This is done dynamically, so get rid of the existing items    

        menu.removeAllItems()


        // Now it depends on what exactly is in this line.
            
        if let object = document.determineObject() {
                
            switch object {
                    
            case .NULL:
                menu.addItemWithTitle("To Bool", action: "convertToBool:", keyEquivalent: "")
                menu.addItemWithTitle("To Number", action: "convertToNumber:", keyEquivalent: "")
                
            case .BOOL:
                menu.addItemWithTitle("To Null", action: "convertToNull:", keyEquivalent: "")
                menu.addItemWithTitle("To Number", action: "convertToNumber:", keyEquivalent: "")
                
            }
        }

        
    default: break
    }
}            

This produces the popup menu:


The example probably speaks for itself, but if not:
  • The switch statement ensures that each case only processes the menu that is assigned to it.
  • In each case it is determined first if the menu is already build or if it has to be build. Note that in the case of the "Convert" menu, the menu is created anew each time since it must be dynamically populated.
  • The first time around, only the Popup menu exists, the other two (sub) menus are created when the Popup menu is selected for the first time.
  • When creating the Add and Convert (sub) menus, the delegate must be set. Setting the delegate ensures that the func menuNeedsUpdate(menu: NSMenu) is called when the user selects it.
  • In the example above, the "Convert" menu item should be invalidated in the func validateMenuItem(menu: NSMenu) when there is no valid object. Otherwise the user may think that the convert menu is available even when it is not.

Happy coding...

Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH

We don't get the world we wish for... we get the world we pay for.

2015-08-04

Opening a file in the browser from an OSX-Application

After the holidays lets start off with something simple: opening a file from your application in a browser. For iOS this can simply be done through the sharedApplication.openURL, but this call is not available under OS-X.

In OS-X it has to be done through the NSWorkspace. Otherwise it is almost identical to the method used for iOS:

NSWorkspace.shared().openFile(fileLogPath, withApplication: "Safari")

Note that this is not the same as in iOS. Here we have to specify explicitly in which application the file has to be openend. Where in iOS we can simply request to open a URL. It is possible to skip the "withApplication:" parameter in OS-X as well, but then the default app will be used for the extension of the file. Which can be irksome if for example the file in question is an XML file that we want to show in the browser, but would open in an XML editor (like XCode). Also keep in mind tat the user might want a different default browser than Safari, hence it would make sense to keep the "withApplication:" parameter configurable.

If we only want to open an URL in a browser, the syntax can in fact be a little easier:

        let url = URL(string: "http://www.balancingrock.nl")!
        NSWorkspace.shared().open(helpUrl)


Happy coding...

Did this help?, then please help out a small independent.
If you decide that you want to make a small donation, you can do so by clicking this
link: a cup of coffee ($2) or use the popup on the right hand side for different amounts.
Payments will be processed by PayPal, receiver will be sales at balancingrock dot nl
Bitcoins will be gladly accepted at: 1GacSREBxPy1yskLMc9de2nofNv2SNdwqH

We don't get the world we wish for... we get the world we pay for.