mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-03-18 23:43:57 +01:00
added ability to query aggregates and attributes straight from the persistent store
This commit is contained in:
@@ -1,99 +0,0 @@
|
||||
//
|
||||
// AggregateFunction.swift
|
||||
// HardcoreData
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - AggregateFunction
|
||||
|
||||
public enum AggregateFunction {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
case Average(AttributeName)
|
||||
case Sum(AttributeName)
|
||||
case Count(AttributeName)
|
||||
case Minimum(AttributeName)
|
||||
case Maximum(AttributeName)
|
||||
case Medium(AttributeName)
|
||||
case Mode(AttributeName)
|
||||
case StandardDeviation(AttributeName)
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal func createExpression() -> NSExpression {
|
||||
|
||||
switch self {
|
||||
|
||||
case .Average(let attributeName):
|
||||
return NSExpression(
|
||||
forFunction: "average:",
|
||||
arguments: [NSExpression(forKeyPath: "\(attributeName)")]
|
||||
)
|
||||
|
||||
case .Sum(let attributeName):
|
||||
return NSExpression(
|
||||
forFunction: "sum:",
|
||||
arguments: [NSExpression(forKeyPath: "\(attributeName)")]
|
||||
)
|
||||
|
||||
case .Count(let attributeName):
|
||||
return NSExpression(
|
||||
forFunction: "count:",
|
||||
arguments: [NSExpression(forKeyPath: "\(attributeName)")]
|
||||
)
|
||||
|
||||
case .Minimum(let attributeName):
|
||||
return NSExpression(
|
||||
forFunction: "min:",
|
||||
arguments: [NSExpression(forKeyPath: "\(attributeName)")]
|
||||
)
|
||||
|
||||
case .Maximum(let attributeName):
|
||||
return NSExpression(
|
||||
forFunction: "max:",
|
||||
arguments: [NSExpression(forKeyPath: "\(attributeName)")]
|
||||
)
|
||||
|
||||
case .Medium(let attributeName):
|
||||
return NSExpression(
|
||||
forFunction: "medium:",
|
||||
arguments: [NSExpression(forKeyPath: "\(attributeName)")]
|
||||
)
|
||||
|
||||
case .Mode(let attributeName):
|
||||
return NSExpression(
|
||||
forFunction: "mode:",
|
||||
arguments: [NSExpression(forKeyPath: "\(attributeName)")]
|
||||
)
|
||||
|
||||
case .StandardDeviation(let attributeName):
|
||||
return NSExpression(
|
||||
forFunction: "stddev:",
|
||||
arguments: [NSExpression(forKeyPath: "\(attributeName)")]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,232 +0,0 @@
|
||||
//
|
||||
// AggregateResultType.swift
|
||||
// HardcoreData
|
||||
//
|
||||
// Copyright (c) 2015 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - AggregateResultType
|
||||
|
||||
public protocol AggregateResultType {
|
||||
|
||||
static var attributeType: NSAttributeType { get }
|
||||
static func fromResultObject(result: AnyObject) -> Self
|
||||
}
|
||||
|
||||
|
||||
extension Bool: AggregateResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .BooleanAttributeType
|
||||
}
|
||||
|
||||
public static func fromResultObject(result: AnyObject) -> Bool {
|
||||
|
||||
return (result as! NSNumber).boolValue
|
||||
}
|
||||
}
|
||||
|
||||
extension Int8: AggregateResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .Integer64AttributeType
|
||||
}
|
||||
|
||||
public static func fromResultObject(result: AnyObject) -> Int8 {
|
||||
|
||||
return numericCast((result as! NSNumber).longLongValue)
|
||||
}
|
||||
}
|
||||
|
||||
extension Int16: AggregateResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .Integer64AttributeType
|
||||
}
|
||||
|
||||
public static func fromResultObject(result: AnyObject) -> Int16 {
|
||||
|
||||
return numericCast((result as! NSNumber).longLongValue)
|
||||
}
|
||||
}
|
||||
|
||||
extension Int32: AggregateResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .Integer64AttributeType
|
||||
}
|
||||
|
||||
public static func fromResultObject(result: AnyObject) -> Int32 {
|
||||
|
||||
return numericCast((result as! NSNumber).longLongValue)
|
||||
}
|
||||
}
|
||||
|
||||
extension Int64: AggregateResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .Integer64AttributeType
|
||||
}
|
||||
|
||||
public static func fromResultObject(result: AnyObject) -> Int64 {
|
||||
|
||||
return numericCast((result as! NSNumber).longLongValue)
|
||||
}
|
||||
}
|
||||
|
||||
extension Int: AggregateResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .Integer64AttributeType
|
||||
}
|
||||
|
||||
public static func fromResultObject(result: AnyObject) -> Int {
|
||||
|
||||
return numericCast((result as! NSNumber).longLongValue)
|
||||
}
|
||||
}
|
||||
|
||||
extension UInt8: AggregateResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .Integer64AttributeType
|
||||
}
|
||||
|
||||
public static func fromResultObject(result: AnyObject) -> UInt8 {
|
||||
|
||||
return numericCast((result as! NSNumber).longLongValue)
|
||||
}
|
||||
}
|
||||
|
||||
extension UInt16: AggregateResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .Integer64AttributeType
|
||||
}
|
||||
|
||||
public static func fromResultObject(result: AnyObject) -> UInt16 {
|
||||
|
||||
return numericCast((result as! NSNumber).longLongValue)
|
||||
}
|
||||
}
|
||||
|
||||
extension UInt32: AggregateResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .Integer64AttributeType
|
||||
}
|
||||
|
||||
public static func fromResultObject(result: AnyObject) -> UInt32 {
|
||||
|
||||
return numericCast((result as! NSNumber).longLongValue)
|
||||
}
|
||||
}
|
||||
|
||||
extension UInt64: AggregateResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .Integer64AttributeType
|
||||
}
|
||||
|
||||
public static func fromResultObject(result: AnyObject) -> UInt64 {
|
||||
|
||||
return numericCast((result as! NSNumber).longLongValue)
|
||||
}
|
||||
}
|
||||
|
||||
extension UInt: AggregateResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .Integer64AttributeType
|
||||
}
|
||||
|
||||
public static func fromResultObject(result: AnyObject) -> UInt {
|
||||
|
||||
return numericCast((result as! NSNumber).longLongValue)
|
||||
}
|
||||
}
|
||||
|
||||
extension Double: AggregateResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .DoubleAttributeType
|
||||
}
|
||||
|
||||
public static func fromResultObject(result: AnyObject) -> Double {
|
||||
|
||||
return (result as! NSNumber).doubleValue
|
||||
}
|
||||
}
|
||||
|
||||
extension Float: AggregateResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .FloatAttributeType
|
||||
}
|
||||
|
||||
public static func fromResultObject(result: AnyObject) -> Float {
|
||||
|
||||
return (result as! NSNumber).floatValue
|
||||
}
|
||||
}
|
||||
|
||||
extension NSNumber: AggregateResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .Integer64AttributeType
|
||||
}
|
||||
|
||||
public class func fromResultObject(result: AnyObject) -> Self {
|
||||
|
||||
return self(bytes: result.bytes, objCType: result.objCType)
|
||||
}
|
||||
}
|
||||
|
||||
extension NSDate: AggregateResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .DateAttributeType
|
||||
}
|
||||
|
||||
public class func fromResultObject(result: AnyObject) -> Self {
|
||||
|
||||
return self(timeInterval: 0.0, sinceDate: result as! NSDate)
|
||||
}
|
||||
}
|
||||
@@ -41,7 +41,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
|
||||
*/
|
||||
public func commit(completion: (result: SaveResult) -> Void) {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to commit a <\(self.dynamicType)> outside a transaction queue.")
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to commit a <\(self.dynamicType)> outside its designated queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to commit a <\(self.dynamicType)> more than once.")
|
||||
|
||||
self.isCommitted = true
|
||||
@@ -62,7 +62,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
|
||||
*/
|
||||
public func commitAndWait() {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to commit a <\(self.dynamicType)> outside a transaction queue.")
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to commit a <\(self.dynamicType)> outside its designated queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to commit a <\(self.dynamicType)> more than once.")
|
||||
|
||||
self.isCommitted = true
|
||||
@@ -77,6 +77,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
|
||||
*/
|
||||
public func beginSynchronous(closure: (transaction: SynchronousDataTransaction) -> Void) -> SaveResult? {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to begin a child transaction from a <\(self.dynamicType)> outside its designated queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to begin a child transaction from an already committed <\(self.dynamicType)>.")
|
||||
|
||||
return SynchronousDataTransaction(
|
||||
|
||||
@@ -35,63 +35,113 @@ public extension BaseDataTransaction {
|
||||
|
||||
public func fetchOne<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> T? {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside its designated queue.")
|
||||
|
||||
return self.context.fetchOne(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchOne<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> T? {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside its designated queue.")
|
||||
|
||||
return self.context.fetchOne(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> [T]? {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside its designated queue.")
|
||||
|
||||
return self.context.fetchAll(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> [T]? {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside its designated queue.")
|
||||
|
||||
return self.context.fetchAll(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchCount<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> Int? {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside its designated queue.")
|
||||
|
||||
return self.context.fetchCount(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchCount<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> Int? {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside its designated queue.")
|
||||
|
||||
return self.context.fetchCount(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchObjectID<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> NSManagedObjectID? {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside its designated queue.")
|
||||
|
||||
return self.context.fetchObjectID(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchObjectID<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> NSManagedObjectID? {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside its designated queue.")
|
||||
|
||||
return self.context.fetchObjectID(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchObjectIDs<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> [NSManagedObjectID]? {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside its designated queue.")
|
||||
|
||||
return self.context.fetchObjectIDs(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchObjectIDs<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> [NSManagedObjectID]? {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside its designated queue.")
|
||||
|
||||
return self.context.fetchObjectIDs(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func deleteAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> Int? {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to delete from a <\(self.dynamicType)> outside its designated queue.")
|
||||
|
||||
return self.context.deleteAll(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func deleteAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> Int? {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to delete from a <\(self.dynamicType)> outside its designated queue.")
|
||||
|
||||
return self.context.deleteAll(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func queryAggregate<T: NSManagedObject>(entity: T.Type, function: AggregateFunction, _ queryClauses: FetchClause...) -> Int? {
|
||||
public func queryValue<T: NSManagedObject, U: SelectValueResultType>(entity: T.Type, _ selectClause: Select<U>, _ queryClauses: FetchClause...) -> U? {
|
||||
|
||||
let result = self.context.queryAggregate(entity, function: function, queryClauses)
|
||||
return result
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to query from a <\(self.dynamicType)> outside its designated queue.")
|
||||
|
||||
return self.context.queryValue(entity, selectClause, queryClauses)
|
||||
}
|
||||
|
||||
public func queryAggregate<T: NSManagedObject>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> Int? {
|
||||
public func queryValue<T: NSManagedObject, U: SelectValueResultType>(entity: T.Type, _ selectClause: Select<U>, _ queryClauses: [FetchClause]) -> U? {
|
||||
|
||||
let result = self.context.queryAggregate(entity, function: function, queryClauses)
|
||||
return result
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to query from a <\(self.dynamicType)> outside its designated queue.")
|
||||
|
||||
return self.context.queryValue(entity, selectClause, queryClauses)
|
||||
}
|
||||
|
||||
public func queryAggregate<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: FetchClause...) -> U? {
|
||||
public func queryAttributes<T: NSManagedObject>(entity: T.Type, _ selectClause: Select<NSDictionary>, _ queryClauses: QueryClause...) -> [[NSString: AnyObject]]? {
|
||||
|
||||
return self.context.queryAggregate(entity, function: function, queryClauses)
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to query from a <\(self.dynamicType)> outside its designated queue.")
|
||||
|
||||
return self.context.queryAttributes(entity, selectClause, queryClauses)
|
||||
}
|
||||
|
||||
public func queryAggregate<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> U? {
|
||||
public func queryAttributes<T: NSManagedObject>(entity: T.Type, _ selectClause: Select<NSDictionary>, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? {
|
||||
|
||||
return self.context.queryAggregate(entity, function: function, queryClauses)
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to query from a <\(self.dynamicType)> outside its designated queue.")
|
||||
|
||||
return self.context.queryAttributes(entity, selectClause, queryClauses)
|
||||
}
|
||||
}
|
||||
@@ -50,7 +50,7 @@ public /*abstract*/ class BaseDataTransaction {
|
||||
*/
|
||||
public func create<T: NSManagedObject>(entity: T.Type) -> T {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to create an entity of type <\(entity)> outside a transaction queue.")
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to create an entity of type <\(entity)> outside its designated queue.")
|
||||
|
||||
return T.createInContext(self.context)
|
||||
}
|
||||
@@ -63,7 +63,7 @@ public /*abstract*/ class BaseDataTransaction {
|
||||
*/
|
||||
public func fetch<T: NSManagedObject>(object: T) -> T? {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to update an entity of type <\(object.dynamicType)> outside a transaction queue.")
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to update an entity of type <\(object.dynamicType)> outside its designated queue.")
|
||||
|
||||
return object.inContext(self.context)
|
||||
}
|
||||
@@ -75,7 +75,7 @@ public /*abstract*/ class BaseDataTransaction {
|
||||
*/
|
||||
public func delete(object: NSManagedObject) {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to delete an entity of type <\(object.dynamicType)> outside a transaction queue.")
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to delete an entity of type <\(object.dynamicType)> outside its designated queue.")
|
||||
|
||||
object.deleteFromContext()
|
||||
}
|
||||
@@ -87,7 +87,7 @@ public /*abstract*/ class BaseDataTransaction {
|
||||
*/
|
||||
public func rollback() {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to rollback a <\(self.dynamicType)> outside a transaction queue.")
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to rollback a <\(self.dynamicType)> outside its designated queue.")
|
||||
|
||||
self.context.reset()
|
||||
}
|
||||
|
||||
@@ -1,5 +1,5 @@
|
||||
//
|
||||
// CustomizeQuery.swift
|
||||
// CustomizeFetch.swift
|
||||
// HardcoreData
|
||||
//
|
||||
// Copyright (c) 2015 John Rommel Estropia
|
||||
@@ -27,9 +27,9 @@ import Foundation
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - CustomizeQuery
|
||||
// MARK: - CustomizeFetch
|
||||
|
||||
public struct CustomizeQuery: FetchClause {
|
||||
public struct CustomizeFetch: FetchClause {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
50
HardcoreData/DataStack+Observing.swift
Normal file
50
HardcoreData/DataStack+Observing.swift
Normal file
@@ -0,0 +1,50 @@
|
||||
//
|
||||
// DataStack+Observing.swift
|
||||
// HardcoreData
|
||||
//
|
||||
// Copyright (c) 2015 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
import GCDKit
|
||||
|
||||
|
||||
// MARK: - DataStack
|
||||
|
||||
public extension DataStack {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
public func observeObjectList<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> ManagedObjectListController<T> {
|
||||
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside the main queue.")
|
||||
|
||||
// TODO: sectionNameKeyPath and cacheResults
|
||||
return ManagedObjectListController(
|
||||
dataStack: self,
|
||||
entity: entity,
|
||||
sectionNameKeyPath: nil,
|
||||
cacheResults: false,
|
||||
queryClauses: queryClauses
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
import GCDKit
|
||||
|
||||
|
||||
// MARK: - DataStack
|
||||
@@ -35,63 +36,113 @@ public extension DataStack {
|
||||
|
||||
public func fetchOne<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> T? {
|
||||
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside the main queue.")
|
||||
|
||||
return self.mainContext.fetchOne(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchOne<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> T? {
|
||||
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside the main queue.")
|
||||
|
||||
return self.mainContext.fetchOne(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> [T]? {
|
||||
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside the main queue.")
|
||||
|
||||
return self.mainContext.fetchAll(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> [T]? {
|
||||
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside the main queue.")
|
||||
|
||||
return self.mainContext.fetchAll(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchCount<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> Int? {
|
||||
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside the main queue.")
|
||||
|
||||
return self.mainContext.fetchCount(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchCount<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> Int? {
|
||||
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside the main queue.")
|
||||
|
||||
return self.mainContext.fetchCount(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchObjectID<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> NSManagedObjectID? {
|
||||
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside the main queue.")
|
||||
|
||||
return self.mainContext.fetchObjectID(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchObjectID<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> NSManagedObjectID? {
|
||||
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside the main queue.")
|
||||
|
||||
return self.mainContext.fetchObjectID(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchObjectIDs<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> [NSManagedObjectID]? {
|
||||
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside the main queue.")
|
||||
|
||||
return self.mainContext.fetchObjectIDs(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchObjectIDs<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> [NSManagedObjectID]? {
|
||||
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside the main queue.")
|
||||
|
||||
return self.mainContext.fetchObjectIDs(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func deleteAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> Int? {
|
||||
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to delete from a <\(self.dynamicType)> outside the main queue.")
|
||||
|
||||
return self.mainContext.deleteAll(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func deleteAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> Int? {
|
||||
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to delete from a <\(self.dynamicType)> outside the main queue.")
|
||||
|
||||
return self.mainContext.deleteAll(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func queryAggregate<T: NSManagedObject>(entity: T.Type, function: AggregateFunction, _ queryClauses: FetchClause...) -> Int? {
|
||||
public func queryValue<T: NSManagedObject, U: SelectValueResultType>(entity: T.Type, _ selectClause: Select<U>, _ queryClauses: FetchClause...) -> U? {
|
||||
|
||||
let result = self.mainContext.queryAggregate(entity, function: function, queryClauses)
|
||||
return result
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to query from a <\(self.dynamicType)> outside the main queue.")
|
||||
|
||||
return self.mainContext.queryValue(entity, selectClause, queryClauses)
|
||||
}
|
||||
|
||||
public func queryAggregate<T: NSManagedObject>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> Int? {
|
||||
public func queryValue<T: NSManagedObject, U: SelectValueResultType>(entity: T.Type, _ selectClause: Select<U>, _ queryClauses: [FetchClause]) -> U? {
|
||||
|
||||
let result = self.mainContext.queryAggregate(entity, function: function, queryClauses)
|
||||
return result
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to query from a <\(self.dynamicType)> outside the main queue.")
|
||||
|
||||
return self.mainContext.queryValue(entity, selectClause, queryClauses)
|
||||
}
|
||||
|
||||
public func queryAggregate<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: FetchClause...) -> U? {
|
||||
public func queryAttributes<T: NSManagedObject>(entity: T.Type, _ selectClause: Select<NSDictionary>, _ queryClauses: QueryClause...) -> [[NSString: AnyObject]]? {
|
||||
|
||||
return self.mainContext.queryAggregate(entity, function: function, queryClauses)
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to query from a <\(self.dynamicType)> outside the main queue.")
|
||||
|
||||
return self.mainContext.queryAttributes(entity, selectClause, queryClauses)
|
||||
}
|
||||
|
||||
public func queryAggregate<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> U? {
|
||||
public func queryAttributes<T: NSManagedObject>(entity: T.Type, _ selectClause: Select<NSDictionary>, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? {
|
||||
|
||||
return self.mainContext.queryAggregate(entity, function: function, queryClauses)
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to query from a <\(self.dynamicType)> outside the main queue.")
|
||||
|
||||
return self.mainContext.queryAttributes(entity, selectClause, queryClauses)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -25,6 +25,7 @@
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
import GCDKit
|
||||
|
||||
|
||||
// MARK: - DataStack
|
||||
@@ -40,6 +41,8 @@ public extension DataStack {
|
||||
*/
|
||||
public func beginAsynchronous(closure: (transaction: AsynchronousDataTransaction) -> Void) {
|
||||
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to begin a transaction from a <\(self.dynamicType)> outside the main queue.")
|
||||
|
||||
AsynchronousDataTransaction(
|
||||
mainContext: self.rootSavingContext,
|
||||
queue: self.childTransactionQueue,
|
||||
@@ -54,6 +57,8 @@ public extension DataStack {
|
||||
*/
|
||||
public func beginSynchronous(closure: (transaction: SynchronousDataTransaction) -> Void) -> SaveResult? {
|
||||
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to begin a transaction from a <\(self.dynamicType)> outside the main queue.")
|
||||
|
||||
return SynchronousDataTransaction(
|
||||
mainContext: self.rootSavingContext,
|
||||
queue: self.childTransactionQueue,
|
||||
@@ -67,6 +72,8 @@ public extension DataStack {
|
||||
*/
|
||||
public func beginDetached() -> DetachedDataTransaction {
|
||||
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to begin a transaction from a <\(self.dynamicType)> outside the main queue.")
|
||||
|
||||
return DetachedDataTransaction(
|
||||
mainContext: self.rootSavingContext,
|
||||
queue: .Main)
|
||||
|
||||
@@ -105,7 +105,7 @@ public final class DataStack {
|
||||
var error: NSError?
|
||||
|
||||
var store: NSPersistentStore?
|
||||
coordinator.performSynchronously {
|
||||
coordinator.performBlockAndWait {
|
||||
|
||||
store = coordinator.addPersistentStoreWithType(
|
||||
NSInMemoryStoreType,
|
||||
@@ -203,7 +203,7 @@ public final class DataStack {
|
||||
|
||||
var store: NSPersistentStore?
|
||||
var persistentStoreError: NSError?
|
||||
coordinator.performSynchronously {
|
||||
coordinator.performBlockAndWait {
|
||||
|
||||
store = coordinator.addPersistentStoreWithType(
|
||||
NSSQLiteStoreType,
|
||||
@@ -238,7 +238,7 @@ public final class DataStack {
|
||||
error: nil)
|
||||
|
||||
var store: NSPersistentStore?
|
||||
coordinator.performSynchronously {
|
||||
coordinator.performBlockAndWait {
|
||||
|
||||
store = coordinator.addPersistentStoreWithType(
|
||||
NSSQLiteStoreType,
|
||||
|
||||
@@ -41,7 +41,7 @@ public final class DetachedDataTransaction: BaseDataTransaction {
|
||||
*/
|
||||
public func commit(completion: (result: SaveResult) -> Void) {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to commit a <\(self.dynamicType)> outside a transaction queue.")
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to commit a <\(self.dynamicType)> outside its designated queue.")
|
||||
|
||||
self.context.saveAsynchronouslyWithCompletion { (result) -> Void in
|
||||
|
||||
|
||||
@@ -29,7 +29,4 @@ import CoreData
|
||||
|
||||
// MARK: - FetchClause
|
||||
|
||||
public protocol FetchClause {
|
||||
|
||||
func applyToFetchRequest(fetchRequest: NSFetchRequest)
|
||||
}
|
||||
public protocol FetchClause: QueryClause { }
|
||||
|
||||
80
HardcoreData/GroupBy.swift
Normal file
80
HardcoreData/GroupBy.swift
Normal file
@@ -0,0 +1,80 @@
|
||||
//
|
||||
// GroupBy.swift
|
||||
// HardcoreData
|
||||
//
|
||||
// Copyright (c) 2015 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
|
||||
// MARK: - GroupBy
|
||||
|
||||
public struct GroupBy: QueryClause {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
public init(_ keyPaths: [KeyPath]) {
|
||||
|
||||
self.keyPaths = keyPaths
|
||||
}
|
||||
|
||||
public init() {
|
||||
|
||||
self.init([])
|
||||
}
|
||||
|
||||
public init(_ keyPath: KeyPath, _ subKeyPaths: KeyPath...) {
|
||||
|
||||
self.init([keyPath] + subKeyPaths)
|
||||
}
|
||||
|
||||
public let keyPaths: [KeyPath]
|
||||
|
||||
|
||||
// MARK: QueryClause
|
||||
|
||||
public func applyToFetchRequest(fetchRequest: NSFetchRequest) {
|
||||
|
||||
if fetchRequest.propertiesToGroupBy != nil {
|
||||
|
||||
HardcoreData.log(.Warning, message: "An existing \"propertiesToGroupBy\" for the <\(NSFetchRequest.self)> was overwritten by <\(self.dynamicType)> query clause.")
|
||||
}
|
||||
|
||||
fetchRequest.propertiesToGroupBy = self.keyPaths
|
||||
|
||||
// let entityDescription = fetchRequest.entity!
|
||||
// let propertyMapping = entityDescription.propertiesByName
|
||||
// fetchRequest.propertiesToGroupBy = self.keyPaths.reduce([AnyObject]()) { (var properties, keyPath) -> [AnyObject] in
|
||||
//
|
||||
// if let propertyDescription: AnyObject = propertyMapping[keyPath] {
|
||||
//
|
||||
// properties.append(propertyDescription)
|
||||
// }
|
||||
// else {
|
||||
//
|
||||
// HardcoreData.log(.Warning, message: "The property \"\(keyPath)\" does not exist in entity <\(entityDescription.managedObjectClassName)> and will be ignored by <\(self.dynamicType)> query clause.")
|
||||
// }
|
||||
//
|
||||
// return properties
|
||||
// }
|
||||
}
|
||||
}
|
||||
@@ -61,6 +61,26 @@ public extension HardcoreData {
|
||||
return self.defaultStack.fetchCount(entity, queryClauses)
|
||||
}
|
||||
|
||||
public static func fetchObjectID<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> NSManagedObjectID? {
|
||||
|
||||
return self.defaultStack.fetchObjectID(entity, queryClauses)
|
||||
}
|
||||
|
||||
public static func fetchObjectID<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> NSManagedObjectID? {
|
||||
|
||||
return self.defaultStack.fetchObjectID(entity, queryClauses)
|
||||
}
|
||||
|
||||
public static func fetchObjectIDs<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> [NSManagedObjectID]? {
|
||||
|
||||
return self.defaultStack.fetchObjectIDs(entity, queryClauses)
|
||||
}
|
||||
|
||||
public static func fetchObjectIDs<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> [NSManagedObjectID]? {
|
||||
|
||||
return self.defaultStack.fetchObjectIDs(entity, queryClauses)
|
||||
}
|
||||
|
||||
public static func deleteAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> Int? {
|
||||
|
||||
return self.defaultStack.deleteAll(entity, queryClauses)
|
||||
@@ -71,25 +91,23 @@ public extension HardcoreData {
|
||||
return self.defaultStack.deleteAll(entity, queryClauses)
|
||||
}
|
||||
|
||||
public static func queryAggregate<T: NSManagedObject>(entity: T.Type, function: AggregateFunction, _ queryClauses: FetchClause...) -> Int? {
|
||||
public static func queryValue<T: NSManagedObject, U: SelectValueResultType>(entity: T.Type, _ selectClause: Select<U>, _ queryClauses: FetchClause...) -> U? {
|
||||
|
||||
let result = self.defaultStack.queryAggregate(entity, function: function, queryClauses)
|
||||
return result
|
||||
return self.defaultStack.queryValue(entity, selectClause, queryClauses)
|
||||
}
|
||||
|
||||
public static func queryAggregate<T: NSManagedObject>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> Int? {
|
||||
public static func queryValue<T: NSManagedObject, U: SelectValueResultType>(entity: T.Type, _ selectClause: Select<U>, _ queryClauses: [FetchClause]) -> U? {
|
||||
|
||||
let result = self.defaultStack.queryAggregate(entity, function: function, queryClauses)
|
||||
return result
|
||||
return self.defaultStack.queryValue(entity, selectClause, queryClauses)
|
||||
}
|
||||
|
||||
public static func queryAggregate<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: FetchClause...) -> U? {
|
||||
public static func queryAttributes<T: NSManagedObject>(entity: T.Type, _ selectClause: Select<NSDictionary>, _ queryClauses: QueryClause...) -> [[NSString: AnyObject]]? {
|
||||
|
||||
return self.defaultStack.queryAggregate(entity, function: function, queryClauses)
|
||||
return self.defaultStack.queryAttributes(entity, selectClause, queryClauses)
|
||||
}
|
||||
|
||||
public static func queryAggregate<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> U? {
|
||||
public static func queryAttributes<T: NSManagedObject>(entity: T.Type, _ selectClause: Select<NSDictionary>, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? {
|
||||
|
||||
return self.defaultStack.queryAggregate(entity, function: function, queryClauses)
|
||||
return self.defaultStack.queryAttributes(entity, selectClause, queryClauses)
|
||||
}
|
||||
}
|
||||
27
HardcoreData/ManagedObjectController.swift
Normal file
27
HardcoreData/ManagedObjectController.swift
Normal file
@@ -0,0 +1,27 @@
|
||||
//
|
||||
// ManagedObjectController.swift
|
||||
// HardcoreData
|
||||
//
|
||||
// Copyright (c) 2015 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
125
HardcoreData/ManagedObjectListController.swift
Normal file
125
HardcoreData/ManagedObjectListController.swift
Normal file
@@ -0,0 +1,125 @@
|
||||
//
|
||||
// ManagedObjectListController.swift
|
||||
// HardcoreData
|
||||
//
|
||||
// Copyright (c) 2015 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
import GCDKit
|
||||
|
||||
|
||||
// MARK: - ManagedObjectListController
|
||||
|
||||
public final class ManagedObjectListController<T: NSManagedObject>: NSFetchedResultsControllerDelegate {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
public typealias EntityType = T
|
||||
|
||||
public func addObserver<U: ManagedObjectListObserver where U.EntityType == EntityType>(observer: U) {
|
||||
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to add a <\(U.self)> outside the main queue.")
|
||||
|
||||
self.observers.addObject(observer)
|
||||
}
|
||||
|
||||
public func removeObserver<U: ManagedObjectListObserver where U.EntityType == EntityType>(observer: U) {
|
||||
|
||||
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to remove a <\(U.self)> outside the main queue.")
|
||||
|
||||
self.observers.removeObject(observer)
|
||||
}
|
||||
|
||||
|
||||
// MARK: NSFetchedResultsControllerDelegate
|
||||
|
||||
private func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
|
||||
|
||||
}
|
||||
|
||||
private func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
|
||||
|
||||
}
|
||||
|
||||
private func controllerWillChangeContent(controller: NSFetchedResultsController) {
|
||||
|
||||
}
|
||||
|
||||
private func controllerDidChangeContent(controller: NSFetchedResultsController) {
|
||||
|
||||
}
|
||||
|
||||
private func controller(controller: NSFetchedResultsController, sectionIndexTitleForSectionName sectionName: String?) -> String? {
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal init(dataStack: DataStack, entity: T.Type, sectionNameKeyPath: KeyPath?, cacheResults: Bool, queryClauses: [FetchClause]) {
|
||||
|
||||
let context = dataStack.mainContext
|
||||
|
||||
let fetchRequest = NSFetchRequest()
|
||||
fetchRequest.entity = context.entityDescriptionForEntityClass(entity)
|
||||
fetchRequest.fetchLimit = 0
|
||||
fetchRequest.resultType = .ManagedObjectResultType
|
||||
|
||||
for clause in queryClauses {
|
||||
|
||||
clause.applyToFetchRequest(fetchRequest)
|
||||
}
|
||||
|
||||
let fetchedResultsController = NSFetchedResultsController(
|
||||
fetchRequest: fetchRequest,
|
||||
managedObjectContext: context,
|
||||
sectionNameKeyPath: sectionNameKeyPath,
|
||||
cacheName: (cacheResults
|
||||
? "\(ManagedObjectListController<T>.self).\(NSUUID())"
|
||||
: nil)
|
||||
)
|
||||
|
||||
|
||||
self.fetchedResultsController = fetchedResultsController
|
||||
self.parentStack = dataStack
|
||||
self.observers = NSHashTable.weakObjectsHashTable()
|
||||
|
||||
fetchedResultsController.delegate = self
|
||||
|
||||
var error: NSError?
|
||||
if !fetchedResultsController.performFetch(&error) {
|
||||
|
||||
HardcoreData.handleError(
|
||||
error ?? NSError(hardcoreDataErrorCode: .UnknownError),
|
||||
"Failed to perform fetch on \(NSFetchedResultsController.self)")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let fetchedResultsController: NSFetchedResultsController
|
||||
private weak var parentStack: DataStack?
|
||||
private let observers: NSHashTable
|
||||
}
|
||||
80
HardcoreData/ManagedObjectListObserver.swift
Normal file
80
HardcoreData/ManagedObjectListObserver.swift
Normal file
@@ -0,0 +1,80 @@
|
||||
//
|
||||
// ManagedObjectListObserver.swift
|
||||
// HardcoreData
|
||||
//
|
||||
// Copyright (c) 2015 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - ManagedObjectListObserver
|
||||
|
||||
public protocol ManagedObjectListObserver: class {
|
||||
|
||||
typealias EntityType: NSManagedObject
|
||||
|
||||
func managedObjectListWillChange(listController: ManagedObjectListController<EntityType>)
|
||||
|
||||
|
||||
// func managedObjectList(listController: ManagedObjectListController<EntityType>, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int)
|
||||
//
|
||||
// func managedObjectList(listController: ManagedObjectListController<EntityType>, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int)
|
||||
//
|
||||
// func managedObjectList(listController: ManagedObjectListController<EntityType>, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int)
|
||||
//
|
||||
// func managedObjectList(listController: ManagedObjectListController<EntityType>, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int)
|
||||
|
||||
|
||||
func managedObjectList(listController: ManagedObjectListController<EntityType>, didInsertObject object: EntityType, toItemIndex itemIndex: Int)
|
||||
|
||||
func managedObjectList(listController: ManagedObjectListController<EntityType>, didDeleteObject object: EntityType, fromItemIndex itemIndex: Int)
|
||||
|
||||
func managedObjectList(listController: ManagedObjectListController<EntityType>, didUpdateObject object: EntityType, atItemIndex itemIndex: Int)
|
||||
|
||||
func managedObjectList(listController: ManagedObjectListController<EntityType>, didMoveObject object: EntityType, fromItemIndex: Int, toItemIndex: Int)
|
||||
|
||||
|
||||
func managedObjectListDidChange(listController: ManagedObjectListController<EntityType>)
|
||||
|
||||
|
||||
// private func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// private func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// private func controllerWillChangeContent(controller: NSFetchedResultsController) {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// private func controllerDidChangeContent(controller: NSFetchedResultsController) {
|
||||
//
|
||||
// }
|
||||
//
|
||||
// private func controller(controller: NSFetchedResultsController, sectionIndexTitleForSectionName sectionName: String?) -> String? {
|
||||
//
|
||||
// return nil
|
||||
// }
|
||||
}
|
||||
13
HardcoreData/ManagedObjectListSectionInfo.swift
Normal file
13
HardcoreData/ManagedObjectListSectionInfo.swift
Normal file
@@ -0,0 +1,13 @@
|
||||
//
|
||||
// ManagedObjectListSectionInfo.swift
|
||||
// HardcoreData
|
||||
//
|
||||
// Created by John Rommel Estropia on 2015/03/11.
|
||||
// Copyright (c) 2015 John Rommel Estropia. All rights reserved.
|
||||
//
|
||||
|
||||
import UIKit
|
||||
|
||||
class ManagedObjectListSectionInfo: NSObject {
|
||||
|
||||
}
|
||||
@@ -133,6 +133,74 @@ internal extension NSManagedObjectContext {
|
||||
return count
|
||||
}
|
||||
|
||||
internal func fetchObjectID<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> NSManagedObjectID? {
|
||||
|
||||
return self.fetchObjectID(entity, queryClauses)
|
||||
}
|
||||
|
||||
internal func fetchObjectID<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> NSManagedObjectID? {
|
||||
|
||||
let fetchRequest = NSFetchRequest()
|
||||
fetchRequest.entity = self.entityDescriptionForEntityClass(entity)
|
||||
fetchRequest.fetchLimit = 1
|
||||
fetchRequest.resultType = .ManagedObjectIDResultType
|
||||
|
||||
for clause in queryClauses {
|
||||
|
||||
clause.applyToFetchRequest(fetchRequest)
|
||||
}
|
||||
|
||||
var fetchResults: [NSManagedObjectID]?
|
||||
var error: NSError?
|
||||
self.performBlockAndWait {
|
||||
|
||||
fetchResults = self.executeFetchRequest(fetchRequest, error: &error) as? [NSManagedObjectID]
|
||||
}
|
||||
if fetchResults == nil {
|
||||
|
||||
HardcoreData.handleError(
|
||||
error ?? NSError(hardcoreDataErrorCode: .UnknownError),
|
||||
"Failed executing fetch request.")
|
||||
return nil
|
||||
}
|
||||
|
||||
return fetchResults?.first
|
||||
}
|
||||
|
||||
internal func fetchObjectIDs<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> [NSManagedObjectID]? {
|
||||
|
||||
return self.fetchObjectIDs(entity, queryClauses)
|
||||
}
|
||||
|
||||
internal func fetchObjectIDs<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> [NSManagedObjectID]? {
|
||||
|
||||
let fetchRequest = NSFetchRequest()
|
||||
fetchRequest.entity = self.entityDescriptionForEntityClass(entity)
|
||||
fetchRequest.fetchLimit = 0
|
||||
fetchRequest.resultType = .ManagedObjectIDResultType
|
||||
|
||||
for clause in queryClauses {
|
||||
|
||||
clause.applyToFetchRequest(fetchRequest)
|
||||
}
|
||||
|
||||
var fetchResults: [NSManagedObjectID]?
|
||||
var error: NSError?
|
||||
self.performBlockAndWait {
|
||||
|
||||
fetchResults = self.executeFetchRequest(fetchRequest, error: &error) as? [NSManagedObjectID]
|
||||
}
|
||||
if fetchResults == nil {
|
||||
|
||||
HardcoreData.handleError(
|
||||
error ?? NSError(hardcoreDataErrorCode: .UnknownError),
|
||||
"Failed executing fetch request.")
|
||||
return nil
|
||||
}
|
||||
|
||||
return fetchResults
|
||||
}
|
||||
|
||||
internal func deleteAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> Int? {
|
||||
|
||||
return self.deleteAll(entity, queryClauses)
|
||||
@@ -178,38 +246,18 @@ internal extension NSManagedObjectContext {
|
||||
return numberOfDeletedObjects
|
||||
}
|
||||
|
||||
internal func queryAggregate<T: NSManagedObject>(entity: T.Type, function: AggregateFunction, _ queryClauses: FetchClause...) -> Int? {
|
||||
internal func queryValue<T: NSManagedObject, U: SelectValueResultType>(entity: T.Type, _ selectClause: Select<U>, _ queryClauses: FetchClause...) -> U? {
|
||||
|
||||
return self.queryAggregateImplementation(entity, function: function, queryClauses)
|
||||
return self.queryValue(entity, selectClause, queryClauses)
|
||||
}
|
||||
|
||||
internal func queryAggregate<T: NSManagedObject>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> Int? {
|
||||
|
||||
return self.queryAggregateImplementation(entity, function: function, queryClauses)
|
||||
}
|
||||
|
||||
internal func queryAggregate<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: FetchClause...) -> U? {
|
||||
|
||||
return self.queryAggregateImplementation(entity, function: function, queryClauses)
|
||||
}
|
||||
|
||||
internal func queryAggregate<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> U? {
|
||||
|
||||
return self.queryAggregateImplementation(entity, function: function, queryClauses)
|
||||
}
|
||||
|
||||
internal func queryAggregateImplementation<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> U? {
|
||||
|
||||
let expressionDescription = NSExpressionDescription()
|
||||
expressionDescription.name = "queryAggregate"
|
||||
expressionDescription.expressionResultType = U.attributeType
|
||||
expressionDescription.expression = function.createExpression()
|
||||
internal func queryValue<T: NSManagedObject, U: SelectValueResultType>(entity: T.Type, _ selectClause: Select<U>, _ queryClauses: [FetchClause]) -> U? {
|
||||
|
||||
let fetchRequest = NSFetchRequest()
|
||||
fetchRequest.entity = self.entityDescriptionForEntityClass(entity)
|
||||
fetchRequest.resultType = .DictionaryResultType
|
||||
fetchRequest.propertiesToFetch = [expressionDescription]
|
||||
fetchRequest.includesPendingChanges = false
|
||||
fetchRequest.fetchLimit = 0
|
||||
|
||||
selectClause.applyToFetchRequest(fetchRequest)
|
||||
|
||||
for clause in queryClauses {
|
||||
|
||||
@@ -222,19 +270,54 @@ internal extension NSManagedObjectContext {
|
||||
|
||||
fetchResults = self.executeFetchRequest(fetchRequest, error: &error)
|
||||
}
|
||||
if fetchResults == nil {
|
||||
if let fetchResults = fetchResults {
|
||||
|
||||
HardcoreData.handleError(
|
||||
error ?? NSError(hardcoreDataErrorCode: .UnknownError),
|
||||
"Failed executing fetch request.")
|
||||
if let rawResult = fetchResults.first as? NSDictionary,
|
||||
let rawObject: AnyObject = rawResult[selectClause.keyPathForFirstSelectTerm()] {
|
||||
|
||||
return Select<U>.ReturnType.fromResultObject(rawObject)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
if let result: AnyObject = (fetchResults?.first as! [String: AnyObject])[expressionDescription.name] {
|
||||
HardcoreData.handleError(
|
||||
error ?? NSError(hardcoreDataErrorCode: .UnknownError),
|
||||
"Failed executing fetch request.")
|
||||
return nil
|
||||
}
|
||||
|
||||
internal func queryAttributes<T: NSManagedObject>(entity: T.Type, _ selectClause: Select<NSDictionary>, _ queryClauses: QueryClause...) -> [[NSString: AnyObject]]? {
|
||||
|
||||
return self.queryAttributes(entity, selectClause, queryClauses)
|
||||
}
|
||||
|
||||
internal func queryAttributes<T: NSManagedObject>(entity: T.Type, _ selectClause: Select<NSDictionary>, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? {
|
||||
|
||||
let fetchRequest = NSFetchRequest()
|
||||
fetchRequest.entity = self.entityDescriptionForEntityClass(entity)
|
||||
fetchRequest.fetchLimit = 0
|
||||
|
||||
selectClause.applyToFetchRequest(fetchRequest)
|
||||
|
||||
for clause in queryClauses {
|
||||
|
||||
return U.fromResultObject(result)
|
||||
clause.applyToFetchRequest(fetchRequest)
|
||||
}
|
||||
|
||||
var fetchResults: [AnyObject]?
|
||||
var error: NSError?
|
||||
self.performBlockAndWait {
|
||||
|
||||
fetchResults = self.executeFetchRequest(fetchRequest, error: &error)
|
||||
}
|
||||
if let fetchResults = fetchResults {
|
||||
|
||||
return Select<NSDictionary>.ReturnType.fromResultObjects(fetchResults)
|
||||
}
|
||||
|
||||
HardcoreData.handleError(
|
||||
error ?? NSError(hardcoreDataErrorCode: .UnknownError),
|
||||
"Failed executing fetch request.")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -1,8 +1,8 @@
|
||||
//
|
||||
// NSPersistentStoreCoordinator+HardcoreData.swift
|
||||
// QueryClause.swift
|
||||
// HardcoreData
|
||||
//
|
||||
// Copyright (c) 2014 John Rommel Estropia
|
||||
// Copyright (c) 2015 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
@@ -27,23 +27,9 @@ import Foundation
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - NSPersistentStoreCoordinator
|
||||
// MARK: - QueryClause
|
||||
|
||||
internal extension NSPersistentStoreCoordinator {
|
||||
public protocol QueryClause {
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal func performSynchronously(closure: () -> Void) {
|
||||
|
||||
if self.respondsToSelector("performBlockAndWait:") {
|
||||
|
||||
self.performBlockAndWait(closure)
|
||||
}
|
||||
else {
|
||||
|
||||
self.lock()
|
||||
autoreleasepool(closure)
|
||||
self.unlock()
|
||||
}
|
||||
}
|
||||
}
|
||||
func applyToFetchRequest(fetchRequest: NSFetchRequest)
|
||||
}
|
||||
515
HardcoreData/Select.swift
Normal file
515
HardcoreData/Select.swift
Normal file
@@ -0,0 +1,515 @@
|
||||
//
|
||||
// Select.swift
|
||||
// HardcoreData
|
||||
//
|
||||
// Copyright (c) 2015 John Rommel Estropia
|
||||
//
|
||||
// Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
// of this software and associated documentation files (the "Software"), to deal
|
||||
// in the Software without restriction, including without limitation the rights
|
||||
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
// copies of the Software, and to permit persons to whom the Software is
|
||||
// furnished to do so, subject to the following conditions:
|
||||
//
|
||||
// The above copyright notice and this permission notice shall be included in all
|
||||
// copies or substantial portions of the Software.
|
||||
//
|
||||
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
// SOFTWARE.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import CoreData
|
||||
|
||||
|
||||
// MARK: - SelectResultType
|
||||
|
||||
public protocol SelectResultType { }
|
||||
|
||||
|
||||
// MARK: - SelectValueResultType
|
||||
|
||||
public protocol SelectValueResultType: SelectResultType {
|
||||
|
||||
static func fromResultObject(result: AnyObject) -> Self?
|
||||
}
|
||||
|
||||
|
||||
// MARK: - SelectAttributesResultType
|
||||
|
||||
public protocol SelectAttributesResultType: SelectResultType {
|
||||
|
||||
static func fromResultObjects(result: [AnyObject]) -> [[NSString: AnyObject]]
|
||||
}
|
||||
|
||||
|
||||
// MARK: - SelectTerm
|
||||
|
||||
public enum SelectTerm: StringLiteralConvertible {
|
||||
|
||||
case Attribute(KeyPath)
|
||||
case Aggregate(function: String, KeyPath, As: String)
|
||||
|
||||
public static func Average(keyPath: KeyPath, As alias: KeyPath? = nil) -> SelectTerm {
|
||||
|
||||
return .Aggregate(
|
||||
function: "average:",
|
||||
keyPath,
|
||||
As: alias ?? "average(\(keyPath))"
|
||||
)
|
||||
}
|
||||
|
||||
public static func Count(keyPath: KeyPath, As alias: KeyPath? = nil) -> SelectTerm {
|
||||
|
||||
return .Aggregate(
|
||||
function: "count:",
|
||||
keyPath,
|
||||
As: alias ?? "count(\(keyPath))"
|
||||
)
|
||||
}
|
||||
|
||||
public static func Maximum(keyPath: KeyPath, As alias: KeyPath? = nil) -> SelectTerm {
|
||||
|
||||
return .Aggregate(
|
||||
function: "max:",
|
||||
keyPath,
|
||||
As: alias ?? "max(\(keyPath))"
|
||||
)
|
||||
}
|
||||
|
||||
public static func Median(keyPath: KeyPath, As alias: KeyPath? = nil) -> SelectTerm {
|
||||
|
||||
return .Aggregate(
|
||||
function: "median:",
|
||||
keyPath, As:
|
||||
alias ?? "median(\(keyPath))"
|
||||
)
|
||||
}
|
||||
|
||||
public static func Minimum(keyPath: KeyPath, As alias: KeyPath? = nil) -> SelectTerm {
|
||||
|
||||
return .Aggregate(
|
||||
function: "min:",
|
||||
keyPath,
|
||||
As: alias ?? "min(\(keyPath))"
|
||||
)
|
||||
}
|
||||
|
||||
public static func StandardDeviation(keyPath: KeyPath, As alias: KeyPath? = nil) -> SelectTerm {
|
||||
|
||||
return .Aggregate(
|
||||
function: "stddev:",
|
||||
keyPath,
|
||||
As: alias ?? "stddev(\(keyPath))"
|
||||
)
|
||||
}
|
||||
|
||||
public static func Sum(keyPath: KeyPath, As alias: KeyPath? = nil) -> SelectTerm {
|
||||
|
||||
return .Aggregate(
|
||||
function: "sum:",
|
||||
keyPath,
|
||||
As: alias ?? "sum(\(keyPath))"
|
||||
)
|
||||
}
|
||||
|
||||
public init(stringLiteral value: KeyPath) {
|
||||
|
||||
self = .Attribute(value)
|
||||
}
|
||||
|
||||
public init(unicodeScalarLiteral value: KeyPath) {
|
||||
|
||||
self = .Attribute(value)
|
||||
}
|
||||
|
||||
public init(extendedGraphemeClusterLiteral value: KeyPath) {
|
||||
|
||||
self = .Attribute(value)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Select
|
||||
|
||||
public struct Select<T: SelectResultType> {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
public typealias ReturnType = T
|
||||
|
||||
public init(_ selectTerm: SelectTerm, _ selectTerms: SelectTerm...) {
|
||||
|
||||
self.selectTerms = [selectTerm] + selectTerms
|
||||
}
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal func applyToFetchRequest(fetchRequest: NSFetchRequest) {
|
||||
|
||||
if fetchRequest.propertiesToFetch != nil {
|
||||
|
||||
HardcoreData.log(.Warning, message: "An existing \"propertiesToFetch\" for the <\(NSFetchRequest.self)> was overwritten by <\(self.dynamicType)> query clause.")
|
||||
}
|
||||
|
||||
fetchRequest.includesPendingChanges = false
|
||||
fetchRequest.resultType = .DictionaryResultType
|
||||
|
||||
let entityDescription = fetchRequest.entity!
|
||||
let propertiesByName = entityDescription.propertiesByName
|
||||
let attributesByName = entityDescription.attributesByName
|
||||
|
||||
var propertiesToFetch = [AnyObject]()
|
||||
for term in self.selectTerms {
|
||||
|
||||
switch term {
|
||||
|
||||
case .Attribute(let keyPath):
|
||||
if let propertyDescription = propertiesByName[keyPath] as? NSPropertyDescription {
|
||||
|
||||
propertiesToFetch.append(propertyDescription)
|
||||
}
|
||||
else {
|
||||
|
||||
HardcoreData.log(.Warning, message: "The property \"\(keyPath)\" does not exist in entity <\(entityDescription.managedObjectClassName)> and will be ignored by <\(self.dynamicType)> query clause.")
|
||||
}
|
||||
|
||||
case .Aggregate(let function, let keyPath, let alias):
|
||||
if let attributeDescription = attributesByName[keyPath] as? NSAttributeDescription {
|
||||
|
||||
let expressionDescription = NSExpressionDescription()
|
||||
expressionDescription.name = alias
|
||||
expressionDescription.expressionResultType = attributeDescription.attributeType
|
||||
expressionDescription.expression = NSExpression(
|
||||
forFunction: function,
|
||||
arguments: [NSExpression(forKeyPath: keyPath)]
|
||||
)
|
||||
|
||||
propertiesToFetch.append(expressionDescription)
|
||||
}
|
||||
else {
|
||||
|
||||
HardcoreData.log(.Warning, message: "The attribute \"\(keyPath)\" does not exist in entity <\(entityDescription.managedObjectClassName)> and will be ignored by <\(self.dynamicType)> query clause.")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fetchRequest.propertiesToFetch = propertiesToFetch
|
||||
}
|
||||
|
||||
internal func keyPathForFirstSelectTerm() -> KeyPath {
|
||||
|
||||
switch self.selectTerms.first! {
|
||||
|
||||
case .Attribute(let keyPath):
|
||||
return keyPath
|
||||
|
||||
case .Aggregate(_, _, let alias):
|
||||
return alias
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let selectTerms: [SelectTerm]
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Bool: SelectValueResultType
|
||||
|
||||
extension Bool: SelectValueResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .BooleanAttributeType
|
||||
}
|
||||
|
||||
public static func fromResultObject(result: AnyObject) -> Bool? {
|
||||
|
||||
return (result as? NSNumber)?.boolValue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Int8: SelectValueResultType
|
||||
|
||||
extension Int8: SelectValueResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .Integer64AttributeType
|
||||
}
|
||||
|
||||
public static func fromResultObject(result: AnyObject) -> Int8? {
|
||||
|
||||
if let value = (result as? NSNumber)?.longLongValue {
|
||||
|
||||
return numericCast(value) as Int8
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Int16: SelectValueResultType
|
||||
|
||||
extension Int16: SelectValueResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .Integer64AttributeType
|
||||
}
|
||||
|
||||
public static func fromResultObject(result: AnyObject) -> Int16? {
|
||||
|
||||
if let value = (result as? NSNumber)?.longLongValue {
|
||||
|
||||
return numericCast(value) as Int16
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Int32: SelectValueResultType
|
||||
|
||||
extension Int32: SelectValueResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .Integer64AttributeType
|
||||
}
|
||||
|
||||
public static func fromResultObject(result: AnyObject) -> Int32? {
|
||||
|
||||
if let value = (result as? NSNumber)?.longLongValue {
|
||||
|
||||
return numericCast(value) as Int32
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Int64: SelectValueResultType
|
||||
|
||||
extension Int64: SelectValueResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .Integer64AttributeType
|
||||
}
|
||||
|
||||
public static func fromResultObject(result: AnyObject) -> Int64? {
|
||||
|
||||
return (result as? NSNumber)?.longLongValue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Int: SelectValueResultType
|
||||
|
||||
extension Int: SelectValueResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .Integer64AttributeType
|
||||
}
|
||||
|
||||
public static func fromResultObject(result: AnyObject) -> Int? {
|
||||
|
||||
if let value = (result as? NSNumber)?.longLongValue {
|
||||
|
||||
return numericCast(value) as Int
|
||||
}
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Double : SelectValueResultType
|
||||
|
||||
extension Double: SelectValueResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .DoubleAttributeType
|
||||
}
|
||||
|
||||
public static func fromResultObject(result: AnyObject) -> Double? {
|
||||
|
||||
return (result as? NSNumber)?.doubleValue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Float: SelectValueResultType
|
||||
|
||||
extension Float: SelectValueResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .FloatAttributeType
|
||||
}
|
||||
|
||||
public static func fromResultObject(result: AnyObject) -> Float? {
|
||||
|
||||
return (result as? NSNumber)?.floatValue
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - String: SelectValueResultType
|
||||
|
||||
extension String: SelectValueResultType {
|
||||
|
||||
public static var attributeType: NSAttributeType {
|
||||
|
||||
return .StringAttributeType
|
||||
}
|
||||
|
||||
public static func fromResultObject(result: AnyObject) -> String? {
|
||||
|
||||
return result as? NSString as? String
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - NSNumber: SelectValueResultType
|
||||
|
||||
extension NSNumber: SelectValueResultType {
|
||||
|
||||
public class var attributeType: NSAttributeType {
|
||||
|
||||
return .Integer64AttributeType
|
||||
}
|
||||
|
||||
public class func fromResultObject(result: AnyObject) -> Self? {
|
||||
|
||||
func forceCast<T: NSNumber>(object: AnyObject) -> T? {
|
||||
|
||||
return (object as? T)
|
||||
}
|
||||
return forceCast(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - NSString: SelectValueResultType
|
||||
|
||||
extension NSString: SelectValueResultType {
|
||||
|
||||
public class var attributeType: NSAttributeType {
|
||||
|
||||
return .StringAttributeType
|
||||
}
|
||||
|
||||
public class func fromResultObject(result: AnyObject) -> Self? {
|
||||
|
||||
func forceCast<T: NSString>(object: AnyObject) -> T? {
|
||||
|
||||
return (object as? T)
|
||||
}
|
||||
return forceCast(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - NSDecimalNumber: SelectValueResultType
|
||||
|
||||
extension NSDecimalNumber: SelectValueResultType {
|
||||
|
||||
public override class var attributeType: NSAttributeType {
|
||||
|
||||
return .DecimalAttributeType
|
||||
}
|
||||
|
||||
public override class func fromResultObject(result: AnyObject) -> Self? {
|
||||
|
||||
func forceCast<T: NSDecimalNumber>(object: AnyObject) -> T? {
|
||||
|
||||
return (object as? T)
|
||||
}
|
||||
return forceCast(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - NSDate: SelectValueResultType
|
||||
|
||||
extension NSDate: SelectValueResultType {
|
||||
|
||||
public class var attributeType: NSAttributeType {
|
||||
|
||||
return .DateAttributeType
|
||||
}
|
||||
|
||||
public class func fromResultObject(result: AnyObject) -> Self? {
|
||||
|
||||
func forceCast<T: NSDate>(object: AnyObject) -> T? {
|
||||
|
||||
return (object as? T)
|
||||
}
|
||||
return forceCast(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - NSData: SelectValueResultType
|
||||
|
||||
extension NSData: SelectValueResultType {
|
||||
|
||||
public class var attributeType: NSAttributeType {
|
||||
|
||||
return .BinaryDataAttributeType
|
||||
}
|
||||
|
||||
public class func fromResultObject(result: AnyObject) -> Self? {
|
||||
|
||||
func forceCast<T: NSData>(object: AnyObject) -> T? {
|
||||
|
||||
return (object as? T)
|
||||
}
|
||||
return forceCast(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - NSManagedObjectID: SelectValueResultType
|
||||
|
||||
extension NSManagedObjectID: SelectValueResultType {
|
||||
|
||||
public class var attributeType: NSAttributeType {
|
||||
|
||||
return .ObjectIDAttributeType
|
||||
}
|
||||
|
||||
public class func fromResultObject(result: AnyObject) -> Self? {
|
||||
|
||||
func forceCast<T: NSManagedObjectID>(object: AnyObject) -> T? {
|
||||
|
||||
return (object as? T)
|
||||
}
|
||||
return forceCast(result)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: - NSManagedObjectID: SelectAttributesResultType
|
||||
|
||||
extension NSDictionary: SelectAttributesResultType {
|
||||
|
||||
// MARK: SelectAttributesResultType
|
||||
|
||||
public class func fromResultObjects(result: [AnyObject]) -> [[NSString: AnyObject]] {
|
||||
|
||||
return result as! [[NSString: AnyObject]]
|
||||
}
|
||||
}
|
||||
@@ -32,17 +32,17 @@ public func +(left: SortedBy, right: SortedBy) -> SortedBy {
|
||||
}
|
||||
|
||||
|
||||
// MARK: - AttributeName
|
||||
// MARK: - KeyPath
|
||||
|
||||
public typealias AttributeName = Selector
|
||||
public typealias KeyPath = String
|
||||
|
||||
|
||||
// MARK: - SortOrder
|
||||
|
||||
public enum SortOrder {
|
||||
|
||||
case Ascending(AttributeName)
|
||||
case Descending(AttributeName)
|
||||
case Ascending(KeyPath)
|
||||
case Descending(KeyPath)
|
||||
}
|
||||
|
||||
|
||||
@@ -69,21 +69,19 @@ public struct SortedBy: FetchClause {
|
||||
|
||||
public init(_ order: [SortOrder]) {
|
||||
|
||||
self.init(order.map { sortOrder -> NSSortDescriptor in
|
||||
|
||||
switch sortOrder {
|
||||
self.init(
|
||||
order.map { sortOrder -> NSSortDescriptor in
|
||||
|
||||
case .Ascending(let attributeName):
|
||||
return NSSortDescriptor(
|
||||
key: NSStringFromSelector(attributeName),
|
||||
ascending: true)
|
||||
|
||||
case .Descending(let attributeName):
|
||||
return NSSortDescriptor(
|
||||
key: NSStringFromSelector(attributeName),
|
||||
ascending: false)
|
||||
switch sortOrder {
|
||||
|
||||
case .Ascending(let keyPath):
|
||||
return NSSortDescriptor(key: keyPath, ascending: true)
|
||||
|
||||
case .Descending(let keyPath):
|
||||
return NSSortDescriptor(key: keyPath, ascending: false)
|
||||
}
|
||||
}
|
||||
})
|
||||
)
|
||||
}
|
||||
|
||||
public init(_ order: SortOrder, _ subOrder: SortOrder...) {
|
||||
|
||||
@@ -20,7 +20,7 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
|
||||
*/
|
||||
public func commitAndWait() {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to commit a <\(self.dynamicType)> outside a transaction queue.")
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to commit a <\(self.dynamicType)> outside its designated queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to commit a <\(self.dynamicType)> more than once.")
|
||||
|
||||
self.isCommitted = true
|
||||
@@ -35,6 +35,7 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
|
||||
*/
|
||||
public func beginSynchronous(closure: (transaction: SynchronousDataTransaction) -> Void) -> SaveResult? {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to begin a child transaction from a <\(self.dynamicType)> outside its designated queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to begin a child transaction from an already committed <\(self.dynamicType)>.")
|
||||
|
||||
return SynchronousDataTransaction(
|
||||
|
||||
@@ -68,16 +68,16 @@ public struct Where: FetchClause {
|
||||
self.init(NSPredicate(format: format, argumentArray: args))
|
||||
}
|
||||
|
||||
public init(_ format: String, argumentArray: [AnyObject]?) {
|
||||
public init(_ format: String, argumentArray: [NSObject]?) {
|
||||
|
||||
self.init(NSPredicate(format: format, argumentArray: argumentArray))
|
||||
}
|
||||
|
||||
public init(_ attributeName: AttributeName, isEqualTo value: NSObject?) {
|
||||
public init(_ keyPath: KeyPath, isEqualTo value: NSObject?) {
|
||||
|
||||
self.init(value == nil
|
||||
? NSPredicate(format: "\(attributeName) == nil")
|
||||
: NSPredicate(format: "\(attributeName) == %@", value!))
|
||||
? NSPredicate(format: "\(keyPath) == nil")
|
||||
: NSPredicate(format: "\(keyPath) == %@", value!))
|
||||
}
|
||||
|
||||
public let predicate: NSPredicate
|
||||
|
||||
Reference in New Issue
Block a user