When Apple announced the Swift programming language in 2014, there were questions about the future of frameworks that depended heavily on the dynamic nature of Objective-C. Frameworks such as Core Data marshall objects with type dependencies determined at runtime. The strict type checks required by the Swift compiler appeared in conflict with Core Data.

Swift 2 was introduced about a year later, which generated talk about Value Types and the practical benefits of immutable model objects. Protocols — in particular protocol extensions — allow for behaviorally typed objects determined through multiple inheritance and mixins. The promise looks incredible, but unfortunately WWDC 2015 was thin in practical examples of these new language features. Further, it was unknown if Swift 2 would bring a return to expressive and compact code we had grown accustomed to with Objective-C.

Using an NSManagedObject category I wrote in Objective-C nearly five years ago, I converted it to Swift as a practical example. I wanted to explore the features of the new language in a real-world scenario. I found Swift to be every bit as expressive a language as Objective-C. Of course, I also gained the better type checking and superior error-handling that are signatures of Swift. But why tell you when it’s easier to show in code? Let’s meet the cast.

Meet NSManagedObject+MCPAdditions

One of the main complaints I hear about Core Data is “too much boilerplate code”. You have to create NSPredicate objects for queries, and don’t forget their associated NSSortDescriptors. One of the first things I did after learning Core Data was write a few categories to make my life easier and expedite the process of integrating Core Data into a project1.

The following is one such category, which allows us to quickly query a supplied NSManagedObjectContext for records of a specified type:


// NSManagedObjectContext+MCPAdditions.h
#import <CoreData/CoreData.h>

@interface NSManagedObject (MCPAdditions)
  + (NSArray *)mcp_objectsInContext:(NSManagedObjectContext *)context;
  + (NSArray *)mcp_objectsInContext:(NSManagedObjectContext *)context filteredUsing:(NSPredicate *)predicate;
  + (NSArray *)mcp_objectsInContext:(NSManagedObjectContext *)context filteredUsing:(NSPredicate *)predicate sortedBy:(NSString *)sortKey ascending:(BOOL)isAscending;
  + (id)mcp_singleObjectInContext:(NSManagedObjectContext *)context filteredUsing:(NSPredicate *)predicate;
  + (NSUInteger)mcp_objectCountInContext:(NSManagedObjectContext *)context;
  + (NSUInteger)mcp_objectCountInContext:(NSManagedObjectContext *)context filteredUsing:(NSPredicate *)predicate;
  + (NSFetchRequest *)mcp_fetchRequestInContext:(NSManagedObjectContext *)context filteredUsing:(NSPredicate *)predicate;
@end

First, the telescoping method signatures so we can use default sorting or find all objects of a particular record type from a shorter method signature. Next, you might notice the mcp_ prefix applied to each of the methods to prevent namespace collision. Since this is Objective-C, I can use id and a generic NSArray collection as flexible return types that will accept objects of any type. An astute reader would assume all error handling occurs inside the method. Great for clean code up the chain, but bad for finding out the particular place in code where an error is occurring. It isn’t perfect, but it followed the standard Cocoa and C conventions.

Swift to the rescue

Right away there are obvious language features in Swift that can simplify this API. My first approach was to port the class extension directly to Swift:


// NSManagedObject+MCPAdditions.swift
import CoreData

extension NSManagedObject
{
  static func objectsInContext(context: NSManagedObjectContext, predicate: NSPredicate? = nil, sortedBy: String? = nil, ascending: Bool = false) throws -> [AnyObject] {}
  static func singleObjectInContext(context: NSManagedObjectContext, predicate: NSPredicate? = nil, sortedBy: String? = nil, ascending: Bool = false) throws -> AnyObject? {}
  static func objectCountInContext(context: NSManagedObjectContext, predicate: NSPredicate? = nil) -> Int {}
  static func fetchRequest(context: NSManagedObjectContext, predicate: NSPredicate? = nil, sortedBy: String? = nil, ascending: Bool = false) -> NSFetchRequest {}
}

Not bad, huh? Thanks to default values, we can simplify the extension to only four method blocks. objectsInContext(context) and others will still work, of course, thanks to Swift collapsing the method signature automatically. Killer. We can also let callers know when an object might be nil and force them to guard against it using an optional return type. And let us not forget the wonderful throw that forces callers to handle errors that could occur when executing fetch requests in the context.

I was pretty happy with this when I shared it with my colleague Trevor, who’s way smarter about Core Data than I am. I went to him with two problems:

  1. Stupid force-casting AnyObject to my NSManagedObject subclasses, e.g., return results as! NSManagedObject, etc.
  2. A class extension didn’t feel very Swift-like. Shouldn’t this be a protocol?

Why force-casting is sometimes dumb

I get that the compiler is all knowing and if I would just trust it dammit, it would save me from stupid bugs. But sometimes the Swift compiler is a little too bitchy for my taste. Take the required casting when calling objectsInContext(_,predicate:,sortedBy:,ascending:), which would look something like:


  do {
    let results = try MyRumMO.objectsInContext(context, predicate:filter)
    if let castResults = results as? MyRumMO {
      return castResults
    }
  }

  catch {
    FatalError("Why is the rum always gone?")
  }

Or if I wanted to live dangerously, I could instead write:


  return results as! MyRumMO

But every time you type !, someone at Apple deletes a Radar, or so the compiler wants us to believe. Thing is, typing the above isn’t dangerous in practice because Core Data will almost always vend back an array of objects of the Entity type passed into the request. That’s the point of the model file you create in Xcode where you assign specific class types to model definitions.

I get that I shouldn’t trust code written by me2 but shouldn’t I be able to trust Apple’s frameworks? You can probably hear Objective-C laughing at us right now, with the cigarrette still dangling from its mouth. Jerk. Maybe this is what Brent Simmons was talking about when he said he missed the dynamic runtime of Objective-C.

The bottom line is that the Swift compiler — like Charon — requires tribute before we can pass on into the next life.

Render unto Caesar…

It turns out the answer to one question begat the solution to the other. By turning this class extension into a protocol and with a clever use of typealias, I resolved the insidious casting problem while maintaining type safety. Protocols to the rescue!

First, the definition for Fetchable:


  // Fetchable.swift
  protocol Fetchable
  {
    typealias FetchableType: NSManagedObject

    static func entityName() -> String
    static func objectsInContext(context: NSManagedObjectContext, predicate: NSPredicate?, sortedBy: String?, ascending: Bool) throws -> [FetchableType]
    static func singleObjectInContext(context: NSManagedObjectContext, predicate: NSPredicate?, sortedBy: String?, ascending: Bool) throws -> FetchableType?
    static func objectCountInContext(context: NSManagedObjectContext, predicate: NSPredicate?) -> Int
    static func fetchRequest(context: NSManagedObjectContext, predicate: NSPredicate?, sortedBy: String?, ascending: Bool) -> NSFetchRequest
  }

Oh, clever girl. It turns out we can simply define our desired type by deferring downstream with the FetchableType definition.

In order to share the implementation, we have to create a protocol extension that houses the methods doing the heavy lifting:


  // Fetchable.swift
  extension Fetchable
  {
    static func singleObjectInContext(context: NSManagedObjectContext, predicate: NSPredicate? = nil, sortedBy: String? = nil, ascending: Bool = false) throws -> FetchableType?
    {
      // ...
    }

    static func objectCountInContext(context: NSManagedObjectContext, predicate: NSPredicate? = nil) -> Int
    {
      // ...
    }

    static func objectsInContext(context: NSManagedObjectContext, predicate: NSPredicate? = nil, sortedBy: String? = nil, ascending: Bool = false) throws -> [FetchableType]
    {
      // ...
    }

    static func fetchRequest(context: NSManagedObjectContext, predicate: NSPredicate? = nil, sortedBy: String? = nil, ascending: Bool = false) -> NSFetchRequest
    {
      // ...
    }
  }

Now we only need a simple extension for each of our child MO classes, like so:


  // MyRumMO.swift
  extension MyRumMO : Fetchable
  {
    typealias FetchableType = MyRumMO

    static func entityName() -> String
    {
      return "Rum"
    }
  }

This is incredibly cool, because now we get the benefits of type inference, without the bugs or uncertainty because I’m actually defining the type in my code. In your face, Objective-C!

Now I can type let results = try MyRumMO.objectsInContext(context, predicate:filter) without a single cast statement and expect results will be an array full of MyRumMO objects.

One more thing…

If you’re a Swift superfan, you’re probably shaking your head and wondering why I’m celebrating code so brittle. After all, a simple typo in the FetchableType definition could break things and the compiler won’t even warn you. Fret not, fellow superfan, we can add constraints to improve type checking:


extension Fetchable where Self : NSManagedObject, FetchableType == Self

One of the killer features of protocol extensions is that you can constrain the extension based on a variety of conditions. Conditional inheritance is incredibly powerful and in this case, allows us to enforce integrity of our FetchableType definition. The line FetchableType == Self will prevent the protocol extension (where the implementation lives) from ever being run against a Fetchable sub-class if you mis-define FetchableType in your class definition. Peanut Butter Jelly Time.

Note: This only prevents the protocol extension from being applied, causing a compiler warning for an incomplete implementation of Fetchable. It’s not perfect, but it does give you an extra check.

A brave new world

The point of this activity (and writing this article) was to show the benefits of Swift in a practical example, with code that will get used in production apps. I’ve shown how you can replace the flexibility of type inference in Objective-C using the expressive capabilities of Swift protocol extensions, while still maintaining the benefits of Swift you already understand.

Our resulting protocol (and extension) is more compact than the original Objective-C. It has superior error handling and it can be added to any existing MOs without altering the existing inheritance hierarchy. Finally, it ensures type safety and is explicit.

There you have it. Swift and Core Data. Cats and dogs living together. If you want a peek at the fully implemented protocol, you can check it out here

  1. Marcus Zarra already showed how simple it is to setup your Core Data stack 

  2. There’s good reason I hired smart people to write all of the code for MartianCraft clients. 

Rob Rhyne

MartianCraft Alumni

MartianCraft is a US-based mobile software development agency. For nearly two decades, we have been building world-class and award-winning mobile apps for all types of businesses. We would love to create a custom software solution that meets your specific needs. Let's get in touch.