elegant queries

This commit is contained in:
John Rommel Estropia
2014-12-14 23:47:18 +09:00
parent 45a65d9262
commit 6b8bb3e434
14 changed files with 237 additions and 145 deletions

View File

@@ -25,9 +25,10 @@
B5D8080E1A3471A500A44484 /* GCDKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5D808021A34715700A44484 /* GCDKit.framework */; }; B5D8080E1A3471A500A44484 /* GCDKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5D808021A34715700A44484 /* GCDKit.framework */; };
B5D808161A34947300A44484 /* NotificationObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D808151A34947300A44484 /* NotificationObserver.swift */; }; B5D808161A34947300A44484 /* NotificationObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D808151A34947300A44484 /* NotificationObserver.swift */; };
B5D8081A1A3495BD00A44484 /* NSObject+HardcoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D808191A3495BD00A44484 /* NSObject+HardcoreData.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 */; }; B5E209E01A0726460089C9D4 /* NSManagedObject+HardcoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E209DF1A0726460089C9D4 /* NSManagedObject+HardcoreData.swift */; };
B5E472271A35E84700804CE1 /* Queryable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E472261A35E84700804CE1 /* Queryable.swift */; }; B5E472271A35E84700804CE1 /* ObjectQueryable.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E472261A35E84700804CE1 /* ObjectQueryable.swift */; };
B5F539901A17A6FC00EC763B /* Query.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F5398F1A17A6FC00EC763B /* Query.swift */; }; B5F539901A17A6FC00EC763B /* ObjectQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F5398F1A17A6FC00EC763B /* ObjectQuery.swift */; };
/* End PBXBuildFile section */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@@ -77,10 +78,11 @@
B5D806C51A34715700A44484 /* GCDKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = GCDKit.xcodeproj; sourceTree = "<group>"; }; B5D806C51A34715700A44484 /* GCDKit.xcodeproj */ = {isa = PBXFileReference; lastKnownFileType = "wrapper.pb-project"; path = GCDKit.xcodeproj; sourceTree = "<group>"; };
B5D808151A34947300A44484 /* NotificationObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationObserver.swift; sourceTree = "<group>"; }; B5D808151A34947300A44484 /* NotificationObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NotificationObserver.swift; sourceTree = "<group>"; };
B5D808191A3495BD00A44484 /* NSObject+HardcoreData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+HardcoreData.swift"; sourceTree = "<group>"; }; B5D808191A3495BD00A44484 /* NSObject+HardcoreData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSObject+HardcoreData.swift"; sourceTree = "<group>"; };
B5E186341A3C5CDE002171F0 /* ValueQueryable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ValueQueryable.swift; sourceTree = "<group>"; };
B5E209DF1A0726460089C9D4 /* NSManagedObject+HardcoreData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObject+HardcoreData.swift"; sourceTree = "<group>"; }; B5E209DF1A0726460089C9D4 /* NSManagedObject+HardcoreData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObject+HardcoreData.swift"; sourceTree = "<group>"; };
B5E472261A35E84700804CE1 /* Queryable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Queryable.swift; sourceTree = "<group>"; }; B5E472261A35E84700804CE1 /* ObjectQueryable.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjectQueryable.swift; sourceTree = "<group>"; };
B5F3D98419F3EB8E009690A6 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; }; B5F3D98419F3EB8E009690A6 /* LICENSE */ = {isa = PBXFileReference; lastKnownFileType = text; path = LICENSE; sourceTree = "<group>"; };
B5F5398F1A17A6FC00EC763B /* Query.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Query.swift; sourceTree = "<group>"; }; B5F5398F1A17A6FC00EC763B /* ObjectQuery.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ObjectQuery.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@@ -129,7 +131,6 @@
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
B5D399F019FC818E000E91BB /* DataStack.swift */, B5D399F019FC818E000E91BB /* DataStack.swift */,
B5E472261A35E84700804CE1 /* Queryable.swift */,
B5CFF23F19FD383100D6DFC4 /* DataTransaction.swift */, B5CFF23F19FD383100D6DFC4 /* DataTransaction.swift */,
2F03A53519C5C6DA005002A5 /* HardcoreData.h */, 2F03A53519C5C6DA005002A5 /* HardcoreData.h */,
2F291E2619C6D3CF007AF63F /* HardcoreData.swift */, 2F291E2619C6D3CF007AF63F /* HardcoreData.swift */,
@@ -137,7 +138,9 @@
B5E209DF1A0726460089C9D4 /* NSManagedObject+HardcoreData.swift */, B5E209DF1A0726460089C9D4 /* NSManagedObject+HardcoreData.swift */,
B5CFF23D19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift */, B5CFF23D19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift */,
B5D399F419FCF4E0000E91BB /* NSPersistentStoreCoordinator+HardcoreData.swift */, B5D399F419FCF4E0000E91BB /* NSPersistentStoreCoordinator+HardcoreData.swift */,
B5F5398F1A17A6FC00EC763B /* Query.swift */, B5F5398F1A17A6FC00EC763B /* ObjectQuery.swift */,
B5E472261A35E84700804CE1 /* ObjectQueryable.swift */,
B5E186341A3C5CDE002171F0 /* ValueQueryable.swift */,
B5D1E22919FA9E63003B2874 /* PersistentStoreResult.swift */, B5D1E22919FA9E63003B2874 /* PersistentStoreResult.swift */,
B5CFD36D1A0775F000B7885F /* SaveResult.swift */, B5CFD36D1A0775F000B7885F /* SaveResult.swift */,
B5D808141A34945A00A44484 /* Internal */, B5D808141A34945A00A44484 /* Internal */,
@@ -349,17 +352,18 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
B5F539901A17A6FC00EC763B /* Query.swift in Sources */, B5F539901A17A6FC00EC763B /* ObjectQuery.swift in Sources */,
B5CFF24019FD383100D6DFC4 /* DataTransaction.swift in Sources */, B5CFF24019FD383100D6DFC4 /* DataTransaction.swift in Sources */,
B5D399F519FCF4E0000E91BB /* NSPersistentStoreCoordinator+HardcoreData.swift in Sources */, B5D399F519FCF4E0000E91BB /* NSPersistentStoreCoordinator+HardcoreData.swift in Sources */,
B5CFD36E1A0775F000B7885F /* SaveResult.swift in Sources */, B5CFD36E1A0775F000B7885F /* SaveResult.swift in Sources */,
B5D1E22C19FA9FBC003B2874 /* NSError+HardcoreData.swift in Sources */, B5D1E22C19FA9FBC003B2874 /* NSError+HardcoreData.swift in Sources */,
B5CFF23E19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift in Sources */, B5CFF23E19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift in Sources */,
B5E186351A3C5CDE002171F0 /* ValueQueryable.swift in Sources */,
2F291E2719C6D3CF007AF63F /* HardcoreData.swift in Sources */, 2F291E2719C6D3CF007AF63F /* HardcoreData.swift in Sources */,
B5D8081A1A3495BD00A44484 /* NSObject+HardcoreData.swift in Sources */, B5D8081A1A3495BD00A44484 /* NSObject+HardcoreData.swift in Sources */,
B5E209E01A0726460089C9D4 /* NSManagedObject+HardcoreData.swift in Sources */, B5E209E01A0726460089C9D4 /* NSManagedObject+HardcoreData.swift in Sources */,
B5D1E22A19FA9E63003B2874 /* PersistentStoreResult.swift in Sources */, B5D1E22A19FA9E63003B2874 /* PersistentStoreResult.swift in Sources */,
B5E472271A35E84700804CE1 /* Queryable.swift in Sources */, B5E472271A35E84700804CE1 /* ObjectQueryable.swift in Sources */,
B5D399F119FC818E000E91BB /* DataStack.swift in Sources */, B5D399F119FC818E000E91BB /* DataStack.swift in Sources */,
B5D808161A34947300A44484 /* NotificationObserver.swift in Sources */, B5D808161A34947300A44484 /* NotificationObserver.swift in Sources */,
); );

View File

@@ -266,9 +266,9 @@ public class DataStack: NSObject {
Begins a transaction synchronously where NSManagedObject creates, updates, and deletes can be made. 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. :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( return DataTransaction(
mainContext: self.mainContext, mainContext: self.mainContext,

View File

@@ -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(_:). 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 // MARK: - Public
@@ -102,9 +102,9 @@ public class DataTransaction {
HardcoreData.assert(!self.isCommitted, "Attempted to commit a DataTransaction more than once.") HardcoreData.assert(!self.isCommitted, "Attempted to commit a DataTransaction more than once.")
self.isCommitted = true self.isCommitted = true
self.context.saveAsynchronouslyWithCompletion { [weak self] (result) -> () in self.context.saveAsynchronouslyWithCompletion { (result) -> () in
self?.result = result self.result = result
completion(result: result) completion(result: result)
} }
} }
@@ -114,15 +114,13 @@ public class DataTransaction {
:returns: a SaveResult value indicating success or failure. :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.transactionQueue.isCurrentExecutionContext() == true, "Attempted to commit a DataTransaction outside a transaction queue.")
HardcoreData.assert(!self.isCommitted, "Attempted to commit a DataTransaction more than once.") HardcoreData.assert(!self.isCommitted, "Attempted to commit a DataTransaction more than once.")
self.isCommitted = true self.isCommitted = true
let result = self.context.saveSynchronously() self.result = self.context.saveSynchronously()
self.result = result
return result
} }
@@ -141,24 +139,16 @@ public class DataTransaction {
self.transactionQueue.barrierAsync { self.transactionQueue.barrierAsync {
self.closure(transaction: self) self.closure(transaction: self)
if !self.isCommitted {
self.commit { (result) -> () in }
}
} }
} }
internal func performAndWait() -> SaveResult { internal func performAndWait() -> SaveResult? {
self.transactionQueue.barrierSync { self.transactionQueue.barrierSync {
self.closure(transaction: self) self.closure(transaction: self)
if !self.isCommitted {
self.commitAndWait()
}
} }
return self.result! return self.result
} }
@@ -174,34 +164,54 @@ public class DataTransaction {
// MARK: - DataContextProvider // MARK: - DataContextProvider
extension DataTransaction: Queryable { extension DataTransaction: ObjectQueryable {
public func findFirst<T: NSManagedObject>(entity: T.Type) -> T? { public func findFirst<T: NSManagedObject>(entity: T.Type) -> T? {
return self.context.findFirst(entity) return self.context.findFirst(entity)
} }
public func findFirst<T: NSManagedObject>(query: Query<T>) -> T? { public func findFirst<T: NSManagedObject>(entity: T.Type, customizeFetch: FetchRequestCustomization?) -> T? {
return self.context.findFirst(entity, customizeFetch: customizeFetch)
}
public func findFirst<T: NSManagedObject>(query: ObjectQuery<T>) -> T? {
return self.context.findFirst(query) return self.context.findFirst(query)
} }
public func findFirst<T: NSManagedObject>(query: ObjectQuery<T>, customizeFetch: FetchRequestCustomization?) -> T? {
return self.context.findFirst(query, customizeFetch: customizeFetch)
}
public func findAll<T: NSManagedObject>(entity: T.Type) -> [T]? { public func findAll<T: NSManagedObject>(entity: T.Type) -> [T]? {
return self.context.findAll(entity) return self.context.findAll(entity)
} }
public func findAll<T: NSManagedObject>(query: Query<T>) -> [T]? { public func findAll<T: NSManagedObject>(entity: T.Type, customizeFetch: FetchRequestCustomization?) -> [T]? {
return self.context.findAll(entity, customizeFetch: customizeFetch)
}
public func findAll<T: NSManagedObject>(query: ObjectQuery<T>) -> [T]? {
return self.context.findAll(query) return self.context.findAll(query)
} }
public func findAll<T: NSManagedObject>(query: ObjectQuery<T>, customizeFetch: FetchRequestCustomization?) -> [T]? {
return self.context.findAll(query, customizeFetch: customizeFetch)
}
public func count<T: NSManagedObject>(entity: T.Type) -> Int { public func count<T: NSManagedObject>(entity: T.Type) -> Int {
return self.context.count(entity) return self.context.count(entity)
} }
public func count<T: NSManagedObject>(query: Query<T>) -> Int { public func count<T: NSManagedObject>(query: ObjectQuery<T>) -> Int {
return self.context.count(query) return self.context.count(query)
} }

View File

@@ -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. The HardcoreData struct is the main entry point for all other APIs.
*/ */
public struct HardcoreData { 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. 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. :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) return self.defaultStack.performTransactionAndWait(closure)
} }
@@ -87,7 +85,7 @@ public struct HardcoreData {
case Trace case Trace
case Notice case Notice
case Alert case Warning
case Fatal case Fatal
} }

View File

@@ -48,32 +48,37 @@ public extension NSManagedObject {
// MARK: Querying // MARK: Querying
public class func WHERE(predicate: NSPredicate) -> Query<NSManagedObject> { public class func WHERE(predicate: NSPredicate) -> ObjectQuery<NSManagedObject> {
return Query(entity: self).WHERE(predicate) return ObjectQuery(entity: self).WHERE(predicate)
} }
public class func WHERE(value: Bool) -> Query<NSManagedObject> { public class func WHERE(value: Bool) -> ObjectQuery<NSManagedObject> {
return self.WHERE(NSPredicate(value: value)) return self.WHERE(NSPredicate(value: value))
} }
public class func WHERE(format: String, _ args: CVarArgType...) -> Query<NSManagedObject> { public class func WHERE(format: String, _ args: CVarArgType...) -> ObjectQuery<NSManagedObject> {
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<NSManagedObject> { public class func WHERE(format: String, argumentArray: [AnyObject]?) -> ObjectQuery<NSManagedObject> {
return self.WHERE(NSPredicate(format: format, argumentArray: argumentArray)) return self.WHERE(NSPredicate(format: format, argumentArray: argumentArray))
} }
public class func SORTEDBY(order: [SortOrder]) -> Query<NSManagedObject> { public class func WHERE(attributeName: AttributeName, isEqualTo value: NSObject?) -> ObjectQuery<NSManagedObject> {
return Query(entity: self).SORTEDBY(order) return ObjectQuery(entity: self).WHERE(attributeName, isEqualTo: value)
} }
public class func SORTEDBY(order: SortOrder, _ subOrder: SortOrder...) -> Query<NSManagedObject> { public class func SORTEDBY(order: [SortOrder]) -> ObjectQuery<NSManagedObject> {
return ObjectQuery(entity: self).SORTEDBY(order)
}
public class func SORTEDBY(order: SortOrder, _ subOrder: SortOrder...) -> ObjectQuery<NSManagedObject> {
return self.SORTEDBY([order] + subOrder) return self.SORTEDBY([order] + subOrder)
} }

View File

@@ -101,7 +101,7 @@ public extension NSManagedObjectContext {
self.reset() self.reset()
if let completion = completion { if let completion = completion {
GCDBlock.async(.Main) { GCDQueue.Main.async {
completion(result: SaveResult(hasChanges: false)) completion(result: SaveResult(hasChanges: false))
} }
@@ -126,7 +126,7 @@ public extension NSManagedObjectContext {
if let completion = completion { if let completion = completion {
GCDBlock.async(.Main) { GCDQueue.Main.async {
completion(result: SaveResult(hasChanges: true)) completion(result: SaveResult(hasChanges: true))
} }
@@ -139,7 +139,7 @@ public extension NSManagedObjectContext {
"Failed to save NSManagedObjectContext.") "Failed to save NSManagedObjectContext.")
if let completion = completion { if let completion = completion {
GCDBlock.async(.Main) { GCDQueue.Main.async {
completion(result: SaveResult(error)) completion(result: SaveResult(error))
} }
@@ -147,7 +147,7 @@ public extension NSManagedObjectContext {
} }
else if let completion = completion { else if let completion = completion {
GCDBlock.async(.Main) { GCDQueue.Main.async {
completion(result: SaveResult(hasChanges: false)) completion(result: SaveResult(hasChanges: false))
} }
@@ -274,30 +274,40 @@ public extension NSManagedObjectContext {
// MARK: - DataContextProvider // MARK: - DataContextProvider
extension NSManagedObjectContext: Queryable { extension NSManagedObjectContext: ObjectQueryable {
public func findFirst<T: NSManagedObject>(entity: T.Type) -> T? { public func findFirst<T: NSManagedObject>(entity: T.Type) -> T? {
return self.findFirst(Query(entity: entity)) return self.findFirst(entity, customizeFetch: nil)
} }
public func findFirst<T: NSManagedObject>(query: Query<T>) -> T? { public func findFirst<T: NSManagedObject>(entity: T.Type, customizeFetch: FetchRequestCustomization?) -> T? {
var query = query return self.findFirst(ObjectQuery(entity: entity), customizeFetch: customizeFetch)
query.fetchLimit = 1 }
let fetchRequest = query.createFetchRequestInContext(self)
public func findFirst<T: NSManagedObject>(query: ObjectQuery<T>) -> T? {
return self.findFirst(query, customizeFetch: nil)
}
public func findFirst<T: NSManagedObject>(query: ObjectQuery<T>, customizeFetch: FetchRequestCustomization?) -> T? {
let fetchRequest = query.createFetchRequestForContext(self)
customizeFetch?(fetchRequest: fetchRequest)
fetchRequest.fetchLimit = 1
fetchRequest.resultType = .ManagedObjectResultType
var fetchResults: [T]? var fetchResults: [T]?
var error: NSError?
self.performBlockAndWait { self.performBlockAndWait {
var error: NSError?
fetchResults = self.executeFetchRequest(fetchRequest, error: &error) as? [T] fetchResults = self.executeFetchRequest(fetchRequest, error: &error) as? [T]
if fetchResults == nil { }
if fetchResults == nil {
HardcoreData.handleError(
error!, HardcoreData.handleError(error!, "Failed executing fetch request.")
"Failed executing fetch request.") return nil
}
} }
return fetchResults?.first return fetchResults?.first
@@ -305,24 +315,34 @@ extension NSManagedObjectContext: Queryable {
public func findAll<T: NSManagedObject>(entity: T.Type) -> [T]? { public func findAll<T: NSManagedObject>(entity: T.Type) -> [T]? {
return self.findAll(Query(entity: entity)) return self.findAll(entity, customizeFetch: nil)
} }
public func findAll<T: NSManagedObject>(query: Query<T>) -> [T]? { public func findAll<T: NSManagedObject>(entity: T.Type, customizeFetch: FetchRequestCustomization?) -> [T]? {
let fetchRequest = query.createFetchRequestInContext(self) return self.findAll(ObjectQuery(entity: entity), customizeFetch: customizeFetch)
}
public func findAll<T: NSManagedObject>(query: ObjectQuery<T>) -> [T]? {
return self.findAll(query, customizeFetch: nil)
}
public func findAll<T: NSManagedObject>(query: ObjectQuery<T>, customizeFetch: FetchRequestCustomization?) -> [T]? {
let fetchRequest = query.createFetchRequestForContext(self)
fetchRequest.fetchLimit = 0
var fetchResults: [T]? var fetchResults: [T]?
var error: NSError?
self.performBlockAndWait { self.performBlockAndWait {
var error: NSError?
fetchResults = self.executeFetchRequest(fetchRequest, error: &error) as? [T] fetchResults = self.executeFetchRequest(fetchRequest, error: &error) as? [T]
if fetchResults == nil { }
if fetchResults == nil {
HardcoreData.handleError(
error!, HardcoreData.handleError(error!, "Failed executing fetch request.")
"Failed executing fetch request.") return nil
}
} }
return fetchResults return fetchResults
@@ -330,12 +350,12 @@ extension NSManagedObjectContext: Queryable {
public func count<T: NSManagedObject>(entity: T.Type) -> Int { public func count<T: NSManagedObject>(entity: T.Type) -> Int {
return self.count(Query(entity: entity)) return self.count(ObjectQuery(entity: entity))
} }
public func count<T: NSManagedObject>(query: Query<T>) -> Int { public func count<T: NSManagedObject>(query: ObjectQuery<T>) -> Int {
let fetchRequest = query.createFetchRequestInContext(self) let fetchRequest = query.createFetchRequestForContext(self)
var count = 0 var count = 0
var error: NSError? var error: NSError?
@@ -345,9 +365,7 @@ extension NSManagedObjectContext: Queryable {
} }
if count == NSNotFound { if count == NSNotFound {
HardcoreData.handleError( HardcoreData.handleError( error!, "Failed executing fetch request.")
error!,
"Failed executing fetch request.")
return 0 return 0
} }

View File

@@ -25,7 +25,7 @@
import Foundation import Foundation
internal class NotificationObserver { internal final class NotificationObserver {
let notificationName: String let notificationName: String
let object: AnyObject? let object: AnyObject?

View File

@@ -1,5 +1,5 @@
// //
// Query.swift // ObjectQuery.swift
// HardcoreData // HardcoreData
// //
// Copyright (c) 2014 John Rommel Estropia // Copyright (c) 2014 John Rommel Estropia
@@ -35,39 +35,55 @@ public enum SortOrder {
case Descending(AttributeName) case Descending(AttributeName)
} }
public class Query<T: NSManagedObject> { public final class ObjectQuery<T: NSManagedObject> {
public var fetchLimit: Int = 0
public var fetchOffset: Int = 0
public var fetchBatchSize: Int = 0
public var entityName: String { public var entityName: String {
return self.entity.entityName return self.entity.entityName
} }
public func WHERE(predicate: NSPredicate) -> Query<T> { public func WHERE(predicate: NSPredicate) -> ObjectQuery<T> {
if self.predicate != nil { 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 self.predicate = predicate
return self return self
} }
public func WHERE(value: Bool) -> Query<T> { public func WHERE(value: Bool) -> ObjectQuery<T> {
return self.WHERE(NSPredicate(value: value)) return self.WHERE(NSPredicate(value: value))
} }
public func WHERE(format: String, _ args: CVarArgType...) -> Query<T> { public func WHERE(format: String, _ args: CVarArgType...) -> ObjectQuery<T> {
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<T> { public func WHERE(format: String, argumentArray: [AnyObject]?) -> ObjectQuery<T> {
return self.WHERE(NSPredicate(format: format, argumentArray: argumentArray)) return self.WHERE(NSPredicate(format: format, argumentArray: argumentArray))
} }
public func SORTEDBY(order: [SortOrder]) -> Query<T> { public func WHERE(attributeName: AttributeName, isEqualTo value: NSObject?) -> ObjectQuery<T> {
return self.WHERE(value == nil
? NSPredicate(format: "\(attributeName) == nil")!
: NSPredicate(format: "\(attributeName) == %@", value!)!)
}
public func SORTEDBY(order: [SortOrder]) -> ObjectQuery<T> {
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 self.sortDescriptors = order.map { sortOrder in
switch sortOrder { switch sortOrder {
@@ -86,25 +102,11 @@ public class Query<T: NSManagedObject> {
return self return self
} }
public func SORTEDBY(order: SortOrder, _ subOrder: SortOrder...) -> Query<T> { public func SORTEDBY(order: SortOrder, _ subOrder: SortOrder...) -> ObjectQuery<T> {
return self.SORTEDBY([order] + subOrder) 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 // MARK: Internal
internal init(entity: T.Type) { internal init(entity: T.Type) {
@@ -112,12 +114,21 @@ public class Query<T: NSManagedObject> {
self.entity = entity 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 // MARK: Private
private let entity: T.Type private let entity: T.Type
public var fetchLimit: Int = 0 private var predicate: NSPredicate?
public var fetchOffset: Int = 0 private var sortDescriptors: [NSSortDescriptor]?
public var fetchBatchSize: Int = 0
public var predicate: NSPredicate?
public var sortDescriptors: [NSSortDescriptor]?
} }

View File

@@ -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<T: NSManagedObject>(entity: T.Type) -> T?
func findFirst<T: NSManagedObject>(entity: T.Type, customizeFetch: FetchRequestCustomization?) -> T?
func findFirst<T: NSManagedObject>(query: ObjectQuery<T>) -> T?
func findFirst<T: NSManagedObject>(query: ObjectQuery<T>, customizeFetch: FetchRequestCustomization?) -> T?
func findAll<T: NSManagedObject>(entity: T.Type) -> [T]?
func findAll<T: NSManagedObject>(entity: T.Type, customizeFetch: FetchRequestCustomization?) -> [T]?
func findAll<T: NSManagedObject>(query: ObjectQuery<T>) -> [T]?
func findAll<T: NSManagedObject>(query: ObjectQuery<T>, customizeFetch: FetchRequestCustomization?) -> [T]?
func count<T: NSManagedObject>(entity: T.Type) -> Int
func count<T: NSManagedObject>(query: ObjectQuery<T>) -> Int
}

View File

@@ -1,5 +1,5 @@
// //
// Queryable.swift // ValueQueryable.swift
// HardcoreData // HardcoreData
// //
// Copyright (c) 2014 John Rommel Estropia // Copyright (c) 2014 John Rommel Estropia
@@ -23,16 +23,16 @@
// SOFTWARE. // SOFTWARE.
// //
import UIKit import Foundation
public protocol Queryable { public protocol ValueQueryable {
func findFirst<T: NSManagedObject>(entity: T.Type) -> T? func findFirst<T: NSManagedObject>(entity: T.Type) -> T?
func findFirst<T: NSManagedObject>(query: Query<T>) -> T? func findFirst<T: NSManagedObject>(query: ObjectQuery<T>) -> T?
func findAll<T: NSManagedObject>(entity: T.Type) -> [T]? func findAll<T: NSManagedObject>(entity: T.Type) -> [T]?
func findAll<T: NSManagedObject>(query: Query<T>) -> [T]? func findAll<T: NSManagedObject>(query: ObjectQuery<T>) -> [T]?
func count<T: NSManagedObject>(entity: T.Type) -> Int func count<T: NSManagedObject>(entity: T.Type) -> Int
func count<T: NSManagedObject>(query: Query<T>) -> Int func count<T: NSManagedObject>(query: ObjectQuery<T>) -> Int
} }

View File

@@ -30,18 +30,17 @@ import HardcoreData
class HardcoreDataTests: XCTestCase { class HardcoreDataTests: XCTestCase {
override func setUp() { override func setUp() {
super.setUp() super.setUp()
// Put setup code here. This method is called before the invocation of each test method in the class.
} }
override func tearDown() { override func tearDown() {
// Put teardown code here. This method is called after the invocation of each test method in the class.
super.tearDown() super.tearDown()
} }
func testExample() { func testExample() {
// This is an example of a functional test case.
NSLog("Test aaaa")
#if DEBUG #if DEBUG
let resetStoreOnMigrationFailure = true let resetStoreOnMigrationFailure = true
#else #else
@@ -56,33 +55,33 @@ class HardcoreDataTests: XCTestCase {
reason: error.localizedDescription, reason: error.localizedDescription,
userInfo: error.userInfo).raise() 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 TestEntity1
.WHERE(true) .WHERE("testEntityID", isEqualTo: 1)
.SORTEDBY(.Ascending("testEntityID"), .Descending("testString"))) .SORTEDBY(.Ascending("testEntityID"), .Descending("testString")),
transaction.commit { (result) -> () in customizeFetch: { (fetchRequest) -> () in
switch result {
case .Success(let hasChanges): fetchRequest.includesPendingChanges = true
dump(hasChanges, name: "hasChanges")
case .Failure(let error):
dump(error, name: "error")
} }
} )
} 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.
}
}
} }

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="6254" systemVersion="14B25" minimumToolsVersion="Xcode 4.3" macOSVersion="Automatic" iOSVersion="Automatic"> <model userDefinedModelVersionIdentifier="" type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="6254" systemVersion="14B25" minimumToolsVersion="Xcode 4.3" macOSVersion="Automatic" iOSVersion="Automatic">
<entity name="TestEntity1" representedClassName="TestEntity1" syncable="YES"> <entity name="TestEntity1" representedClassName="HardcoreDataTests.TestEntity1" syncable="YES">
<attribute name="testDate" optional="YES" attributeType="Date" syncable="YES"/> <attribute name="testDate" optional="YES" attributeType="Date" syncable="YES"/>
<attribute name="testEntityID" attributeType="Integer 64" syncable="YES"/> <attribute name="testEntityID" attributeType="Integer 64" syncable="YES"/>
<attribute name="testNumber" optional="YES" attributeType="Integer 32" defaultValueString="0" syncable="YES"/> <attribute name="testNumber" optional="YES" attributeType="Integer 32" defaultValueString="0" syncable="YES"/>

View File

@@ -28,9 +28,9 @@ import CoreData
class TestEntity1: NSManagedObject { class TestEntity1: NSManagedObject {
@NSManaged var testEntityID: NSNumber @NSManaged var testEntityID: NSNumber?
@NSManaged var testString: String @NSManaged var testString: String?
@NSManaged var testNumber: NSNumber @NSManaged var testNumber: NSNumber?
@NSManaged var testDate: NSDate @NSManaged var testDate: NSDate?
} }