Useful “Finder” methods on NSManagedObject in Swift

I don’t know about you, but CoreData seemed insane to me before I discovered MagicalRecord, back in the days of Mogenerator and Objective-C.

But since Swift 3 has come out, and the tools have improved to support Swift development (remember poor compiler warnings, if not just a “segmentation fault 11” error), I’m finding that I like to work with the Xcode tools again, and forego these old approaches.

My old way of doing things worked very well, and in some ways I miss some aspects of that, but ultimately I quickly (swiftly… cough cough) became a lover of Swift and simply prefer developing in that language.

What I miss most were the “MagicalFinders” categories present in MagicalRecord.  I found quite a concise way to do that however in Swift, and the code looks like this:

import Foundation
import CoreData

@objc public protocol CoreDataFinders {

    /// Because we are doing fetch requests on a data model,
    /// Fetch requests require sort descriptors.
    static func defaultSortDescriptors() -> [NSSortDescriptor]
}

extension CoreDataFinders where Self: NSManagedObject {
    
    public static func findAll(with predicate: NSPredicate?, context: NSManagedObjectContext) -> [Self] {
        
        let fetchRequest: NSFetchRequest = NSFetchRequest(entityName: Self.entity().name!)
        
        let predicate = predicate
        
        fetchRequest.predicate = predicate
        fetchRequest.sortDescriptors = self.defaultSortDescriptors()
        
        do {
            let results = try context.fetch(fetchRequest)
            return results
        } catch {
            if predicate != nil {
                print("Failed to fetch objects with predicate:\(predicate!.description) error:\(error)")
            } else {
                print("Failed to fetch objects with no predicate.  error:\(error)")
            }
        }
        return []
    }
    
    public static func findFirst(with predicate: NSPredicate?, context: NSManagedObjectContext) -> Self? {
        
        let fetchRequest: NSFetchRequest = NSFetchRequest(entityName: Self.entity().name!)
        
        let predicate = predicate
        
        fetchRequest.predicate = predicate
        fetchRequest.sortDescriptors = self.defaultSortDescriptors()
        fetchRequest.fetchLimit = 1
        
        do {
            let results = try context.fetch(fetchRequest)
            return results.first
        } catch {
            if predicate != nil {
                print("Failed to fetch objects with predicate:\(predicate!.description) error:\(error)")
            } else {
                print("Failed to fetch objects with no predicate.  error:\(error)")
            }
            
        }
        return nil
    }
}

And then you can add these to any object by either making a new baseclass in your app that subclasses NSManagedObject, or you just declare protocol support in your class definition and then these methods are added:

import Foundation
import CoreData

@objc(RecentItem)
class RecentItem: NSManagedObject, CoreDataFinders {
  static func defaultSortDescriptors() -> [NSSortDescriptor] {
    return [NSSortDescriptor(key: #keyPath(RecentItem.lastViewed), ascending: false)]
  }
  // ...
}

You can see here that you can extend this approach to pass in your own sort descriptors or limit fetch sizes, etc. This should be enough to get you started! The idea is that you can write the code once and have it apply to all instances of NSManagedObject on an opt-in basis.

Advertisements

Incredibly Useful Way to Print Your Core Data Model

So, I love a good bit of old fashioned Pen and Paper to get my thoughts out.  Maybe it’s that I enjoy just using a pen, the flow of the ink on the page.  (I’m, accordingly, a total pen snob).

Sometimes when I want to analyze a data model, I’d love to have it on paper, just so I can scribble, cross things out, etc, etc.

Until now, I found it difficult to have to deal with the CoreData model editor in Xcode because I couldn’t easily print it out, then I found this gem on stackoverflow.com

Which can be copy-pasted here:

I defined a 1m by 1m paper size, used it to create a PDF, cropped and then printed it:

  • Go to “File”->”Page Setup…”
  • Go to “Paper Size”->”Manage Custom Sizes…”
  • Define a new paper size with 1000×1000 mm and no borders
  • Go to “File”->”Print…”
  • Choose “PDF”->”Open PDF in Preview”
  • Go to “Tools”->”Rectangular Selection”, select the area to crop
  • Do “Tools”->”Crop”
  • Go to “File”->”Print…”, print

Sounds complicated, but works. Instead of cropping, you could use the scale factor in the Preview print dialog.

Cocoa Bindings – The Easy way for an NSView subclass to update itself

So, say you have a NSView subclass in your Interface that is purely data-driven (i.e. it’s not an interactive component, it merely provides a graphical representation of an underlying data model).  It would be nice to keep this view updated to the current state of your data model automatically without writing too much glue code.  Here comes Cocoa Bindings!

Now, as Cocoa Bindings and Core Data are both powerful technologies, it’s hard to write a tutorial that doesn’t start from the beginning.  So, this is not a tutorial (per se), nor am I starting from the beginning.  Here is the start situation:

– You are using Core Data in your app, and don’t have any issues like I have described in blog posts preceding these (i.e. pretty much every attribute is immutable (i.e. you use myAttribute = newAttribute to register any kind of change on my attribute),

– You are familiar with NSArrayControllers and know a bit about basic Cocoa Bindings.

So?  How to get things working?  The trick is to make use of the CoreData framework’s automatically generated NSNotifications, namely this one:

NSManagedObjectContextObjectsDidChangeNotification, which you can use to determine if any of these objects are of any interest to your NSView subclass, and if yes, tell your class to redraw itself.

How does this all begin?  It begins with the bindings.   In an app I’m working on, I have sort of objects that have a specific duration, and this duration is derived from the duration of child objects.  Imagine the parent object as a day, and the child objects as tasks, and those children have children of subtasks, who have a duration.  Get it ?  A sort of pseudo equation for the entire duration would be:

totalDuration = [day valueForKeyPath: @"tasks.@sum.subtasks.@sum.duration"];

(although in Cocoa this isn’t allowed, but perhaps you get the shorthand.  It sums all durations in the sub elements)

Or said another way, a Day has many Task object who each have many Subtask objects, and a subtask has a duration property that you are editing elsewhere in your GUI.

So, how do we get this working? Here would be a portion of your NSView subclass’ .m file. The .h is a subclass of NSView and has:

@property (nonatomic, strong) Day *day;

static int DayObservingContext; // see here for an explanation of KVO contexts http://stackoverflow.com/a/11917449/421797

+ (void)initialize
{
    [self exposeBinding:@"day"];
}

- (void)bind:(NSString *)binding toObject:(id)observable withKeyPath:(NSString *)keyPath options:(NSDictionary *)options
{
    if ([binding isEqualToString:@"day"]) {
        [observable addObserver:self forKeyPath:@"selection" options:0 context:&DayObservingContext];
        _arrayController = (NSArrayController*)observable;
    }
    else{
        [super bind:binding toObject:observable withKeyPath:keyPath options:options];
    }
}

- (void)unbind:(NSString *)binding
{
    if ([binding isEqualToString:@"day"]) {

        [_arrayController removeObserver:self forKeyPath:@"selection"];
        _arrayController = nil;
        self.day = nil;
    }
    else{
        [super unbind: binding];
    }
}

- (void)observeValueForKeyPath:(NSString *)keyPath ofObject:(id)object change:(NSDictionary *)change context:(void *)context
{
    if (context == &DayObservingContext) {

        id selection = _arrayController.arrangedObjects[_arrayController.selectionIndex];

        if (!selection && [selection isKindOfClass:[Day class]] == NO) {
            [NSException raise:NSInternalInconsistencyException format:@"This object should be bound to Day Objects!"];
        }

        self.day = (Day*)selection;

        [self setNeedsDisplayInRect:[self visibleRect]];
    }
    else
    {
        [super observeValueForKeyPath:keyPath ofObject:object change:change context:context];
    }
}

- (Day*)day { return _day;}
- (void)setDay:(Day *)day
{
    if (day == _day) {
        return;
    }

    if (_day) {
        [[NSNotificationCenter defaultCenter] removeObserver: self
                                                        name: NSManagedObjectContextObjectsDidChangeNotification
                                                      object: _day.managedObjectContext];
    }

    _day = day;

    if (_day) {
        [[NSNotificationCenter defaultCenter] addObserver: self
                                                 selector: @selector(contextChanged:)
                                                     name: NSManagedObjectContextObjectsDidChangeNotification
                                                   object: _day.managedObjectContext];
    }

    [self setNeedsDisplayInRect:[self visibleRect]];
}

- (void)contextChanged:(NSNotification*)notification
{
    // Get a set containing ALL objects which have been changed
    NSSet* insertedObjects = (NSSet*)[[notification userInfo] objectForKey:NSInsertedObjectsKey];
    NSSet* updatedObjects = (NSSet*)[[notification userInfo] objectForKey:NSUpdatedObjectsKey];
    NSSet* deletedObjects = (NSSet*)[[notification userInfo] objectForKey:NSDeletedObjectsKey];

    NSSet *changedObjects = [NSSet set];

    if (insertedObjects) changedObjects = [changedObjects setByAddingObjectsFromSet:insertedObjects];
    if (updatedObjects) changedObjects = [changedObjects setByAddingObjectsFromSet:updatedObjects];
    if (deletedObjects) changedObjects = [changedObjects setByAddingObjectsFromSet: deletedObjects];

    // go through all objects, find type, and see if they have a relationship that is a part of this Day
    BOOL shouldRefresh = NO;

    for (NSManagedObject *object in changedObjects) {

        if ([object isKindOfClass:[Day class]]) {
            if (object == _day) {
                shouldRefresh = YES;
                break;
            }
        }
        if ([object isKindOfClass:[Task class]]) {
            if ([(SCRecipeStep*)object day] == _day) {
                shouldRefresh = YES;
                break;
            }
        }
        if ([object isKindOfClass:[Subtask class]]) {  // it is a subtask that has the .duration property which is edited elsewhere in your GUI
            if ([[(Subtask*)object task] day] == _day) {
                shouldRefresh = YES;
                break;
            }
        }
    }

    if (shouldRefresh) {
        [self setNeedsDisplayInRect:[self visibleRect]];
    }

}

- (void)drawRect:(NSRect)dirtyRect
{
   // your custom code here that knows how to draw a Day object
}

Now, all you have to do in Interface Builder is create an outlet to the NSArrayController that is managing your Day objects (i.e. _arrayController.arrangedObjects will be a collection of Day objects), and of course an outlet to your “DayView” subclass.

then, you just add the binding in your loadView method:

- (void)loadView
{
    [super loadView];

    [self.dayView bind: @"day"
              toObject: self.daysArrayController
           withKeyPath: @"selection" /* due to the way we overrode bind: we could specify nil here */
               options: nil];
}

And so any time your currently selected day changes, it will know how to change the currently drawn day, and because we are using NSNotificationCenter to be informed of any changes to the managedObjectContext, and have code that checks if any of these changes are relevant to our view, the DayView will update itself accordingly.

I spent ages trying to get the Day object’s children to notify the parent if any of its properties change (explained in the KVO documentation, “Registering Dependent Keys, to-many relationships), but found it to be an insane amount of code required and thought “there must be an easier way than this”, I read that one sentence in this section of the documentation that states:

“2. If you’re using Core Data, you can register the parent with the application’s notification center as an observer of its managed object context. The parentshould respond to relevant change notifications posted by the children in a manner similar to that for key-value observing.”

This is what this post attempts to clarify. I hope it helps. I can’t believe how much time I’ve spent trying to get something that’s meant to be simple, working.

PS – THIS POST was very helpful for getting the right approach

Core Data, KVO, Bindings… Tips and Tricks for the Layman

So, as my blog has indicated, I have been trying to tackle many advanced technologies all at once, and I’m sure I will look back on this blog one day and shake my head at the silliness of the issues I’m having currently, but well, you can’t make an omelette without breaking some eggs.

I really wish I had a mentor, some guru, someone where I could just ask “am I on the right track?  Is this the right approach?”  I don’t need much supervision or direction, but just a point in the right direction would be IMMENSELY helpful.   So, without that, taking the brute force approach, I thought I’d post a few things that you, as the beginner may want to keep in mind until you are more advanced yourself.

This list will be periodically updated.

* KVO and Bindings are deep and powerful technologies (as is Core Data).  Apple has tried to hide a lot of this from you so you can get up and running quickly.  That said, it’s still a steep learning curve and it’s difficult to just ‘jump into’ KVO, Bindings, and Core Data all at once.

* Errors and exceptions relating to KVO, CoreData, and Bindings are difficult to debug as the console output of such errors is seldom descriptive enough so to help you track down the problem.

* Core Data entities should have properties that are ‘one thing’, and not a compound property.  (else you have to write a bunch of custom code)  i.e., if you for example use a Value Transformer to restore a custom object, changes to those objects’ properties will not mark the Core Data entity as dirty, and thus any changes you make to that will not get saved.  (unless you are setting these properties after just having added that object to the array, because by definition that object will be dirty.)  In slightly more programmer speak, your custom properties (transformable attributes) should be IMMUTABLE, meaning, only changes are registered if you make a call that’s something like

myEntity.myCustomProperty = someValue

NOT,

myEntity.myCustomProperty.maxValue = someMaxValue

* All the tutorials and intros to Cocoa Bindings tout it as this magic thing that will result in you never having to write glue code again!  Wrong.  F•cking wrong wrong wrong.  (At least until you get your head around it!!)  I’m actually quite surprised at how terrible some of Apple’s documentation can be on the topic of KVO, Core Data, and Bindings.  iOS Developers (which I am, 2 weeks into Cocoa…) won’t have to worry as most of this stuff doesn’t work on iOS.  For example, the easiest way to make a NSView subclass that is backed by a Core Data model.  I spent AGES trying to figure this out the KVO way, whereas the solution’s approach was briefly mentioned in ONE sentence in a KVO programmer’s guide.   (will write a post about this soon)

False Assumptions I have made along the way and what WASN’T true:

– changes to an object in a to-many relationship will notify its parent.  (i.e. I change an Employee’s name, the Department will not receive a KVO notification about this.  Only if you add/remove employees, will the Department receive an update about this.  This isn’t such an issue, but what if your object was a TimeLine with many TimeSteps, who each have a duration property?  The Timeline won’t update.  Yes yes, this is all in the KVO Documentation, and implementing such updates are non-trivial)

Cocoa Bindings misery

I’m really starting to think twice about Cocoa Bindings.  It’s touted as the ‘never write glue code again’ solution, making it seem like you’ll never spend time writing boring code again.  YAY.

Except they forget to mention that you’ll spend just as much time trying to debug a black box.  -.-  OR writing code that makes your application ‘fit into’ their way of doing things.  So if you don’t fully understand the technology, you’re spending all of your time trying to figure out WHY something won’t work.  It’s really kind of a pain in the a**.  I feel like I’m a first grader who got thrown into 5th grade math class.   Missing big pieces of the big picture and hoping you can fumble or fake your way through it.  Cocoa Bindings will punish you for that.

Can anyone tell me how you can set breakpoints on specific setters on Core Data generated accessors?  It would be nice to inspect the properties of specific data models at runtime.

I am binding a NSTableView (well, it’s individual columns), to an array controller who elements (arrangedObjects) are of type ‘TimeBlock’.  Now a TimeBlock is defined in the Core Data model, and it has a few NSNumber properties, and then a Transformable Property to type TimeRange (which I successfully convert to and from NSData with a NSValueTransformer subclass… as the docs say you should).

A TimeRange object has 2 NSNumbers for min and max amount of time something should take.  I want to bind either of these to a NSTextCell’s value.  So I bind the column to arrangedObjects.timeRange.max  (and even use a NSValueTransformer  to ensure strings are numbers and vice versa) and provide a NSNumberFormatter object to the NSTextCell.

But the Time Range object shows itself to be nil in the UI.  I have no idea what’s going wrong.  I set a value.  It ‘sticks’.  I save the models.  I restart the app.  None of the values have been committed.  I can’t debug it.  (Can I ?)

Teaching oneself such a technology is maybe not the best idea.  It’s like it’s a constant two steps forward, one step back way of learning, and it’s kind of annoying.

Rant over.  If and when I get this figured out I’m gonna write a tutorial – in plain English – how one needs to visualize how these things work.  This is just way too annoying, and the documentation is very very boring.