Added aggregate function querying and deletion querying

This commit is contained in:
John Rommel Estropia
2015-03-05 14:24:26 +08:00
parent 55dee6ec2f
commit 1fae78434f
20 changed files with 730 additions and 84 deletions

View File

@@ -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)",

View 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)")]
)
}
}
}

View 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)
}
}

View File

@@ -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

View 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)
}
}

View File

@@ -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)
}

View File

@@ -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)
}
}

View File

@@ -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()
}

View File

@@ -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)
}
}

View File

@@ -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 {

View File

@@ -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? {

View File

@@ -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;
}
}

View File

@@ -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 {

View File

@@ -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
}
}

View File

@@ -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,

View File

@@ -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 {

View File

@@ -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

View File

@@ -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
}

View File

@@ -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

View File

@@ -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
)
}
}