Coder Social home page Coder Social logo

adamnemecek / coredataqueryinterface Goto Github PK

View Code? Open in Web Editor NEW

This project forked from prosumma/coredataqueryinterface

0.0 3.0 0.0 877 KB

A type-safe, fluent Swift library for working with Core Data

License: MIT License

Ruby 9.61% Swift 88.88% Objective-C 1.51%

coredataqueryinterface's Introduction

CoreDataQueryInterface

CocoaPods compatible Carthage compatible Language Platforms

Core Data Query Interface (CDQI) is a type-safe, fluent, intuitive library for working with Core Data in Swift. CDQI tremendously reduces the amount of code needed to do Core Data, and dramatically improves readability by allowing method chaining and by eliminating magic strings. CDQI is a bit like jQuery or LINQ, but for Core Data.

Features

  • Fluent interface, i.e., chainable methods
  • Large number of useful overloads
  • Type-safety in filter comparisons.
  • Filtering, sorting, grouping, aggregate expressions, limits, etc.
  • Optionally eliminates the use of magic strings so common in Core Data
  • Query reuse, i.e., no side-effects from chaining
  • Support for iOS 9+, macOS 10.11+, tvOS 9+, and watchOS 2+.
  • Swift 3 (for Swift 2.2, use v4.0)

Overview

In essence, CDQI is a tool that allows the creation (and execution) of fetch requests using a fluent syntax. In most cases, this can reduce many lines of code to a single (but still highly readable) line.

let swiftDevelopers = managedObjectContext.from(Developer.self).
                      filter{ any($0.languages.name == "Swift") }.
                      order(ascending: false, {$0.lastName})
                      .limit(5)
                      .all()

Integration

Carthage

In your Cartfile, add the following line:

github "prosumma/CoreDataQueryInterface" ~> 5.0

CocoaPods

Add the following to your Podfile. If it isn't already present, you will have to add use_frameworks! as well.

pod 'CoreDataQueryInterface', '~> 5.0'

Changes From Previous Versions

The overall syntax of CDQI is unchanged from previous versions, as the examples in this document show. But there are small changes.

First, EntityQuery, ExpressionQuery and the like are gone, replaced by a single type Query<M, R>. The first generic parameter is the type of managed object model to work with. The second parameter is the result type, which must conform to the NSFetchResultType protocol. So instead of saying EntityQuery.from(Project.self), just create an instance with Query<Project, NSDictionary>().

The second major difference is the use of prefixes on methods, properties, and type aliases. CDQI extends common types like String, NSExpression, and so on. Previous versions of CDQI added method and property names that had a higher likelihood of conflict with other frameworks or future changes by Apple. To mitigate this, methods, properties, and associated types that may be added to arbitrary types use the cdqi or CDQI prefix as appropriate. For example:

public protocol ExpressionConvertible {
  var cdqiExpression: NSExpression { get }
}

public protocol TypedExpressionConvertible: ExpressionConvertible, Typed {
    associatedtype CDQIComparisonType: Typed
}

Attribute Proxies

In order to use expressions such as $0.languages.name as in the example above, proxy objects must be created. In the bin folder at the root of the project is a simple tool called cdqi that accomplishes this. Before running this tool, make sure that each NSManagedObject is represented by a corresponding class in your Swift project.

cdqi Developers

This searches all subdirectories recursively until it finds a managed object model called Developers.xcdatamodeld. It then examines the current version of this model and generates proxy classes for each NSManagedObject. By default, these proxy classes are placed in the same directory as the managed object model, side by side. cdqi has many options to change this behavior if desired, but in most cases the default is what you want. For more options, execute cdqi --help.

Note that when working with autogenerated Core Data classes, you should create your proxies with the --public flag, otherwise you will get compilation errors.

Type Safety

CDQI supports type safety in filter expressions. In the expression $0.languages.name, the name attribute has been defined as a string in the Core Data model, so it can only be compared to strings. The following will not compile:

$0.languages.name == 4

In order to support extensibility, CDQI's type safety is actually more sophisticated than described above. The Swift String type is able to participate in comparisons to string attributes because it implements TypedExpressionConvertible:

extension String: TypedExpressionConvertible {
    public typealias CDQIComparisonType = String
    public static let cdqiStaticType = NSAttributeType.stringAttributeType
    public var cdqiExpression: NSExpression {
        return NSExpression(forConstantValue: self)
    }
}

By implementing the TypedExpressionConvertible protocol and defining its CDQIComparisonType typealias as String, a type can be made to participate in CDQI string comparisons. To participate in numeric comparisons, CDQIComparisonType should be NSNumber.

Imagine a Weekday enumeration to which we wish to compare an Int32 Core Data attribute. Instead of saying $0.weekday == Weekday.Monday.rawValue, we can make things a little nicer:

public enum Weekday: Int {
    case Sunday = 1
    case Monday = 2
    case Tuesday = 3
    case Wednesday = 4
    case Thursday = 5
    case Friday = 6
    case Saturday = 7    
}

extension Weekday: TypedExpressionConvertible {
    public typealias CDQIComparisonType = NSNumber
    public static let cdqiStaticType = NSAttributeType.integer32AttributeType
    public var cdqiExpression: NSExpression {
        return NSExpression(forConstantValue: NSNumber(value: rawValue))
    }
}

Now we can say $0.weekday == Weekday.Monday. Any type can be made to participate in CDQI filter comparisons using this technique.

Query Reuse

CDQI uses value types wherever possible. Most CDQI methods such as filter, order, and so on return the value type Query<M, R>. This allows techniques such as the following:

let projectQuery = Query<Project, Project>()
let swiftProjectQuery = projectQuery.filter{ any($0.languages.name == "Swift") }

The second statement causes no side effects on the first one.

Examples

A great number of examples can be found in the unit tests and the "Top Hits" example app in the Examples folder, but here are a few at a glance.

let developerQuery = managedObjectContext.from(Developer.self)
// predicate: languages.@count == 3 AND ANY languages.name == "Rust"
// developersWhoKnowThreeLanguagesIncludingRust is an array of Developer entities
let developersWhoKnowThreeLanguagesIncludingRust = developerQuery.filter{ $0.languages.cdqiCount() == 3 &&
                                                   any($0.languages.name == "Rust") }.all()

// predicate: ANY languages.name == "Haskell"
// haskellDevelopers is an array of dictionaries, e.g., [["firstName": "Haskell", "lastName": "Curry"]]
let haskellDevelopers = developerQuery.
                        filter{ developer in any(developer.languages.name == "Haskell") }.
                        select{ developer in [developer.firstName, developer.lastName] }.all()

// Instead of using $0, we can create a proxy up front.
let project = Project.CDQIAttribute()

// We can do a query in a single line
var swiftProjectNames: [String] = managedObjectContext.from(Project.self).
                                  filter(any(project.languages.name == "Swift")).
                                  order(project.name).array(project.name)

// Or we can build it up in multiple lines
var projectQuery = managedObjectContext.from(Project.self)
projectQuery = projectQuery.filter(any(project.languages.name == "Swift"))
projectQuery = projectQuery.order(project.name)
swiftProjectNames = projectQuery.array(project.name)

coredataqueryinterface's People

Contributors

revolucent avatar patgoley avatar

Watchers

 avatar James Cloos avatar  avatar

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.