From 6b8bb3e434d6e152a90a554319f1cc388eb6a450 Mon Sep 17 00:00:00 2001 From: John Rommel Estropia Date: Sun, 14 Dec 2014 23:47:18 +0900 Subject: [PATCH] elegant queries --- HardcoreData.xcodeproj/project.pbxproj | 20 +++-- HardcoreData/DataStack.swift | 4 +- HardcoreData/DataTransaction.swift | 52 +++++++----- HardcoreData/HardcoreData.swift | 8 +- .../NSManagedObject+HardcoreData.swift | 23 +++-- .../NSManagedObjectContext+HardcoreData.swift | 84 +++++++++++-------- HardcoreData/NotificationObserver.swift | 2 +- .../{Query.swift => ObjectQuery.swift} | 67 ++++++++------- HardcoreData/ObjectQueryable.swift | 47 +++++++++++ .../{Queryable.swift => ValueQueryable.swift} | 12 +-- HardcoreDataTests/HardcoreDataTests.swift | 51 ++++++----- .../Model.xcdatamodel/contents | 2 +- HardcoreDataTests/TestEntity1.swift | 8 +- Libraries/GCDKit | 2 +- 14 files changed, 237 insertions(+), 145 deletions(-) rename HardcoreData/{Query.swift => ObjectQuery.swift} (72%) create mode 100644 HardcoreData/ObjectQueryable.swift rename HardcoreData/{Queryable.swift => ValueQueryable.swift} (83%) diff --git a/HardcoreData.xcodeproj/project.pbxproj b/HardcoreData.xcodeproj/project.pbxproj index f263d74..454b0bb 100644 --- a/HardcoreData.xcodeproj/project.pbxproj +++ b/HardcoreData.xcodeproj/project.pbxproj @@ -25,9 +25,10 @@ B5D8080E1A3471A500A44484 /* GCDKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5D808021A34715700A44484 /* GCDKit.framework */; }; B5D808161A34947300A44484 /* NotificationObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D808151A34947300A44484 /* NotificationObserver.swift */; }; B5D8081A1A3495BD00A44484 /* NSObject+HardcoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D808191A3495BD00A44484 /* NSObject+HardcoreData.swift */; }; + B5E186351A3C5CDE002171F0 /* ValueQueryable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E186341A3C5CDE002171F0 /* ValueQueryable.swift */; }; B5E209E01A0726460089C9D4 /* NSManagedObject+HardcoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E209DF1A0726460089C9D4 /* NSManagedObject+HardcoreData.swift */; }; - B5E472271A35E84700804CE1 /* Queryable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E472261A35E84700804CE1 /* Queryable.swift */; }; - B5F539901A17A6FC00EC763B /* Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F5398F1A17A6FC00EC763B /* Query.swift */; }; + B5E472271A35E84700804CE1 /* ObjectQueryable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E472261A35E84700804CE1 /* ObjectQueryable.swift */; }; + B5F539901A17A6FC00EC763B /* ObjectQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F5398F1A17A6FC00EC763B /* ObjectQuery.swift */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -77,10 +78,11 @@ B5D806C51A34715700A44484 /* GCDKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = GCDKit.xcodeproj; sourceTree = ""; }; B5D808151A34947300A44484 /* NotificationObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationObserver.swift; sourceTree = ""; }; B5D808191A3495BD00A44484 /* NSObject+HardcoreData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+HardcoreData.swift"; sourceTree = ""; }; + B5E186341A3C5CDE002171F0 /* ValueQueryable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValueQueryable.swift; sourceTree = ""; }; B5E209DF1A0726460089C9D4 /* NSManagedObject+HardcoreData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObject+HardcoreData.swift"; sourceTree = ""; }; - B5E472261A35E84700804CE1 /* Queryable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Queryable.swift; sourceTree = ""; }; + B5E472261A35E84700804CE1 /* ObjectQueryable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjectQueryable.swift; sourceTree = ""; }; B5F3D98419F3EB8E009690A6 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = ""; }; - B5F5398F1A17A6FC00EC763B /* Query.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Query.swift; sourceTree = ""; }; + B5F5398F1A17A6FC00EC763B /* ObjectQuery.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjectQuery.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -129,7 +131,6 @@ isa = PBXGroup; children = ( B5D399F019FC818E000E91BB /* DataStack.swift */, - B5E472261A35E84700804CE1 /* Queryable.swift */, B5CFF23F19FD383100D6DFC4 /* DataTransaction.swift */, 2F03A53519C5C6DA005002A5 /* HardcoreData.h */, 2F291E2619C6D3CF007AF63F /* HardcoreData.swift */, @@ -137,7 +138,9 @@ B5E209DF1A0726460089C9D4 /* NSManagedObject+HardcoreData.swift */, B5CFF23D19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift */, B5D399F419FCF4E0000E91BB /* NSPersistentStoreCoordinator+HardcoreData.swift */, - B5F5398F1A17A6FC00EC763B /* Query.swift */, + B5F5398F1A17A6FC00EC763B /* ObjectQuery.swift */, + B5E472261A35E84700804CE1 /* ObjectQueryable.swift */, + B5E186341A3C5CDE002171F0 /* ValueQueryable.swift */, B5D1E22919FA9E63003B2874 /* PersistentStoreResult.swift */, B5CFD36D1A0775F000B7885F /* SaveResult.swift */, B5D808141A34945A00A44484 /* Internal */, @@ -349,17 +352,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( - B5F539901A17A6FC00EC763B /* Query.swift in Sources */, + B5F539901A17A6FC00EC763B /* ObjectQuery.swift in Sources */, B5CFF24019FD383100D6DFC4 /* DataTransaction.swift in Sources */, B5D399F519FCF4E0000E91BB /* NSPersistentStoreCoordinator+HardcoreData.swift in Sources */, B5CFD36E1A0775F000B7885F /* SaveResult.swift in Sources */, B5D1E22C19FA9FBC003B2874 /* NSError+HardcoreData.swift in Sources */, B5CFF23E19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift in Sources */, + B5E186351A3C5CDE002171F0 /* ValueQueryable.swift in Sources */, 2F291E2719C6D3CF007AF63F /* HardcoreData.swift in Sources */, B5D8081A1A3495BD00A44484 /* NSObject+HardcoreData.swift in Sources */, B5E209E01A0726460089C9D4 /* NSManagedObject+HardcoreData.swift in Sources */, B5D1E22A19FA9E63003B2874 /* PersistentStoreResult.swift in Sources */, - B5E472271A35E84700804CE1 /* Queryable.swift in Sources */, + B5E472271A35E84700804CE1 /* ObjectQueryable.swift in Sources */, B5D399F119FC818E000E91BB /* DataStack.swift in Sources */, B5D808161A34947300A44484 /* NotificationObserver.swift in Sources */, ); diff --git a/HardcoreData/DataStack.swift b/HardcoreData/DataStack.swift index f6ced52..c8adb55 100644 --- a/HardcoreData/DataStack.swift +++ b/HardcoreData/DataStack.swift @@ -266,9 +266,9 @@ public class DataStack: NSObject { Begins a transaction synchronously where NSManagedObject creates, updates, and deletes can be made. :param: closure the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent NSManagedObjectContext. - :returns: a SaveResult value indicating success or failure. + :returns: a SaveResult value indicating success or failure, or nil if the transaction was not comitted synchronously */ - public func performTransactionAndWait(closure: (transaction: DataTransaction) -> ()) -> SaveResult { + public func performTransactionAndWait(closure: (transaction: DataTransaction) -> ()) -> SaveResult? { return DataTransaction( mainContext: self.mainContext, diff --git a/HardcoreData/DataTransaction.swift b/HardcoreData/DataTransaction.swift index 9da58c8..a0f5d4d 100644 --- a/HardcoreData/DataTransaction.swift +++ b/HardcoreData/DataTransaction.swift @@ -30,7 +30,7 @@ import GCDKit /** The DataTransaction provides an interface for NSManagedObject creates, updates, and deletes. A transaction object should typically be only used from within a transaction block initiated from DataStack.performTransaction(_:), or from HardcoreData.performTransaction(_:). */ -public class DataTransaction { +public final class DataTransaction { // MARK: - Public @@ -102,9 +102,9 @@ public class DataTransaction { HardcoreData.assert(!self.isCommitted, "Attempted to commit a DataTransaction more than once.") self.isCommitted = true - self.context.saveAsynchronouslyWithCompletion { [weak self] (result) -> () in + self.context.saveAsynchronouslyWithCompletion { (result) -> () in - self?.result = result + self.result = result completion(result: result) } } @@ -114,15 +114,13 @@ public class DataTransaction { :returns: a SaveResult value indicating success or failure. */ - public func commitAndWait() -> SaveResult { + public func commitAndWait() { HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext() == true, "Attempted to commit a DataTransaction outside a transaction queue.") HardcoreData.assert(!self.isCommitted, "Attempted to commit a DataTransaction more than once.") self.isCommitted = true - let result = self.context.saveSynchronously() - self.result = result - return result + self.result = self.context.saveSynchronously() } @@ -141,24 +139,16 @@ public class DataTransaction { self.transactionQueue.barrierAsync { self.closure(transaction: self) - if !self.isCommitted { - - self.commit { (result) -> () in } - } } } - internal func performAndWait() -> SaveResult { + internal func performAndWait() -> SaveResult? { self.transactionQueue.barrierSync { self.closure(transaction: self) - if !self.isCommitted { - - self.commitAndWait() - } } - return self.result! + return self.result } @@ -174,34 +164,54 @@ public class DataTransaction { // MARK: - DataContextProvider -extension DataTransaction: Queryable { +extension DataTransaction: ObjectQueryable { public func findFirst(entity: T.Type) -> T? { return self.context.findFirst(entity) } - public func findFirst(query: Query) -> T? { + public func findFirst(entity: T.Type, customizeFetch: FetchRequestCustomization?) -> T? { + + return self.context.findFirst(entity, customizeFetch: customizeFetch) + } + + public func findFirst(query: ObjectQuery) -> T? { return self.context.findFirst(query) } + public func findFirst(query: ObjectQuery, customizeFetch: FetchRequestCustomization?) -> T? { + + return self.context.findFirst(query, customizeFetch: customizeFetch) + } + public func findAll(entity: T.Type) -> [T]? { return self.context.findAll(entity) } - public func findAll(query: Query) -> [T]? { + public func findAll(entity: T.Type, customizeFetch: FetchRequestCustomization?) -> [T]? { + + return self.context.findAll(entity, customizeFetch: customizeFetch) + } + + public func findAll(query: ObjectQuery) -> [T]? { return self.context.findAll(query) } + public func findAll(query: ObjectQuery, customizeFetch: FetchRequestCustomization?) -> [T]? { + + return self.context.findAll(query, customizeFetch: customizeFetch) + } + public func count(entity: T.Type) -> Int { return self.context.count(entity) } - public func count(query: Query) -> Int { + public func count(query: ObjectQuery) -> Int { return self.context.count(query) } diff --git a/HardcoreData/HardcoreData.swift b/HardcoreData/HardcoreData.swift index 20f9048..0bfe3ce 100644 --- a/HardcoreData/HardcoreData.swift +++ b/HardcoreData/HardcoreData.swift @@ -28,8 +28,6 @@ import GCDKit /** -HardcoreData - Simple, elegant, and smart Core Data management with Swift - The HardcoreData struct is the main entry point for all other APIs. */ public struct HardcoreData { @@ -75,9 +73,9 @@ public struct HardcoreData { Using the defaultStack, begins a transaction asynchronously where NSManagedObject creates, updates, and deletes can be made. :param: closure the block where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent NSManagedObjectContext. - :returns: a SaveResult value indicating success or failure. + :returns: a SaveResult value indicating success or failure, or nil if the transaction was not comitted synchronously */ - public static func performTransactionAndWait(closure: (transaction: DataTransaction) -> ()) -> SaveResult { + public static func performTransactionAndWait(closure: (transaction: DataTransaction) -> ()) -> SaveResult? { return self.defaultStack.performTransactionAndWait(closure) } @@ -87,7 +85,7 @@ public struct HardcoreData { case Trace case Notice - case Alert + case Warning case Fatal } diff --git a/HardcoreData/NSManagedObject+HardcoreData.swift b/HardcoreData/NSManagedObject+HardcoreData.swift index 33f4d57..9ce421f 100644 --- a/HardcoreData/NSManagedObject+HardcoreData.swift +++ b/HardcoreData/NSManagedObject+HardcoreData.swift @@ -48,32 +48,37 @@ public extension NSManagedObject { // MARK: Querying - public class func WHERE(predicate: NSPredicate) -> Query { + public class func WHERE(predicate: NSPredicate) -> ObjectQuery { - return Query(entity: self).WHERE(predicate) + return ObjectQuery(entity: self).WHERE(predicate) } - public class func WHERE(value: Bool) -> Query { + public class func WHERE(value: Bool) -> ObjectQuery { return self.WHERE(NSPredicate(value: value)) } - public class func WHERE(format: String, _ args: CVarArgType...) -> Query { + public class func WHERE(format: String, _ args: CVarArgType...) -> ObjectQuery { - return self.WHERE(NSPredicate(format: format, arguments: withVaList(args, { $0 }))) + return self.WHERE(NSPredicate(format: format, arguments: getVaList(args))) } - public class func WHERE(format: String, argumentArray: [AnyObject]?) -> Query { + public class func WHERE(format: String, argumentArray: [AnyObject]?) -> ObjectQuery { return self.WHERE(NSPredicate(format: format, argumentArray: argumentArray)) } - public class func SORTEDBY(order: [SortOrder]) -> Query { + public class func WHERE(attributeName: AttributeName, isEqualTo value: NSObject?) -> ObjectQuery { - return Query(entity: self).SORTEDBY(order) + return ObjectQuery(entity: self).WHERE(attributeName, isEqualTo: value) } - public class func SORTEDBY(order: SortOrder, _ subOrder: SortOrder...) -> Query { + public class func SORTEDBY(order: [SortOrder]) -> ObjectQuery { + + return ObjectQuery(entity: self).SORTEDBY(order) + } + + public class func SORTEDBY(order: SortOrder, _ subOrder: SortOrder...) -> ObjectQuery { return self.SORTEDBY([order] + subOrder) } diff --git a/HardcoreData/NSManagedObjectContext+HardcoreData.swift b/HardcoreData/NSManagedObjectContext+HardcoreData.swift index d4a99c4..5e05e62 100644 --- a/HardcoreData/NSManagedObjectContext+HardcoreData.swift +++ b/HardcoreData/NSManagedObjectContext+HardcoreData.swift @@ -101,7 +101,7 @@ public extension NSManagedObjectContext { self.reset() if let completion = completion { - GCDBlock.async(.Main) { + GCDQueue.Main.async { completion(result: SaveResult(hasChanges: false)) } @@ -126,7 +126,7 @@ public extension NSManagedObjectContext { if let completion = completion { - GCDBlock.async(.Main) { + GCDQueue.Main.async { completion(result: SaveResult(hasChanges: true)) } @@ -139,7 +139,7 @@ public extension NSManagedObjectContext { "Failed to save NSManagedObjectContext.") if let completion = completion { - GCDBlock.async(.Main) { + GCDQueue.Main.async { completion(result: SaveResult(error)) } @@ -147,7 +147,7 @@ public extension NSManagedObjectContext { } else if let completion = completion { - GCDBlock.async(.Main) { + GCDQueue.Main.async { completion(result: SaveResult(hasChanges: false)) } @@ -274,30 +274,40 @@ public extension NSManagedObjectContext { // MARK: - DataContextProvider -extension NSManagedObjectContext: Queryable { +extension NSManagedObjectContext: ObjectQueryable { public func findFirst(entity: T.Type) -> T? { - return self.findFirst(Query(entity: entity)) + return self.findFirst(entity, customizeFetch: nil) } - public func findFirst(query: Query) -> T? { + public func findFirst(entity: T.Type, customizeFetch: FetchRequestCustomization?) -> T? { - var query = query - query.fetchLimit = 1 - let fetchRequest = query.createFetchRequestInContext(self) + return self.findFirst(ObjectQuery(entity: entity), customizeFetch: customizeFetch) + } + + public func findFirst(query: ObjectQuery) -> T? { + + return self.findFirst(query, customizeFetch: nil) + } + + public func findFirst(query: ObjectQuery, customizeFetch: FetchRequestCustomization?) -> T? { + + let fetchRequest = query.createFetchRequestForContext(self) + customizeFetch?(fetchRequest: fetchRequest) + fetchRequest.fetchLimit = 1 + fetchRequest.resultType = .ManagedObjectResultType var fetchResults: [T]? + var error: NSError? self.performBlockAndWait { - var error: NSError? fetchResults = self.executeFetchRequest(fetchRequest, error: &error) as? [T] - if fetchResults == nil { - - HardcoreData.handleError( - error!, - "Failed executing fetch request.") - } + } + if fetchResults == nil { + + HardcoreData.handleError(error!, "Failed executing fetch request.") + return nil } return fetchResults?.first @@ -305,24 +315,34 @@ extension NSManagedObjectContext: Queryable { public func findAll(entity: T.Type) -> [T]? { - return self.findAll(Query(entity: entity)) + return self.findAll(entity, customizeFetch: nil) } - public func findAll(query: Query) -> [T]? { + public func findAll(entity: T.Type, customizeFetch: FetchRequestCustomization?) -> [T]? { - let fetchRequest = query.createFetchRequestInContext(self) + return self.findAll(ObjectQuery(entity: entity), customizeFetch: customizeFetch) + } + + public func findAll(query: ObjectQuery) -> [T]? { + + return self.findAll(query, customizeFetch: nil) + } + + public func findAll(query: ObjectQuery, customizeFetch: FetchRequestCustomization?) -> [T]? { + + let fetchRequest = query.createFetchRequestForContext(self) + fetchRequest.fetchLimit = 0 var fetchResults: [T]? + var error: NSError? self.performBlockAndWait { - var error: NSError? fetchResults = self.executeFetchRequest(fetchRequest, error: &error) as? [T] - if fetchResults == nil { - - HardcoreData.handleError( - error!, - "Failed executing fetch request.") - } + } + if fetchResults == nil { + + HardcoreData.handleError(error!, "Failed executing fetch request.") + return nil } return fetchResults @@ -330,12 +350,12 @@ extension NSManagedObjectContext: Queryable { public func count(entity: T.Type) -> Int { - return self.count(Query(entity: entity)) + return self.count(ObjectQuery(entity: entity)) } - public func count(query: Query) -> Int { + public func count(query: ObjectQuery) -> Int { - let fetchRequest = query.createFetchRequestInContext(self) + let fetchRequest = query.createFetchRequestForContext(self) var count = 0 var error: NSError? @@ -345,9 +365,7 @@ extension NSManagedObjectContext: Queryable { } if count == NSNotFound { - HardcoreData.handleError( - error!, - "Failed executing fetch request.") + HardcoreData.handleError( error!, "Failed executing fetch request.") return 0 } diff --git a/HardcoreData/NotificationObserver.swift b/HardcoreData/NotificationObserver.swift index 1b214a6..e79c863 100644 --- a/HardcoreData/NotificationObserver.swift +++ b/HardcoreData/NotificationObserver.swift @@ -25,7 +25,7 @@ import Foundation -internal class NotificationObserver { +internal final class NotificationObserver { let notificationName: String let object: AnyObject? diff --git a/HardcoreData/Query.swift b/HardcoreData/ObjectQuery.swift similarity index 72% rename from HardcoreData/Query.swift rename to HardcoreData/ObjectQuery.swift index f241b65..8111bb4 100644 --- a/HardcoreData/Query.swift +++ b/HardcoreData/ObjectQuery.swift @@ -1,5 +1,5 @@ // -// Query.swift +// ObjectQuery.swift // HardcoreData // // Copyright (c) 2014 John Rommel Estropia @@ -35,39 +35,55 @@ public enum SortOrder { case Descending(AttributeName) } -public class Query { +public final class ObjectQuery { + + public var fetchLimit: Int = 0 + public var fetchOffset: Int = 0 + public var fetchBatchSize: Int = 0 public var entityName: String { return self.entity.entityName } - public func WHERE(predicate: NSPredicate) -> Query { + public func WHERE(predicate: NSPredicate) -> ObjectQuery { if self.predicate != nil { + HardcoreData.log(.Warning, message: "Attempted to set a Query's WHERE clause more than once. The last predicate set will be used.") } self.predicate = predicate return self } - public func WHERE(value: Bool) -> Query { + public func WHERE(value: Bool) -> ObjectQuery { return self.WHERE(NSPredicate(value: value)) } - public func WHERE(format: String, _ args: CVarArgType...) -> Query { + public func WHERE(format: String, _ args: CVarArgType...) -> ObjectQuery { - return self.WHERE(NSPredicate(format: format, arguments: withVaList(args, { $0 }))) + return self.WHERE(NSPredicate(format: format, arguments: getVaList(args))) } - public func WHERE(format: String, argumentArray: [AnyObject]?) -> Query { + public func WHERE(format: String, argumentArray: [AnyObject]?) -> ObjectQuery { return self.WHERE(NSPredicate(format: format, argumentArray: argumentArray)) } - public func SORTEDBY(order: [SortOrder]) -> Query { + public func WHERE(attributeName: AttributeName, isEqualTo value: NSObject?) -> ObjectQuery { + return self.WHERE(value == nil + ? NSPredicate(format: "\(attributeName) == nil")! + : NSPredicate(format: "\(attributeName) == %@", value!)!) + } + + public func SORTEDBY(order: [SortOrder]) -> ObjectQuery { + + if self.sortDescriptors != nil { + + HardcoreData.log(.Warning, message: "Attempted to set a Query's SORTEDBY clause more than once. The last sort order set will be used.") + } self.sortDescriptors = order.map { sortOrder in switch sortOrder { @@ -86,25 +102,11 @@ public class Query { return self } - public func SORTEDBY(order: SortOrder, _ subOrder: SortOrder...) -> Query { + public func SORTEDBY(order: SortOrder, _ subOrder: SortOrder...) -> ObjectQuery { return self.SORTEDBY([order] + subOrder) } - public func createFetchRequestInContext(context: NSManagedObjectContext) -> NSFetchRequest { - - let fetchRequest = NSFetchRequest() - fetchRequest.entity = NSEntityDescription.entityForName( - self.entityName, - inManagedObjectContext: context) - fetchRequest.fetchLimit = self.fetchLimit - fetchRequest.fetchOffset = self.fetchOffset - fetchRequest.fetchBatchSize = self.fetchBatchSize - fetchRequest.predicate = self.predicate - - return fetchRequest - } - // MARK: Internal internal init(entity: T.Type) { @@ -112,12 +114,21 @@ public class Query { self.entity = entity } + internal func createFetchRequestForContext(context: NSManagedObjectContext) -> NSFetchRequest { + + let fetchRequest = NSFetchRequest() + fetchRequest.entity = NSEntityDescription.entityForName( + self.entityName, + inManagedObjectContext: context) + fetchRequest.predicate = self.predicate + fetchRequest.sortDescriptors = self.sortDescriptors + + return fetchRequest + } + // MARK: Private private let entity: T.Type - public var fetchLimit: Int = 0 - public var fetchOffset: Int = 0 - public var fetchBatchSize: Int = 0 - public var predicate: NSPredicate? - public var sortDescriptors: [NSSortDescriptor]? + private var predicate: NSPredicate? + private var sortDescriptors: [NSSortDescriptor]? } diff --git a/HardcoreData/ObjectQueryable.swift b/HardcoreData/ObjectQueryable.swift new file mode 100644 index 0000000..06030c6 --- /dev/null +++ b/HardcoreData/ObjectQueryable.swift @@ -0,0 +1,47 @@ +// +// ObjectQueryable.swift +// HardcoreData +// +// Copyright (c) 2014 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 UIKit + + +public typealias FetchRequestCustomization = (fetchRequest: NSFetchRequest) -> () + +public protocol ObjectQueryable { + + func findFirst(entity: T.Type) -> T? + func findFirst(entity: T.Type, customizeFetch: FetchRequestCustomization?) -> T? + + func findFirst(query: ObjectQuery) -> T? + func findFirst(query: ObjectQuery, customizeFetch: FetchRequestCustomization?) -> T? + + func findAll(entity: T.Type) -> [T]? + func findAll(entity: T.Type, customizeFetch: FetchRequestCustomization?) -> [T]? + + func findAll(query: ObjectQuery) -> [T]? + func findAll(query: ObjectQuery, customizeFetch: FetchRequestCustomization?) -> [T]? + + func count(entity: T.Type) -> Int + func count(query: ObjectQuery) -> Int +} diff --git a/HardcoreData/Queryable.swift b/HardcoreData/ValueQueryable.swift similarity index 83% rename from HardcoreData/Queryable.swift rename to HardcoreData/ValueQueryable.swift index f5bd378..d07cffa 100644 --- a/HardcoreData/Queryable.swift +++ b/HardcoreData/ValueQueryable.swift @@ -1,5 +1,5 @@ // -// Queryable.swift +// ValueQueryable.swift // HardcoreData // // Copyright (c) 2014 John Rommel Estropia @@ -23,16 +23,16 @@ // SOFTWARE. // -import UIKit +import Foundation -public protocol Queryable { +public protocol ValueQueryable { func findFirst(entity: T.Type) -> T? - func findFirst(query: Query) -> T? + func findFirst(query: ObjectQuery) -> T? func findAll(entity: T.Type) -> [T]? - func findAll(query: Query) -> [T]? + func findAll(query: ObjectQuery) -> [T]? func count(entity: T.Type) -> Int - func count(query: Query) -> Int + func count(query: ObjectQuery) -> Int } diff --git a/HardcoreDataTests/HardcoreDataTests.swift b/HardcoreDataTests/HardcoreDataTests.swift index b0ac166..f943549 100644 --- a/HardcoreDataTests/HardcoreDataTests.swift +++ b/HardcoreDataTests/HardcoreDataTests.swift @@ -30,18 +30,17 @@ import HardcoreData class HardcoreDataTests: XCTestCase { override func setUp() { + super.setUp() - // Put setup code here. This method is called before the invocation of each test method in the class. } override func tearDown() { - // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() } func testExample() { - // This is an example of a functional test case. - NSLog("Test aaaa") + #if DEBUG let resetStoreOnMigrationFailure = true #else @@ -56,33 +55,33 @@ class HardcoreDataTests: XCTestCase { reason: error.localizedDescription, userInfo: error.userInfo).raise() - default: break + default: + break } - HardcoreData.performTransaction { (transaction) -> () in + HardcoreData.performTransactionAndWait({ (transaction) -> () in + + let obj = transaction.create(TestEntity1) + obj.testEntityID = 1 + obj.testString = "lololol" + obj.testNumber = 42 + obj.testDate = NSDate() + + transaction.commitAndWait() + }) + + HardcoreData.performTransactionAndWait({ (transaction) -> () in - let obj = transaction.findFirst( + let obj = transaction.findAll( TestEntity1 - .WHERE(true) - .SORTEDBY(.Ascending("testEntityID"), .Descending("testString"))) - transaction.commit { (result) -> () in - - switch result { + .WHERE("testEntityID", isEqualTo: 1) + .SORTEDBY(.Ascending("testEntityID"), .Descending("testString")), + customizeFetch: { (fetchRequest) -> () in - case .Success(let hasChanges): - dump(hasChanges, name: "hasChanges") - case .Failure(let error): - dump(error, name: "error") + fetchRequest.includesPendingChanges = true } - } - } + ) + NSLog("%@", obj ?? []) + }) } - - func testPerformanceExample() { - // This is an example of a performance test case. - self.measureBlock() { - // Put the code you want to measure the time of here. - } - } - } diff --git a/HardcoreDataTests/Model.xcdatamodeld/Model.xcdatamodel/contents b/HardcoreDataTests/Model.xcdatamodeld/Model.xcdatamodel/contents index e6db5ed..a120a62 100644 --- a/HardcoreDataTests/Model.xcdatamodeld/Model.xcdatamodel/contents +++ b/HardcoreDataTests/Model.xcdatamodeld/Model.xcdatamodel/contents @@ -1,6 +1,6 @@ - + diff --git a/HardcoreDataTests/TestEntity1.swift b/HardcoreDataTests/TestEntity1.swift index 2cab1ac..9fbbfe3 100644 --- a/HardcoreDataTests/TestEntity1.swift +++ b/HardcoreDataTests/TestEntity1.swift @@ -28,9 +28,9 @@ import CoreData class TestEntity1: NSManagedObject { - @NSManaged var testEntityID: NSNumber - @NSManaged var testString: String - @NSManaged var testNumber: NSNumber - @NSManaged var testDate: NSDate + @NSManaged var testEntityID: NSNumber? + @NSManaged var testString: String? + @NSManaged var testNumber: NSNumber? + @NSManaged var testDate: NSDate? } diff --git a/Libraries/GCDKit b/Libraries/GCDKit index 5a671ab..0ded127 160000 --- a/Libraries/GCDKit +++ b/Libraries/GCDKit @@ -1 +1 @@ -Subproject commit 5a671ab6413c9bff9642d5ca6a9127740082a88e +Subproject commit 0ded12726f61f99cd814f3e10970172b729e31c3