Corbin's Treehouse - Corbin Dunn, Santa Cruz, CA
Plug Bug
Treehouse
Photography
Videos
Projects
Unicycling
About

Archive for the 'Cocoa' Category

Tricks for fast Bluetooth LE data transfers


I’ve been using the Adafruit LE friend to do some data transfer tests. With the basic setup, I was sending 1 kB of data in about 9.5 to 10 seconds. That is horribly slow! About 100 bytes per second. 

I did some research and found a couple of good links. This link about BLE discusses using the Nordic nRF chip and mbed to send data to a peripheral (i.e.: from the computer to the device). This link about sending data from the peripheral has some good tips when going in the other direction.  

I gave up trying to get the Adafruit LE friend to be fast and started working with the RedBearLab BLE nano (v1.5). It has been tough getting Xcode setup to compile code right, and  I ended up using “embedXcode” as a starting point, and the Arduino plugin for RedBearLab. I’d rather just use mbed, but the web experience is terrible, and I must use Xcode. However, I couldn’t get their scripts to compile..mainly do to case sensitivity issues (BLE.h and ble.h both exist, and my drive partition is insensitive). 

For the Nano, the bottom line was I had to edit the projectconfig.h file and use these settings:

    #define CFG_GAP_CONNECTION_MIN_INTERVAL_MS           20// corbin!! fYI, 40 worked. 50                     /**< Minimum acceptable connection interval */

    #define CFG_GAP_CONNECTION_MAX_INTERVAL_MS          40// corbin!! 500                     /**< Maximum acceptable connection interval */

    #define CFG_GAP_CONNECTION_SUPERVISION_TIMEOUT_MS  500// corbin!! 4000                     /**< Connection supervisory timeout */

The key part was not just the min interval, but the max; 500 was too large, and there is no API to control it (I can add some in for myself). 40 is required. I didn’t test lower…but lower might be better too. Too low, and the Mac (or iOS device) might not be able to keep up.

 When sending from an OS X app, I discovered I could get the most data by doing a “write without response”:

            peripheral.writeValue(subData, forCharacteristic: _uartTransmitCharacteristic!, type: CBCharacteristicWriteType.WithoutResponse)

 And batching the 20 byte data chunks every 2 nano seconds:

            let delay: Int64 = Int64(NSEC_PER_MSEC)*2// At about 4ns, it starts to get slower on the recieving end.

            dispatch_after(dispatch_time(DISPATCH_TIME_NOW, delay), dispatch_get_main_queue(), { () -> Voidin

                self.sendMoreData();

            })

 Anything longer and it would be slower. This doesn’t make sense…as the BLE stack can’t really be faster than 7.5ms, but I’m sure the computer is doing a bit of processing and handling the BLE stack separately from my process. I have yet to test the code on iOS…it might need a larger delay, and might be slower. That is fine… my goal is to send files from the desktop to the device, and potentially vice-versa. 

With my above changes, a raw read of 1024 bytes (1kB) took 185ms; and it was fairly consistent. That’s about 5.4kB per second! I could probably improve it 6x more by using multiple characteristics to write at simultaneously, but I’ll test that later. 5.4kB/second is fast enough to start with.

 

 

I was curious if the Adafruit Bluefruit LE friend could be made faster. I know the GAP interval can be changed, so I made it match with:

        m_ble.sendCommandCheckOK(“AT+GAPINTERVALS=20,40,25,30″);

 
Indeed, this did help! the UART service can read 1024 bytes in 3.5 seconds, instead of 10 seconds, or about 0.3kB/second versus 0.1kB/second.  This is reading the data from a teensy, which means the Adafruit BLE friend first reads the data, and then transfers it to the teensy over SPI. That protocol is somewhat slow, and it has a 16 byte payload (it really needs to be 20 bytes to transfer complete packets from the BLE). 
 
Of course…my test is ignoring the SPI transfer from the nRF chip to the teensy chip. This is also going to take some time, and might slow my numbers down a bit. I also need to learn how to write SPI code and make my own mechanism for transferring data. Lots more coding to do…

WWDC 2015: Improving the Full Screen Window Experience


For the past 10 years I’ve given a talk at every Apple World Wild Developer Conference (WWDC). Well, except for one. That was 2007 when I was busy working on UIKit for the iPhone 1.0.

Apples is now posting our videos online without requiring an Apple developer account. So, everyone can check out my video for this year’s session #221 on their site:

Screen Shot 2015 06 12 at 7 18 41 PM

 I talk about how to implement Full Screen in Cocoa desktop applications, including some new tiling features in OS 10.11. Most the full screen information applies to all OS X versions that support full screen (I think 10.7 and higher). I also discuss Title Bar Accessory View Controllers — this feature requires 10.10 or higher. During the second half my colleague Taylor Kelly comes up and discusses how to do some cool dynamic layouts.

We also have a demo app showing off the features: Exhibition – An adaptive OS X App


NSTableView Tips: Doing Animations with Core Data


For my “Cyr Wheel Pattern Editor” app I am using CoreData and an NSTableView. However, I’m sort of “manually” doing bindings for the array content itself to get animations in the View Based NSTableView.

Here’s what my model looks like:

Screen Shot 2014-04-29 at 9.09.53 PM.png

The CDPatternSequence has a children array of CDPatternItems:

@interface CDPatternSequence : NSManagedObject

@property (nonatomic, retain) NSOrderedSet *children;

@end

Now, if you have Xcode generate the standard code you might have noticed the standard NSOrderedSet accessors aren’t implemented for you; this is a bug in CoreData, but here is a good post on stack overflow about it.

The minimal implementation I needed was this (note: CDPatternChildrenKey == @”children”):

- (void)removeChildrenAtIndexes:(NSIndexSet *)indexes {
    [self willChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:CDPatternChildrenKey];
    NSMutableOrderedSet *tmpOrderedSet = 
       [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:CDPatternChildrenKey]];
    [tmpOrderedSet removeObjectsAtIndexes:indexes];
    [self setPrimitiveValue:tmpOrderedSet forKey:CDPatternChildrenKey];
    [self didChange:NSKeyValueChangeRemoval valuesAtIndexes:indexes forKey:CDPatternChildrenKey];
}

- (void)insertObject:(CDPatternItem *)value inChildrenAtIndex:(NSUInteger)idx {
    NSIndexSet* indexes = [NSIndexSet indexSetWithIndex:idx];
    [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:CDPatternChildrenKey];
    NSMutableOrderedSet *tmpOrderedSet = 
      [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:CDPatternChildrenKey]];
    [tmpOrderedSet insertObject:value atIndex:idx];
    [self setPrimitiveValue:tmpOrderedSet forKey:CDPatternChildrenKey];
    [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:CDPatternChildrenKey];
}

- (void)insertChildren:(NSArray *)values atIndexes:(NSIndexSet *)indexes {
    [self willChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:CDPatternChildrenKey];
    NSMutableOrderedSet *tmpOrderedSet = 
       [NSMutableOrderedSet orderedSetWithOrderedSet:[self mutableOrderedSetValueForKey:CDPatternChildrenKey]];
    [tmpOrderedSet insertObjects:values atIndexes:indexes];
    [self setPrimitiveValue:tmpOrderedSet forKey:CDPatternChildrenKey];
    [self didChange:NSKeyValueChangeInsertion valuesAtIndexes:indexes forKey:CDPatternChildrenKey];
}

The main reason to do this is to get the proper KVO notifications to know what is happening. My NSWindowController subclass is the delegate and datasource for my tableview. Each row is managed by an NSViewController, and as I previously I simply have an array of NSViewControllers that starts out with NSNull placeholders and lazily load them when the table requests them:

- (void)_resetPatternViewControllers {
    _patternViewControllers = NSMutableArray.new;
    for (NSInteger i = 0; i < self.document.patternSequence.children.count; i++) {
        [_patternViewControllers addObject:[NSNull null]]; // placeholder
    }
    [_tableView reloadData];
}

- (NSInteger)numberOfRowsInTableView:(NSTableView *)tableView {
    return _patternViewControllers.count;
}

- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn 
                                                              row:(NSInteger)row {
    CDPatternItemViewController *vc = [self _patternViewControllerAtIndex:row];
    // don't set the identifier so it isn't reused...
    return vc.view;
}

- (CDPatternItemViewController *)_patternViewControllerAtIndex:(NSInteger)index {
    id currentObject = [_patternViewControllers objectAtIndex:index];
    CDPatternItemViewController *result;
    if (currentObject == [NSNull null]) {
        CDPatternItem *item = [self.document.patternSequence.children objectAtIndex:index];
        result = [CDPatternItemViewController new];
        result.patternItem = item;
        [_patternViewControllers replaceObjectAtIndex:index withObject:result];
    } else {
        result = (CDPatternItemViewController *)currentObject;
    }
    return result;
}

In my CDPatternItemViewController XIB, I bind stuff to the “patternItem”, which is my CoreData model object.

So, here’s the fun part. I simply observe the children of my container model object and do updates to the table based on that. It makes it *really easy* to simply modify your model and have the table automatically reflect the changes you made. Again, I have a window controller subclass, so I do it here:

- (void)windowDidLoad {
    [super windowDidLoad];

    [self _resetPatternViewControllers]; // This is shown above

    // Watch for changes
    [self.document.patternSequence.children addObserver:self 
              forKeyPath:CDPatternChildrenKey options:0 context:nil];

    // Drag and drop registration below… (snipped out)
}

Then, all the real work is done in the KVO callback.

- (void)_observeValueForChildrenChangeOfObject:(id)object change:(NSDictionary *)change
                                       context:(void *)context {
    NSKeyValueChange changeKind = [[change objectForKey:NSKeyValueChangeKindKey] integerValue];
    switch (changeKind) {
        case NSKeyValueChangeSetting: {
            [self _resetPatternViewControllers];
            break;
        }
        case NSKeyValueChangeInsertion: {
//            if (_draggedRowIndexes != nil) break; // Done manually in the drag operation
            NSIndexSet *indexes = [change objectForKey:NSKeyValueChangeIndexesKey];
            if (indexes) {
                NSMutableArray *emptyObjects = NSMutableArray.new;
                for (NSInteger i = 0; i < indexes.count; i++) {
                    [emptyObjects addObject:[NSNull null]];
                }
                [_patternViewControllers insertObjects:emptyObjects atIndexes:indexes];
//                [NSAnimationContext beginGrouping];
//                [NSAnimationContext.currentContext setDuration:.3];
                [_tableView insertRowsAtIndexes:indexes withAnimation:NSTableViewAnimationEffectFade];
//                [NSAnimationContext endGrouping];
            } else {
                [self _resetPatternViewControllers];
            }
            break;
        }
        case NSKeyValueChangeRemoval: {
//            if (_draggedRowIndexes != nil) break; // Done manually in the drag operation
            NSIndexSet *indexes = [change objectForKey:NSKeyValueChangeIndexesKey];
            if (indexes) {
                [_patternViewControllers removeObjectsAtIndexes:indexes];
                
                [_tableView removeRowsAtIndexes:indexes withAnimation:NSTableViewAnimationEffectFade];
            } else {
                [self _resetPatternViewControllers];
            }
            break;
        }
        case NSKeyValueChangeReplacement: {
            NSIndexSet *indexes = [change objectForKey:NSKeyValueChangeIndexesKey];
            if (indexes) {
                [_patternViewControllers removeObjectsAtIndexes:indexes];
                // replace w/null
                NSMutableArray *emptyObjects = NSMutableArray.new;
                for (NSInteger i = 0; i < indexes.count; i++) {
                    [emptyObjects addObject:[NSNull null]];
                }
                [_patternViewControllers insertObjects:emptyObjects atIndexes:indexes];
                [_tableView reloadDataForRowIndexes:indexes columnIndexes:[NSIndexSet indexSetWithIndex:0]];
            } else {
                [self _resetPatternViewControllers];
            }
            break;
        }
        default: {
            NSAssert(NO, @"internal error: change not known");
            break;
        }
    }
}


- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object 
                        change:(NSDictionary *)change context:(void *)context {
    if ([keyPath isEqualToString:CDPatternChildrenKey]) {
        [self _observeValueForChildrenChangeOfObject:object change:change context:context];
    } else {
        NSAssert(NO, @"bad observation");
    }

}

The code above keeps the view controller array in sync with the model, and also keeps the table in sync with the model.

You can see I have some comments about drag and drop. I can write a post on how to do that, if people are interested.


NSTableView Tips: View Controller per row


Sometimes you may find your application has rather complex rows, and you want an NSViewController to manage each row in a “View Based” NSTableView. This is fairly easy to do, and I did it in my Cyr Wheel pattern editing application. My “Cell View” that I want for each row was designed in Xcode like:

Screen Shot 2014-04-26 at 8.52.47 PM.png

A simple NSViewController subclass:

@interface CDPatternItemViewController : NSViewController
   // the thing we are editing
   @property(weak) CDPatternItem *patternItem;
@end

The controller code is what is interesting. I use an NSWindowController subclass, and keep an array of the view controllers:

@interface CDPatternEditorWindowController () {
@private
  NSMutableArray *_patternViewControllers;


I decided to lazily load the view controllers. On initialization/load of my window I fill the array with NULL:

- (void)_resetPatternViewControllers {
  _patternViewControllers = NSMutableArray.new;
  for (NSInteger i = 0; i < self._patternSequence.children.count; i++) {
    [_patternViewControllers addObject:[NSNull null]]; // placeholder
  }
  [_tableView reloadData];
}



_patternSequence is simply a wrapper around my model object, stored in my Core Data backed NSDocument; it has an array of things I want to show in the table

- (CDPatternSequence *)_patternSequence {
   return self.document.patternSequence;
}


Then, the view based tableview method that requests the view simply creates and caches it:


- (CDPatternItemViewController *)_patternViewControllerAtIndex:(NSInteger)index {
    id currentObject = [_patternViewControllers objectAtIndex:index];
    CDPatternItemViewController *result;
    if (currentObject == [NSNull null]) {
        CDPatternItem *item = [self._patternSequence.children objectAtIndex:index];
        result = [CDPatternItemViewController new];
        result.patternItem = item;
        [_patternViewControllers replaceObjectAtIndex:index withObject:result];
    } else {
        result = (CDPatternItemViewController *)currentObject;
    }
    return result;
}

- (NSView *)tableView:(NSTableView *)tableView viewForTableColumn:(NSTableColumn *)tableColumn row:(NSInteger)row {
    CDPatternItemViewController *vc = [self _patternViewControllerAtIndex:row];
    // don't set the identifier so it isn't reused...
    return vc.view;
}



This should be fairly straightforward, and easy to read. But if you have questions let me know and I will clarify.

I don't cleanup the view controllers when the items are removed from the table. I could use the NSTableView delegate method tableView:didRemoveRowView:forRow: to drop my NSViewController cache at this time if I so wanted. However, my table is small..and I don't bother with this.



NSTableView Tips: Not delaying the first responder


I have a little home-brew Cocoa app for making Cyr Wheel patterns. The UI is built with an NSTableView and looks like this:

Screen Shot 2014-04-26 at 7.31.00 PM.png

Now normally when you try to directly click on one of the NSTextFields the table will first select the row. Then a second click will allow you to start editing. For most tables, this is the behavior you want. For something like what I have, I want to avoid this, and allow the first responder to go through. This can easily be done by subclassing NSTableView and overriding:

- (BOOL)validateProposedFirstResponder:(NSResponder *)responder forEvent:(NSEvent *)event {
   return YES; // no delay
}

NSTableView’s default logic is a bit complex that allows things to go through or not based on several properties. This method is declared in NSResponder.h, and it is sent up the responder chain when the text field itself attempts to become first responder. When NSTableView gets it, it does some determination to determine if it should go through or not. If it wasn’t an already selected row, it returns NO; this causes the table to get the mouseDown: and do the selection logic. br/>I don’t care what control was clicked; I want it to go through without selecting, and always return YES.


Implementing delete in an NSTableView


I’ve seen many ways to implement delete in an NSTableView. Many are good. Many hardcode references to something they shouldn’t, and those are bad.

Here’s an easy way:

Subclass NSTableView and override keyDown:


- (void)keyDown:(NSEvent *)theEvent {
    // handle delete
    unichar key = [[theEvent charactersIgnoringModifiers] characterAtIndex:0];
    if (key == NSDeleteCharacter && self.selectedRow != -1) {
        [NSApp sendAction:@selector(delete:) to:nil from:self];
    } else {
      [super keyDown:theEvent];
  }
}

This just lets the responder chain take care of it. Your window controller can easily handle it by just implementing delete:

- (IBAction)delete:(id)sender {
    if (self.tableView.selectedRowIndexes.count > 0) {
        [_tableView beginUpdates];
        [[self _patternSequence] removeChildrenAtIndexes:self.tableView.selectedRowIndexes];
        [_tableView endUpdates];
    }
}

EDIT: I probably should only call super keyDown if it wasn’t the delete key…otherwise we beep.


Why the API? NSTableView -preparedCellAtColumn:row:


I think I’ll do a few articles on why certain API was introduced in Leopard.

I’ll start with one of the new methods in NSTableView:

/* Returns the fully prepared cell that the view will normally use for
 drawing or any processing. The value for the cell will be correctly set, 
 and the delegate method 'willDisplayCell:' will have be called. You can 
 override this method to do any additional setting up of the cell that is 
 required, or call it to retrieve a cell that will have its contents properly 
 set for the particular column and row.
*/
- (NSCell *)preparedCellAtColumn:(NSInteger)column row:(NSInteger)row;

What is this method and what is it useful for? There are many reasons why it is useful, and they all are based on this fact: *all* of NSTableView’s operations that involve a cell at a particular row/column will be filtered through this method. Previously, NSTableView would have various internal methods and ways of obtaining the cell and there was no single spot where everything filtered through. Now there is.

This means:

1. You can acquire a “fully prepared” cell that is ready to draw — either in the NSTableView itself, or somewhere else. Somewhere else? Yes — an example where AppKit does this is for expansion tool tips, which draw in a separate tool tip window and in a view that is not actually an NSTableView. You might also want to access the “fully prepared” cell to generate a drag image when you override:

- (NSImage *)dragImageForRowsWithIndexes:(NSIndexSet *)dragRows 
	tableColumns:(NSArray *)tableColumns 
	event:(NSEvent*)dragEvent 
	offset:(NSPointPointer)dragImageOffset;

2. You can override the method to modify the cell. Think about this in a simple way, and it means you could easily bold odd rows without having to use -willDisplayCell:. Depending on how your code is setup, it might make more sense to do this work in the view, as opposed to the controller (via the delegate).

3. You can return an entirely different cell. Let’s say you want to have one cell that doesn’t change for one particular row; well, you can just always return that same cell, and it will always be used. You might want to do this if that particular cell has some sort of important state (such as an animation), which you don’t want to have to keep resetting or managing. Another example is for showing highlighting when you mouse over the cell.

Take this example from WWDC that I wrote a few years ago: PhotoSearch. Look at TrackableOutlineView.m, and you’ll see that the cell which is being tracked (ie: mouse is over it), is always returned from preparedCellAtColumn:row:. This was important from an abstraction perspective; the cell will get a single -mouseEntered: from the tracking area, and the same cell will eventually get a -mouseExited: — this allows the cell to set and maintain its own state, without forcing the delegate or NSTableView to save off the state and always reset it at draw time.

Why does this demo copy the cell, and not just use the single cell that is shared from the [tableColumn dataCell]? I mean, if the cell gets a mouseEntered:, and the tableView redraws *just* that cell, then things will work out fine, right? Yes — but if something else triggers any other row to redraw, then your tracked cell will be used, and it currently has the wrong state in it. That would be bad, and it would draw the wrong thing.

4. Another reason this method is useful is for type selection. When you type select, and NSTableView is attempting to find a row, it can acquire a fully prepared cell (including setting the stringValue, calling -willDisplayCell on the delegate, and setting up bindings). The stringValue of the cell can then be compared for type-selection to find a match. But, you wonder, isn’t that doing too much work for just matching on the string? Yes — for any decent sized table, you should implement this delegate method to directly return information from your model:

- (NSString *)tableView:(NSTableView *)tableView 
	typeSelectStringForTableColumn:(NSTableColumn *)tableColumn 
	row:(NSInteger)row;

5. Finally, another reason for the API; there is no easy way to know what the value of a cell will be if you are using bindings. Now, you can find out by using this method, since it will fill the cell’s content with bound values (if there are any).

Anyways, my first entry of “Why the API”. More to follow…hopefully.


Cocoa programmers: avoid writing to the user defaults when you don’t need to


[Edit: ecto ate this post, so I’m typing it in again!]

I discovered that a lot of applications will unnecessarily write to NSUserDefaults. This causes your app to hit the disk when it shouldn’t, and is a slight performance penalty. AppKit is also susceptible to this problem; if you hit cmd-O to bring up the open panel in any application, you will see it writing things to the user defaults, when it probably doesn’t need to do so. I’m working on fixing that, and you should to!

So, how do you do it? It is easy — just add a breakpoint on -[NSUserDefaults(NSUserDefaults) setObject:forKey:]. You can do this with gdb:

b -[NSUserDefaults(NSUserDefaults) setObject:forKey:]

Or you can use the breakpoints window in Xcode (my preferred way):

Picture4 - breaking on user defaults.png

Then, reproduce whatever action might cause it to happen (ie: starting your application, or in my test case, cmd-o to bring up the open panel). Look at the backtrace in Xcode and figure out why you are doing too much work:

Picture5 - the callstack for a user default.png


Getting rid of the undo warning in Xcode after saving


One of the most annoying dialogs in Xcode is the undo warning dialog you get when attempting to undo after a save. I do this all the time, and I hate the warning. Luckily, there is a user default to turn it off:

defaults write com.apple.Xcode XCShowUndoPastSaveWarning NO


Wake up and smell the cocoa




(c) 2008-2015 Corbin Dunn

Corbin's Treehouse is powered by WordPress. Made on a Mac.

Subscribe to RSS feeds for entries and comments.

19 queries. 0.209 seconds.