mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-01-15 21:53:39 +01:00
Added aggregate function querying and deletion querying
This commit is contained in:
@@ -11,6 +11,9 @@
|
||||
2F03A54019C5C6DA005002A5 /* HardcoreDataTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F03A53F19C5C6DA005002A5 /* HardcoreDataTests.swift */; };
|
||||
2F03A54D19C5C872005002A5 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 2F03A54C19C5C872005002A5 /* CoreData.framework */; };
|
||||
2F291E2719C6D3CF007AF63F /* HardcoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = 2F291E2619C6D3CF007AF63F /* HardcoreData.swift */; };
|
||||
B54A9F031AA7640200AFEC05 /* AggregateFunction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54A9F021AA7640200AFEC05 /* AggregateFunction.swift */; };
|
||||
B54A9F051AA7644400AFEC05 /* AggregateResultType.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54A9F041AA7644400AFEC05 /* AggregateResultType.swift */; };
|
||||
B54A9F071AA7654400AFEC05 /* DataStack+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54A9F061AA7654400AFEC05 /* DataStack+Querying.swift */; };
|
||||
B57078B01A50392D007E33F2 /* FetchClause.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57078AF1A50392D007E33F2 /* FetchClause.swift */; };
|
||||
B582DF821A98B0E7003F09C6 /* HardcoreData+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B582DF811A98B0E7003F09C6 /* HardcoreData+Querying.swift */; };
|
||||
B582DF861A98B11B003F09C6 /* HardcoreData+Transaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B582DF851A98B11B003F09C6 /* HardcoreData+Transaction.swift */; };
|
||||
@@ -79,6 +82,9 @@
|
||||
2F03A53F19C5C6DA005002A5 /* HardcoreDataTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = HardcoreDataTests.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
|
||||
2F03A54C19C5C872005002A5 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = System/Library/Frameworks/CoreData.framework; sourceTree = SDKROOT; };
|
||||
2F291E2619C6D3CF007AF63F /* HardcoreData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; lineEnding = 0; path = HardcoreData.swift; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
|
||||
B54A9F021AA7640200AFEC05 /* AggregateFunction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AggregateFunction.swift; sourceTree = "<group>"; };
|
||||
B54A9F041AA7644400AFEC05 /* AggregateResultType.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AggregateResultType.swift; sourceTree = "<group>"; };
|
||||
B54A9F061AA7654400AFEC05 /* DataStack+Querying.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DataStack+Querying.swift"; sourceTree = "<group>"; };
|
||||
B57078AF1A50392D007E33F2 /* FetchClause.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchClause.swift; sourceTree = "<group>"; };
|
||||
B582DF811A98B0E7003F09C6 /* HardcoreData+Querying.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HardcoreData+Querying.swift"; sourceTree = "<group>"; };
|
||||
B582DF851A98B11B003F09C6 /* HardcoreData+Transaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HardcoreData+Transaction.swift"; sourceTree = "<group>"; };
|
||||
@@ -212,6 +218,14 @@
|
||||
name = Frameworks;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B54A9EFF1AA763D100AFEC05 /* Internal */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
B5F409EC1A8B200700A228EA /* NSManagedObjectContext+Querying.swift */,
|
||||
);
|
||||
name = Internal;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
B595CAC01A9A0AC4009A397F /* Setting Up */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -297,9 +311,12 @@
|
||||
B5E126541A7DCE1400AD8B39 /* Where.swift */,
|
||||
B5E126561A7DCE5900AD8B39 /* SortedBy.swift */,
|
||||
B5F409F01A8B27A600A228EA /* CustomizeQuery.swift */,
|
||||
B54A9F021AA7640200AFEC05 /* AggregateFunction.swift */,
|
||||
B54A9F041AA7644400AFEC05 /* AggregateResultType.swift */,
|
||||
B582DF811A98B0E7003F09C6 /* HardcoreData+Querying.swift */,
|
||||
B5F409EC1A8B200700A228EA /* NSManagedObjectContext+Querying.swift */,
|
||||
B54A9F061AA7654400AFEC05 /* DataStack+Querying.swift */,
|
||||
B5F409EE1A8B243D00A228EA /* DataTransaction+Querying.swift */,
|
||||
B54A9EFF1AA763D100AFEC05 /* Internal */,
|
||||
);
|
||||
name = "Fetching and Querying";
|
||||
sourceTree = "<group>";
|
||||
@@ -454,6 +471,7 @@
|
||||
B5D1E22C19FA9FBC003B2874 /* NSError+HardcoreData.swift in Sources */,
|
||||
B595CAC61A9A1260009A397F /* NSManagedObjectContext+Transaction.swift in Sources */,
|
||||
B5CFF23E19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift in Sources */,
|
||||
B54A9F071AA7654400AFEC05 /* DataStack+Querying.swift in Sources */,
|
||||
B5E126571A7DCE5900AD8B39 /* SortedBy.swift in Sources */,
|
||||
B5F409EF1A8B243D00A228EA /* DataTransaction+Querying.swift in Sources */,
|
||||
2F291E2719C6D3CF007AF63F /* HardcoreData.swift in Sources */,
|
||||
@@ -465,9 +483,11 @@
|
||||
B582DF821A98B0E7003F09C6 /* HardcoreData+Querying.swift in Sources */,
|
||||
B5D19BFB1AA14063001D1A99 /* AsynchronousDataTransaction.swift in Sources */,
|
||||
B595CAC81A9A161B009A397F /* WeakObject.swift in Sources */,
|
||||
B54A9F051AA7644400AFEC05 /* AggregateResultType.swift in Sources */,
|
||||
B5D1E22A19FA9E63003B2874 /* PersistentStoreResult.swift in Sources */,
|
||||
B5F409F11A8B27A600A228EA /* CustomizeQuery.swift in Sources */,
|
||||
B5F409EB1A8B199600A228EA /* DefaultLogger.swift in Sources */,
|
||||
B54A9F031AA7640200AFEC05 /* AggregateFunction.swift in Sources */,
|
||||
B5D399F119FC818E000E91BB /* DataStack.swift in Sources */,
|
||||
B5D808161A34947300A44484 /* NotificationObserver.swift in Sources */,
|
||||
B57078B01A50392D007E33F2 /* FetchClause.swift in Sources */,
|
||||
@@ -631,6 +651,7 @@
|
||||
2F03A54719C5C6DA005002A5 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
"$(inherited)",
|
||||
@@ -648,6 +669,7 @@
|
||||
2F03A54819C5C6DA005002A5 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
EMBEDDED_CONTENT_CONTAINS_SWIFT = YES;
|
||||
FRAMEWORK_SEARCH_PATHS = (
|
||||
"$(SDKROOT)/Developer/Library/Frameworks",
|
||||
"$(inherited)",
|
||||
|
||||
99
HardcoreData/AggregateFunction.swift
Normal file
99
HardcoreData/AggregateFunction.swift
Normal file
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// 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 toExpression() -> 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)")]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
232
HardcoreData/AggregateResultType.swift
Normal file
232
HardcoreData/AggregateResultType.swift
Normal file
@@ -0,0 +1,232 @@
|
||||
//
|
||||
// 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,8 +41,8 @@ public class AsynchronousDataTransaction: DataTransaction {
|
||||
*/
|
||||
public func commit(completion: (result: SaveResult) -> Void) {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext() == true, "Attempted to commit a \(self.dynamicType) outside a transaction queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to commit a \(self.dynamicType) more than once.")
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to commit a <\(self.dynamicType)> outside a transaction queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to commit a <\(self.dynamicType)> more than once.")
|
||||
|
||||
self.isCommitted = true
|
||||
let semaphore = GCDSemaphore(0)
|
||||
@@ -62,8 +62,8 @@ public class AsynchronousDataTransaction: DataTransaction {
|
||||
*/
|
||||
public func commitAndWait() {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext() == true, "Attempted to commit a \(self.dynamicType) outside a transaction queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to commit a \(self.dynamicType) more than once.")
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to commit a <\(self.dynamicType)> outside a transaction queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to commit a <\(self.dynamicType)> more than once.")
|
||||
|
||||
self.isCommitted = true
|
||||
self.result = self.context.saveSynchronously()
|
||||
@@ -100,7 +100,7 @@ public class AsynchronousDataTransaction: DataTransaction {
|
||||
self.closure(transaction: self)
|
||||
if !self.isCommitted {
|
||||
|
||||
HardcoreData.log(.Warning, message: "The closure for the \(self.dynamicType) completed without being committed. All changes made within the transaction were discarded.")
|
||||
HardcoreData.log(.Warning, message: "The closure for the <\(self.dynamicType)> completed without being committed. All changes made within the transaction were discarded.")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,7 +112,7 @@ public class AsynchronousDataTransaction: DataTransaction {
|
||||
self.closure(transaction: self)
|
||||
if !self.isCommitted {
|
||||
|
||||
HardcoreData.log(.Warning, message: "The closure for the \(self.dynamicType) completed without being committed. All changes made within the transaction were discarded.")
|
||||
HardcoreData.log(.Warning, message: "The closure for the <\(self.dynamicType)> completed without being committed. All changes made within the transaction were discarded.")
|
||||
}
|
||||
}
|
||||
return self.result
|
||||
|
||||
97
HardcoreData/DataStack+Querying.swift
Normal file
97
HardcoreData/DataStack+Querying.swift
Normal file
@@ -0,0 +1,97 @@
|
||||
//
|
||||
// DataStack+Querying.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: - DataStack
|
||||
|
||||
public extension DataStack {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
public func fetchOne<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> T? {
|
||||
|
||||
return self.mainContext.fetchOne(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchOne<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> T? {
|
||||
|
||||
return self.mainContext.fetchOne(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> [T]? {
|
||||
|
||||
return self.mainContext.fetchAll(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> [T]? {
|
||||
|
||||
return self.mainContext.fetchAll(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchCount<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> Int? {
|
||||
|
||||
return self.mainContext.fetchCount(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchCount<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> Int? {
|
||||
|
||||
return self.mainContext.fetchCount(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func deleteAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> Int? {
|
||||
|
||||
return self.mainContext.deleteAll(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func deleteAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> Int? {
|
||||
|
||||
return self.mainContext.deleteAll(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func queryAggregate<T: NSManagedObject>(entity: T.Type, function: AggregateFunction, _ queryClauses: FetchClause...) -> Int? {
|
||||
|
||||
let result = self.mainContext.queryAggregate(entity, function: function, queryClauses)
|
||||
return result
|
||||
}
|
||||
|
||||
public func queryAggregate<T: NSManagedObject>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> Int? {
|
||||
|
||||
let result = self.mainContext.queryAggregate(entity, function: function, queryClauses)
|
||||
return result
|
||||
}
|
||||
|
||||
public func queryAggregate<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: FetchClause...) -> U? {
|
||||
|
||||
return self.mainContext.queryAggregate(entity, function: function, queryClauses)
|
||||
}
|
||||
|
||||
public func queryAggregate<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> U? {
|
||||
|
||||
return self.mainContext.queryAggregate(entity, function: function, queryClauses)
|
||||
}
|
||||
}
|
||||
@@ -32,11 +32,13 @@ private let applicationSupportDirectory = NSFileManager.defaultManager().URLsFor
|
||||
|
||||
private let applicationName = ((NSBundle.mainBundle().objectForInfoDictionaryKey("CFBundleName") as? String) ?? "CoreData")
|
||||
|
||||
private let defaultSQLiteStoreURL = applicationSupportDirectory.URLByAppendingPathComponent(applicationName, isDirectory: true).URLByAppendingPathExtension("sqlite")
|
||||
|
||||
|
||||
// MARK: - DataStack
|
||||
|
||||
/**
|
||||
The DataStack encapsulates the data model for the Core Data stack. Each DataStack can have multiple data stores, usually specified as a "Configuration" in the model editor. Behind the scenes, the DataStack manages its own NSPersistentStoreCoordinator, a root NSManagedObjectContext for disk saves, and a shared NSManagedObjectContext acting as a model interface for NSManagedObjects.
|
||||
The DataStack encapsulates the data model for the Core Data stack. Each DataStack can have multiple data stores, usually specified as a "Configuration" in the model editor. Behind the scenes, the DataStack manages its own NSPersistentStoreCoordinator, a root NSManagedObjectContext for disk saves, and a shared NSManagedObjectContext designed as a read-only model interface for NSManagedObjects.
|
||||
*/
|
||||
public class DataStack {
|
||||
|
||||
@@ -116,14 +118,14 @@ public class DataStack {
|
||||
|
||||
HardcoreData.handleError(
|
||||
error,
|
||||
"Failed to add in-memory \(NSPersistentStore.self).")
|
||||
"Failed to add in-memory <\(NSPersistentStore.self)>.")
|
||||
return PersistentStoreResult(error)
|
||||
}
|
||||
else {
|
||||
|
||||
HardcoreData.handleError(
|
||||
NSError(hardcoreDataErrorCode: .UnknownError),
|
||||
"Failed to add in-memory \(NSPersistentStore.self).")
|
||||
"Failed to add in-memory <\(NSPersistentStore.self)>.")
|
||||
return PersistentStoreResult(.UnknownError)
|
||||
}
|
||||
}
|
||||
@@ -159,7 +161,7 @@ public class DataStack {
|
||||
:param: resetStoreOnMigrationFailure Set to true to delete the store on migration failure; or set to false to throw exceptions on failure instead. Typically should only be set to true when debugging, or if the persistent store can be recreated easily. If not specified, defaults to false.
|
||||
:returns: a PersistentStoreResult indicating success or failure.
|
||||
*/
|
||||
public func addSQLiteStore(fileURL: NSURL = applicationSupportDirectory.URLByAppendingPathComponent(applicationName, isDirectory: true).URLByAppendingPathExtension("sqlite"), configuration: String? = nil, automigrating: Bool = true, resetStoreOnMigrationFailure: Bool = false) -> PersistentStoreResult {
|
||||
public func addSQLiteStore(fileURL: NSURL = defaultSQLiteStoreURL, configuration: String? = nil, automigrating: Bool = true, resetStoreOnMigrationFailure: Bool = false) -> PersistentStoreResult {
|
||||
|
||||
let coordinator = self.coordinator;
|
||||
if let store = coordinator.persistentStoreForURL(fileURL) {
|
||||
@@ -174,7 +176,7 @@ public class DataStack {
|
||||
|
||||
HardcoreData.handleError(
|
||||
NSError(hardcoreDataErrorCode: .DifferentPersistentStoreExistsAtURL),
|
||||
"Failed to add SQLite \(NSPersistentStore.self) at \"\(fileURL)\" because a different \(NSPersistentStore.self) at that URL already exists.")
|
||||
"Failed to add SQLite <\(NSPersistentStore.self)> at \"\(fileURL)\" because a different <\(NSPersistentStore.self)> at that URL already exists.")
|
||||
|
||||
return PersistentStoreResult(.DifferentPersistentStoreExistsAtURL)
|
||||
}
|
||||
@@ -249,7 +251,7 @@ public class DataStack {
|
||||
|
||||
HardcoreData.handleError(
|
||||
persistentStoreError ?? NSError(hardcoreDataErrorCode: .UnknownError),
|
||||
"Failed to add SQLite \(NSPersistentStore.self) at \"\(fileURL)\".")
|
||||
"Failed to add SQLite <\(NSPersistentStore.self)> at \"\(fileURL)\".")
|
||||
|
||||
return PersistentStoreResult(.UnknownError)
|
||||
}
|
||||
|
||||
@@ -53,13 +53,45 @@ public extension DataTransaction {
|
||||
return self.context.fetchAll(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func queryCount<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> Int {
|
||||
public func fetchCount<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> Int? {
|
||||
|
||||
return self.context.queryCount(entity, queryClauses)
|
||||
return self.context.fetchCount(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func queryCount<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> Int {
|
||||
public func fetchCount<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> Int? {
|
||||
|
||||
return self.context.queryCount(entity, queryClauses)
|
||||
return self.context.fetchCount(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func deleteAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> Int? {
|
||||
|
||||
return self.context.deleteAll(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func deleteAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> Int? {
|
||||
|
||||
return self.context.deleteAll(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func queryAggregate<T: NSManagedObject>(entity: T.Type, function: AggregateFunction, _ queryClauses: FetchClause...) -> Int? {
|
||||
|
||||
let result = self.context.queryAggregate(entity, function: function, queryClauses)
|
||||
return result
|
||||
}
|
||||
|
||||
public func queryAggregate<T: NSManagedObject>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> Int? {
|
||||
|
||||
let result = self.context.queryAggregate(entity, function: function, queryClauses)
|
||||
return result
|
||||
}
|
||||
|
||||
public func queryAggregate<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: FetchClause...) -> U? {
|
||||
|
||||
return self.context.queryAggregate(entity, function: function, queryClauses)
|
||||
}
|
||||
|
||||
public func queryAggregate<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> U? {
|
||||
|
||||
return self.context.queryAggregate(entity, function: function, queryClauses)
|
||||
}
|
||||
}
|
||||
@@ -45,8 +45,8 @@ public /*abstract*/ class DataTransaction {
|
||||
*/
|
||||
public func create<T: NSManagedObject>(entity: T.Type) -> T {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext() == true, "Attempted to create an entity of type \(entity) outside a transaction queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to create an entity of type \(entity) from an already committed \(self.dynamicType).")
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to create an entity of type <\(entity)> outside a transaction queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to create an entity of type <\(entity)> from an already committed <\(self.dynamicType)>.")
|
||||
|
||||
return T.createInContext(self.context)
|
||||
}
|
||||
@@ -59,8 +59,8 @@ public /*abstract*/ class DataTransaction {
|
||||
*/
|
||||
public func fetch<T: NSManagedObject>(object: T) -> T? {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext() == true, "Attempted to update an entity of type \(object.dynamicType) outside a transaction queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to update an entity of type \(object.dynamicType) from an already committed \(self.dynamicType).")
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to update an entity of type <\(object.dynamicType)> outside a transaction queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to update an entity of type <\(object.dynamicType)> from an already committed <\(self.dynamicType)>.")
|
||||
|
||||
return object.inContext(self.context)
|
||||
}
|
||||
@@ -72,8 +72,8 @@ public /*abstract*/ class DataTransaction {
|
||||
*/
|
||||
public func delete(object: NSManagedObject) {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext() == true, "Attempted to delete an entity of type \(object.dynamicType) outside a transaction queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to delete an entity of type \(object.dynamicType) from an already committed \(self.dynamicType).")
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to delete an entity of type <\(object.dynamicType)> outside a transaction queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to delete an entity of type <\(object.dynamicType)> from an already committed <\(self.dynamicType)>.")
|
||||
|
||||
object.deleteFromContext()
|
||||
}
|
||||
@@ -85,8 +85,8 @@ public /*abstract*/ class DataTransaction {
|
||||
*/
|
||||
public func rollback() {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext() == true, "Attempted to rollback a \(self.dynamicType) outside a transaction queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to rollback an already committed \(self.dynamicType).")
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to rollback a <\(self.dynamicType)> outside a transaction queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to rollback an already committed <\(self.dynamicType)>.")
|
||||
|
||||
self.context.reset()
|
||||
}
|
||||
|
||||
@@ -33,31 +33,63 @@ public extension HardcoreData {
|
||||
|
||||
public static func fetchOne<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> T? {
|
||||
|
||||
return self.defaultStack.mainContext.fetchOne(entity, queryClauses)
|
||||
return self.defaultStack.fetchOne(entity, queryClauses)
|
||||
}
|
||||
|
||||
public static func fetchOne<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> T? {
|
||||
|
||||
return self.defaultStack.mainContext.fetchOne(entity, queryClauses)
|
||||
return self.defaultStack.fetchOne(entity, queryClauses)
|
||||
}
|
||||
|
||||
public static func fetchAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> [T]? {
|
||||
|
||||
return self.defaultStack.mainContext.fetchAll(entity, queryClauses)
|
||||
return self.defaultStack.fetchAll(entity, queryClauses)
|
||||
}
|
||||
|
||||
public static func fetchAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> [T]? {
|
||||
|
||||
return self.defaultStack.mainContext.fetchAll(entity, queryClauses)
|
||||
return self.defaultStack.fetchAll(entity, queryClauses)
|
||||
}
|
||||
|
||||
public static func queryCount<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> Int {
|
||||
public static func fetchCount<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> Int? {
|
||||
|
||||
return self.defaultStack.mainContext.queryCount(entity, queryClauses)
|
||||
return self.defaultStack.fetchCount(entity, queryClauses)
|
||||
}
|
||||
|
||||
public static func queryCount<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> Int {
|
||||
public static func fetchCount<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> Int? {
|
||||
|
||||
return self.defaultStack.mainContext.queryCount(entity, queryClauses)
|
||||
return self.defaultStack.fetchCount(entity, queryClauses)
|
||||
}
|
||||
|
||||
public static func deleteAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> Int? {
|
||||
|
||||
return self.defaultStack.deleteAll(entity, queryClauses)
|
||||
}
|
||||
|
||||
public static func deleteAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> Int? {
|
||||
|
||||
return self.defaultStack.deleteAll(entity, queryClauses)
|
||||
}
|
||||
|
||||
public static func queryAggregate<T: NSManagedObject>(entity: T.Type, function: AggregateFunction, _ queryClauses: FetchClause...) -> Int? {
|
||||
|
||||
let result = self.defaultStack.queryAggregate(entity, function: function, queryClauses)
|
||||
return result
|
||||
}
|
||||
|
||||
public static func queryAggregate<T: NSManagedObject>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> Int? {
|
||||
|
||||
let result = self.defaultStack.queryAggregate(entity, function: function, queryClauses)
|
||||
return result
|
||||
}
|
||||
|
||||
public static func queryAggregate<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: FetchClause...) -> U? {
|
||||
|
||||
return self.defaultStack.queryAggregate(entity, function: function, queryClauses)
|
||||
}
|
||||
|
||||
public static func queryAggregate<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> U? {
|
||||
|
||||
return self.defaultStack.queryAggregate(entity, function: function, queryClauses)
|
||||
}
|
||||
}
|
||||
@@ -45,7 +45,7 @@ public enum HardcoreData {
|
||||
/**
|
||||
The default DataStack instance to be used. If defaultStack is not set before the first time accessed, a default-configured DataStack will be created.
|
||||
|
||||
Changing the defaultStack is thread safe.
|
||||
Changing the defaultStack is thread safe, but it is recommended to setup DataStacks on a common queue (e.g. the main queue).
|
||||
*/
|
||||
public static var defaultStack: DataStack {
|
||||
|
||||
|
||||
@@ -52,7 +52,7 @@ public enum HardcoreDataErrorCode: Int {
|
||||
public extension NSError {
|
||||
|
||||
/**
|
||||
If the error's domain is HardcoreDataErrorDomain, returns the associated HardcoreDataErrorCode. For other domains, returns nil.
|
||||
If the error's domain is equal to HardcoreDataErrorDomain, returns the associated HardcoreDataErrorCode. For other domains, returns nil.
|
||||
*/
|
||||
public var hardcoreDataErrorCode: HardcoreDataErrorCode? {
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ import CoreData
|
||||
|
||||
internal extension NSManagedObject {
|
||||
|
||||
// MARK: - Internal
|
||||
// MARK: Internal
|
||||
|
||||
internal class func createInContext(context: NSManagedObjectContext) -> Self {
|
||||
|
||||
@@ -77,7 +77,7 @@ internal extension NSManagedObject {
|
||||
|
||||
HardcoreData.handleError(
|
||||
error ?? NSError(hardcoreDataErrorCode: .UnknownError),
|
||||
"Failed to load existing \(T.self) in context.")
|
||||
"Failed to load existing <\(T.self)> in context.")
|
||||
return nil;
|
||||
}
|
||||
}
|
||||
@@ -32,7 +32,7 @@ import GCDKit
|
||||
|
||||
internal extension NSManagedObjectContext {
|
||||
|
||||
// MARK: - Internal
|
||||
// MARK: Internal
|
||||
|
||||
internal var shouldCascadeSavesToParent: Bool {
|
||||
|
||||
@@ -94,7 +94,7 @@ internal extension NSManagedObjectContext {
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Private
|
||||
// MARK: Private
|
||||
|
||||
private struct PropertyKeys {
|
||||
|
||||
|
||||
@@ -29,16 +29,16 @@ import CoreData
|
||||
|
||||
// MARK: - NSManagedObjectContext
|
||||
|
||||
public extension NSManagedObjectContext {
|
||||
internal extension NSManagedObjectContext {
|
||||
|
||||
// MARK: Public
|
||||
|
||||
public func fetchOne<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> T? {
|
||||
internal func fetchOne<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> T? {
|
||||
|
||||
return self.fetchOne(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchOne<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> T? {
|
||||
internal func fetchOne<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> T? {
|
||||
|
||||
let fetchRequest = NSFetchRequest()
|
||||
fetchRequest.entity = self.entityDescriptionForEntityClass(entity)
|
||||
@@ -67,12 +67,12 @@ public extension NSManagedObjectContext {
|
||||
return fetchResults?.first
|
||||
}
|
||||
|
||||
public func fetchAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> [T]? {
|
||||
internal func fetchAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> [T]? {
|
||||
|
||||
return self.fetchAll(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func fetchAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> [T]? {
|
||||
internal func fetchAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> [T]? {
|
||||
|
||||
let fetchRequest = NSFetchRequest()
|
||||
fetchRequest.entity = self.entityDescriptionForEntityClass(entity)
|
||||
@@ -101,12 +101,12 @@ public extension NSManagedObjectContext {
|
||||
return fetchResults
|
||||
}
|
||||
|
||||
public func queryCount<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> Int {
|
||||
internal func fetchCount<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> Int? {
|
||||
|
||||
return self.queryCount(entity, queryClauses)
|
||||
return self.fetchCount(entity, queryClauses)
|
||||
}
|
||||
|
||||
public func queryCount<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> Int {
|
||||
internal func fetchCount<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> Int? {
|
||||
|
||||
let fetchRequest = NSFetchRequest()
|
||||
fetchRequest.entity = self.entityDescriptionForEntityClass(entity)
|
||||
@@ -127,35 +127,114 @@ public extension NSManagedObjectContext {
|
||||
HardcoreData.handleError(
|
||||
error ?? NSError(hardcoreDataErrorCode: .UnknownError),
|
||||
"Failed executing fetch request.")
|
||||
return 0
|
||||
return nil
|
||||
}
|
||||
|
||||
return count
|
||||
}
|
||||
|
||||
internal func deleteAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> Int? {
|
||||
|
||||
return self.deleteAll(entity, queryClauses)
|
||||
}
|
||||
|
||||
// public func queryCount<T: NSManagedObject, U: IntegerType>(entity: T.Type, _ queryClauses: [FetchClause]) -> U? {
|
||||
//
|
||||
//// let expressionDescription = NSExpressionDescription()
|
||||
//// expressionDescription.name = "queryCount"
|
||||
//// expressionDescription.expressionResultType = .Integer32AttributeType
|
||||
//// expressionDescription.expression = NSExpression(
|
||||
//// forFunction: "min:",
|
||||
//// arguments: [NSExpression(forKeyPath: attribute)])
|
||||
//
|
||||
// let request = NSFetchRequest(entityName: entity.entityName)
|
||||
// request.resultType = .DictionaryResultType
|
||||
// request.predicate = predicate
|
||||
// request.propertiesToFetch = [expressionDescription]
|
||||
//
|
||||
// var error: NSError?
|
||||
// let results = NSManagedObjectContext.context()?.executeFetchRequest(request, error: &error)
|
||||
// if results == nil {
|
||||
//
|
||||
// JEDumpAlert(error, "error")
|
||||
// return nil
|
||||
// }
|
||||
//
|
||||
// return (results?.first as? [String: NSDate])?[expressionDescription.name]
|
||||
// }
|
||||
internal func deleteAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> Int? {
|
||||
|
||||
let fetchRequest = NSFetchRequest()
|
||||
fetchRequest.entity = self.entityDescriptionForEntityClass(entity)
|
||||
fetchRequest.fetchLimit = 0
|
||||
fetchRequest.resultType = .ManagedObjectResultType
|
||||
fetchRequest.returnsObjectsAsFaults = true
|
||||
|
||||
for clause in queryClauses {
|
||||
|
||||
clause.applyToFetchRequest(fetchRequest)
|
||||
}
|
||||
|
||||
var numberOfDeletedObjects: Int?
|
||||
var error: NSError?
|
||||
self.performBlockAndWait {
|
||||
|
||||
autoreleasepool {
|
||||
|
||||
if let fetchResults = self.executeFetchRequest(fetchRequest, error: &error) as? [T] {
|
||||
|
||||
numberOfDeletedObjects = fetchResults.count
|
||||
for object in fetchResults {
|
||||
|
||||
self.deleteObject(object)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if numberOfDeletedObjects == nil {
|
||||
|
||||
HardcoreData.handleError(
|
||||
error ?? NSError(hardcoreDataErrorCode: .UnknownError),
|
||||
"Failed executing fetch request.")
|
||||
return nil
|
||||
}
|
||||
|
||||
return numberOfDeletedObjects
|
||||
}
|
||||
|
||||
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>(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.toExpression()
|
||||
|
||||
let fetchRequest = NSFetchRequest()
|
||||
fetchRequest.entity = self.entityDescriptionForEntityClass(entity)
|
||||
fetchRequest.resultType = .DictionaryResultType
|
||||
fetchRequest.propertiesToFetch = [expressionDescription]
|
||||
fetchRequest.includesPendingChanges = false
|
||||
|
||||
for clause in queryClauses {
|
||||
|
||||
clause.applyToFetchRequest(fetchRequest)
|
||||
}
|
||||
|
||||
var fetchResults: [AnyObject]?
|
||||
var error: NSError?
|
||||
self.performBlockAndWait {
|
||||
|
||||
fetchResults = self.executeFetchRequest(fetchRequest, error: &error)
|
||||
}
|
||||
if fetchResults == nil {
|
||||
|
||||
HardcoreData.handleError(
|
||||
error ?? NSError(hardcoreDataErrorCode: .UnknownError),
|
||||
"Failed executing fetch request.")
|
||||
return nil
|
||||
}
|
||||
|
||||
if let result: AnyObject = (fetchResults?.first as! [String: AnyObject])[expressionDescription.name] {
|
||||
|
||||
return U.fromResultObject(result)
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -31,7 +31,7 @@ import CoreData
|
||||
|
||||
internal extension NSManagedObjectContext {
|
||||
|
||||
// MARK: - Internal
|
||||
// MARK: Internal
|
||||
|
||||
internal weak var parentStack: DataStack? {
|
||||
|
||||
@@ -62,6 +62,7 @@ internal extension NSManagedObjectContext {
|
||||
let context = NSManagedObjectContext(concurrencyType: .PrivateQueueConcurrencyType)
|
||||
context.persistentStoreCoordinator = coordinator
|
||||
context.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy
|
||||
context.undoManager = nil
|
||||
context.setupForHardcoreDataWithContextName("com.hardcoredata.rootcontext")
|
||||
|
||||
return context
|
||||
@@ -73,6 +74,7 @@ internal extension NSManagedObjectContext {
|
||||
context.parentContext = rootContext
|
||||
context.setupForHardcoreDataWithContextName("com.hardcoredata.maincontext")
|
||||
context.shouldCascadeSavesToParent = true
|
||||
context.undoManager = nil
|
||||
context.observerForDidSaveNotification = NotificationObserver(
|
||||
notificationName: NSManagedObjectContextDidSaveNotification,
|
||||
object: rootContext,
|
||||
|
||||
@@ -32,7 +32,7 @@ import GCDKit
|
||||
|
||||
internal extension NSManagedObjectContext {
|
||||
|
||||
// MARK: - Internal
|
||||
// MARK: Internal
|
||||
|
||||
internal weak var parentTransaction: DataTransaction? {
|
||||
|
||||
@@ -94,7 +94,7 @@ internal extension NSManagedObjectContext {
|
||||
|
||||
HardcoreData.handleError(
|
||||
error,
|
||||
"Failed to save \(NSManagedObjectContext.self).")
|
||||
"Failed to save <\(NSManagedObjectContext.self)>.")
|
||||
result = SaveResult(error)
|
||||
}
|
||||
else {
|
||||
@@ -153,7 +153,7 @@ internal extension NSManagedObjectContext {
|
||||
|
||||
HardcoreData.handleError(
|
||||
error,
|
||||
"Failed to save \(NSManagedObjectContext.self).")
|
||||
"Failed to save <\(NSManagedObjectContext.self)>.")
|
||||
if let completion = completion {
|
||||
|
||||
GCDQueue.Main.async {
|
||||
|
||||
@@ -100,7 +100,7 @@ public struct SortedBy: FetchClause {
|
||||
|
||||
if fetchRequest.sortDescriptors != nil {
|
||||
|
||||
HardcoreData.log(.Warning, message: "Existing sortDescriptors for the \(NSFetchRequest.self) was overwritten by \(self.dynamicType) query clause.")
|
||||
HardcoreData.log(.Warning, message: "Existing sortDescriptors for the <\(NSFetchRequest.self)> was overwritten by <\(self.dynamicType)> query clause.")
|
||||
}
|
||||
|
||||
fetchRequest.sortDescriptors = self.sortDescriptors
|
||||
|
||||
@@ -20,8 +20,8 @@ public class SynchronousDataTransaction: DataTransaction {
|
||||
*/
|
||||
public func commitAndWait() {
|
||||
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext() == true, "Attempted to commit a \(self.dynamicType) outside a transaction queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to commit a \(self.dynamicType) more than once.")
|
||||
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to commit a <\(self.dynamicType)> outside a transaction queue.")
|
||||
HardcoreData.assert(!self.isCommitted, "Attempted to commit a <\(self.dynamicType)> more than once.")
|
||||
|
||||
self.isCommitted = true
|
||||
self.result = self.context.saveSynchronously()
|
||||
@@ -51,7 +51,7 @@ public class SynchronousDataTransaction: DataTransaction {
|
||||
self.closure(transaction: self)
|
||||
if !self.isCommitted {
|
||||
|
||||
HardcoreData.log(.Warning, message: "The closure for the \(self.dynamicType) completed without being committed. All changes made within the transaction were discarded.")
|
||||
HardcoreData.log(.Warning, message: "The closure for the <\(self.dynamicType)> completed without being committed. All changes made within the transaction were discarded.")
|
||||
}
|
||||
}
|
||||
return self.result
|
||||
@@ -65,7 +65,7 @@ public class SynchronousDataTransaction: DataTransaction {
|
||||
}
|
||||
|
||||
|
||||
// MARK: - Private
|
||||
// MARK: Private
|
||||
|
||||
private let closure: (transaction: SynchronousDataTransaction) -> Void
|
||||
}
|
||||
|
||||
@@ -89,7 +89,7 @@ public struct Where: FetchClause {
|
||||
|
||||
if fetchRequest.predicate != nil {
|
||||
|
||||
HardcoreData.log(.Warning, message: "An existing predicate for the \(NSFetchRequest.self) was overwritten by \(self.dynamicType) query clause.")
|
||||
HardcoreData.log(.Warning, message: "An existing predicate for the <\(NSFetchRequest.self)> was overwritten by <\(self.dynamicType)> query clause.")
|
||||
}
|
||||
|
||||
fetchRequest.predicate = self.predicate
|
||||
|
||||
@@ -32,11 +32,12 @@ class HardcoreDataTests: XCTestCase {
|
||||
override func setUp() {
|
||||
|
||||
super.setUp()
|
||||
NSFileManager.defaultManager().removeItemAtURL(NSFileManager.defaultManager().URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask).first as! NSURL, error: nil)
|
||||
self.deleteStores()
|
||||
}
|
||||
|
||||
override func tearDown() {
|
||||
|
||||
self.deleteStores()
|
||||
super.tearDown()
|
||||
}
|
||||
|
||||
@@ -73,6 +74,12 @@ class HardcoreDataTests: XCTestCase {
|
||||
obj1.testNumber = 42
|
||||
obj1.testDate = NSDate()
|
||||
|
||||
let count = transaction.queryAggregate(
|
||||
TestEntity1.self,
|
||||
function: .Count("testNumber")
|
||||
)
|
||||
XCTAssertTrue(count == 0, "count == 0 (actual: \(count))") // counts only objects in store
|
||||
|
||||
let obj2 = transaction.create(TestEntity2)
|
||||
obj2.testEntityID = 2
|
||||
obj2.testString = "hahaha"
|
||||
@@ -143,7 +150,6 @@ class HardcoreDataTests: XCTestCase {
|
||||
)
|
||||
XCTAssertNotNil(objs2, "objs2 != nil")
|
||||
XCTAssertTrue(objs2?.count == 2, "objs2?.count == 2")
|
||||
print(objs2)
|
||||
|
||||
transaction.commit { (result) -> Void in
|
||||
|
||||
@@ -160,5 +166,48 @@ class HardcoreDataTests: XCTestCase {
|
||||
}
|
||||
|
||||
self.waitForExpectationsWithTimeout(100, handler: nil)
|
||||
|
||||
let max1 = HardcoreData.queryAggregate(
|
||||
TestEntity2.self,
|
||||
function: .Maximum("testNumber")
|
||||
)
|
||||
XCTAssertTrue(max1 == 100, "max == 100 (actual: \(max1))")
|
||||
|
||||
let max2 = HardcoreData.queryAggregate(
|
||||
TestEntity2.self,
|
||||
function: .Maximum("testNumber"),
|
||||
Where("%K > %@", "testEntityID", 2)
|
||||
)
|
||||
XCTAssertTrue(max2 == 90, "max == 90 (actual: \(max2))")
|
||||
|
||||
HardcoreData.performTransactionAndWait { (transaction) -> Void in
|
||||
|
||||
let numberOfDeletedObjects1 = transaction.deleteAll(TestEntity1)
|
||||
XCTAssertTrue(numberOfDeletedObjects1 == 1, "numberOfDeletedObjects1 == 1 (actual: \(numberOfDeletedObjects1))")
|
||||
|
||||
let numberOfDeletedObjects2 = transaction.deleteAll(
|
||||
TestEntity2.self,
|
||||
Where("%K > %@", "testEntityID", 2)
|
||||
)
|
||||
XCTAssertTrue(numberOfDeletedObjects2 == 1, "numberOfDeletedObjects2 == 1 (actual: \(numberOfDeletedObjects2))")
|
||||
|
||||
transaction.commitAndWait()
|
||||
}
|
||||
|
||||
let objs1 = HardcoreData.fetchAll(TestEntity1)
|
||||
XCTAssertNotNil(objs1, "objs1 != nil")
|
||||
XCTAssertTrue(objs1?.count == 0, "objs1?.count == 0")
|
||||
|
||||
let objs2 = HardcoreData.fetchAll(TestEntity2)
|
||||
XCTAssertNotNil(objs2, "objs2 != nil")
|
||||
XCTAssertTrue(objs2?.count == 1, "objs2?.count == 1")
|
||||
}
|
||||
|
||||
private func deleteStores() {
|
||||
|
||||
NSFileManager.defaultManager().removeItemAtURL(
|
||||
NSFileManager.defaultManager().URLsForDirectory(.ApplicationSupportDirectory, inDomains: .UserDomainMask).first as! NSURL,
|
||||
error: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user