added ability to query aggregates and attributes straight from the persistent store

This commit is contained in:
John Rommel Estropia
2015-03-18 01:25:58 +09:00
parent 13ca911d77
commit bf41605da9
28 changed files with 1311 additions and 488 deletions

View File

@@ -11,10 +11,14 @@
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 */; };
B52B68BA1AAB46AD00CE7F48 /* ManagedObjectController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52B68B91AAB46AD00CE7F48 /* ManagedObjectController.swift */; };
B52B68BC1AAB46BD00CE7F48 /* ManagedObjectListController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52B68BB1AAB46BD00CE7F48 /* ManagedObjectListController.swift */; };
B52B68BE1AAB484C00CE7F48 /* DataStack+Observing.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52B68BD1AAB484C00CE7F48 /* DataStack+Observing.swift */; };
B52B68C01AAB9DAD00CE7F48 /* ManagedObjectListObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52B68BF1AAB9DAD00CE7F48 /* ManagedObjectListObserver.swift */; };
B52B68C21AAF554600CE7F48 /* ManagedObjectListSectionInfo.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52B68C11AAF554600CE7F48 /* ManagedObjectListSectionInfo.swift */; };
B5398AA21AA8938D00B66388 /* DetachedDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5398AA11AA8938D00B66388 /* DetachedDataTransaction.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 */; };
B54D53071AB3538500D55BA8 /* QueryClause.swift in Sources */ = {isa = PBXBuildFile; fileRef = B54D53061AB3538500D55BA8 /* QueryClause.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 */; };
@@ -25,6 +29,8 @@
B5CFF23E19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CFF23D19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift */; };
B5CFF24019FD383100D6DFC4 /* BaseDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5CFF23F19FD383100D6DFC4 /* BaseDataTransaction.swift */; };
B5D022661A90CD340070CA63 /* DataStack+Transaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D022651A90CD340070CA63 /* DataStack+Transaction.swift */; };
B5D10DC01AB42C85004B4EEA /* GroupBy.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D10DBF1AB42C85004B4EEA /* GroupBy.swift */; };
B5D10DC21AB4590F004B4EEA /* Select.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D10DC11AB4590F004B4EEA /* Select.swift */; };
B5D19BFB1AA14063001D1A99 /* AsynchronousDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D19BFA1AA14063001D1A99 /* AsynchronousDataTransaction.swift */; };
B5D19BFF1AA14351001D1A99 /* SynchronousDataTransaction.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D19BFE1AA14351001D1A99 /* SynchronousDataTransaction.swift */; };
B5D19C011AA15E1F001D1A99 /* HardcoreData+Logging.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D19C001AA15E1F001D1A99 /* HardcoreData+Logging.swift */; };
@@ -33,7 +39,6 @@
B5D372841A39CD6900F583D9 /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B5D372821A39CD6900F583D9 /* Model.xcdatamodeld */; };
B5D372861A39CDDB00F583D9 /* TestEntity1.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D372851A39CDDB00F583D9 /* TestEntity1.swift */; };
B5D399F119FC818E000E91BB /* DataStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D399F019FC818E000E91BB /* DataStack.swift */; };
B5D399F519FCF4E0000E91BB /* NSPersistentStoreCoordinator+HardcoreData.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D399F419FCF4E0000E91BB /* NSPersistentStoreCoordinator+HardcoreData.swift */; };
B5D39A0219FD00C9000E91BB /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5D39A0119FD00C9000E91BB /* Foundation.framework */; };
B5D39A0419FD00DE000E91BB /* UIKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5D39A0319FD00DE000E91BB /* UIKit.framework */; };
B5D5E0CF1A4D6AAB006468AF /* TestEntity2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D5E0CE1A4D6AAB006468AF /* TestEntity2.swift */; };
@@ -47,7 +52,7 @@
B5F409EB1A8B199600A228EA /* DefaultLogger.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F409EA1A8B199600A228EA /* DefaultLogger.swift */; };
B5F409ED1A8B200700A228EA /* NSManagedObjectContext+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F409EC1A8B200700A228EA /* NSManagedObjectContext+Querying.swift */; };
B5F409EF1A8B243D00A228EA /* BaseDataTransaction+Querying.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F409EE1A8B243D00A228EA /* BaseDataTransaction+Querying.swift */; };
B5F409F11A8B27A600A228EA /* CustomizeQuery.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F409F01A8B27A600A228EA /* CustomizeQuery.swift */; };
B5F409F11A8B27A600A228EA /* CustomizeFetch.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F409F01A8B27A600A228EA /* CustomizeFetch.swift */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -83,10 +88,14 @@
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; };
B52B68B91AAB46AD00CE7F48 /* ManagedObjectController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedObjectController.swift; sourceTree = "<group>"; };
B52B68BB1AAB46BD00CE7F48 /* ManagedObjectListController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedObjectListController.swift; sourceTree = "<group>"; };
B52B68BD1AAB484C00CE7F48 /* DataStack+Observing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DataStack+Observing.swift"; sourceTree = "<group>"; };
B52B68BF1AAB9DAD00CE7F48 /* ManagedObjectListObserver.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedObjectListObserver.swift; sourceTree = "<group>"; };
B52B68C11AAF554600CE7F48 /* ManagedObjectListSectionInfo.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ManagedObjectListSectionInfo.swift; sourceTree = "<group>"; };
B5398AA11AA8938D00B66388 /* DetachedDataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DetachedDataTransaction.swift; sourceTree = "<group>"; };
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>"; };
B54D53061AB3538500D55BA8 /* QueryClause.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = QueryClause.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>"; };
@@ -97,6 +106,8 @@
B5CFF23D19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+HardcoreData.swift"; sourceTree = "<group>"; };
B5CFF23F19FD383100D6DFC4 /* BaseDataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = BaseDataTransaction.swift; sourceTree = "<group>"; };
B5D022651A90CD340070CA63 /* DataStack+Transaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DataStack+Transaction.swift"; sourceTree = "<group>"; };
B5D10DBF1AB42C85004B4EEA /* GroupBy.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GroupBy.swift; sourceTree = "<group>"; };
B5D10DC11AB4590F004B4EEA /* Select.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Select.swift; sourceTree = "<group>"; };
B5D19BFA1AA14063001D1A99 /* AsynchronousDataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AsynchronousDataTransaction.swift; sourceTree = "<group>"; };
B5D19BFE1AA14351001D1A99 /* SynchronousDataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SynchronousDataTransaction.swift; sourceTree = "<group>"; };
B5D19C001AA15E1F001D1A99 /* HardcoreData+Logging.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "HardcoreData+Logging.swift"; sourceTree = "<group>"; };
@@ -105,7 +116,6 @@
B5D372831A39CD6900F583D9 /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = "<group>"; };
B5D372851A39CDDB00F583D9 /* TestEntity1.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestEntity1.swift; sourceTree = "<group>"; };
B5D399F019FC818E000E91BB /* DataStack.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DataStack.swift; sourceTree = "<group>"; };
B5D399F419FCF4E0000E91BB /* NSPersistentStoreCoordinator+HardcoreData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSPersistentStoreCoordinator+HardcoreData.swift"; sourceTree = "<group>"; };
B5D39A0119FD00C9000E91BB /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; };
B5D39A0319FD00DE000E91BB /* UIKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = UIKit.framework; path = System/Library/Frameworks/UIKit.framework; sourceTree = SDKROOT; };
B5D5E0CE1A4D6AAB006468AF /* TestEntity2.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TestEntity2.swift; sourceTree = "<group>"; };
@@ -120,7 +130,7 @@
B5F409EA1A8B199600A228EA /* DefaultLogger.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DefaultLogger.swift; sourceTree = "<group>"; };
B5F409EC1A8B200700A228EA /* NSManagedObjectContext+Querying.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSManagedObjectContext+Querying.swift"; sourceTree = "<group>"; };
B5F409EE1A8B243D00A228EA /* BaseDataTransaction+Querying.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BaseDataTransaction+Querying.swift"; sourceTree = "<group>"; };
B5F409F01A8B27A600A228EA /* CustomizeQuery.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomizeQuery.swift; sourceTree = "<group>"; };
B5F409F01A8B27A600A228EA /* CustomizeFetch.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CustomizeFetch.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -175,6 +185,7 @@
B5D022621A90BCC60070CA63 /* Saving and Processing */,
B5F409E51A8B11B600A228EA /* Logging */,
B5E126531A7DCCE400AD8B39 /* Fetching and Querying */,
B52B68B61AAB45FB00CE7F48 /* Observing */,
B5D808141A34945A00A44484 /* Internal */,
2F03A53319C5C6DA005002A5 /* Supporting Files */,
);
@@ -220,6 +231,18 @@
name = Frameworks;
sourceTree = "<group>";
};
B52B68B61AAB45FB00CE7F48 /* Observing */ = {
isa = PBXGroup;
children = (
B52B68B91AAB46AD00CE7F48 /* ManagedObjectController.swift */,
B52B68BB1AAB46BD00CE7F48 /* ManagedObjectListController.swift */,
B52B68C11AAF554600CE7F48 /* ManagedObjectListSectionInfo.swift */,
B52B68BF1AAB9DAD00CE7F48 /* ManagedObjectListObserver.swift */,
B52B68BD1AAB484C00CE7F48 /* DataStack+Observing.swift */,
);
name = Observing;
sourceTree = "<group>";
};
B54A9EFF1AA763D100AFEC05 /* Internal */ = {
isa = PBXGroup;
children = (
@@ -228,6 +251,27 @@
name = Internal;
sourceTree = "<group>";
};
B54D53041AB350CF00D55BA8 /* Fetching */ = {
isa = PBXGroup;
children = (
B57078AF1A50392D007E33F2 /* FetchClause.swift */,
B5E126541A7DCE1400AD8B39 /* Where.swift */,
B5E126561A7DCE5900AD8B39 /* SortedBy.swift */,
B5F409F01A8B27A600A228EA /* CustomizeFetch.swift */,
);
name = Fetching;
sourceTree = "<group>";
};
B54D53051AB350FF00D55BA8 /* Querying */ = {
isa = PBXGroup;
children = (
B54D53061AB3538500D55BA8 /* QueryClause.swift */,
B5D10DC11AB4590F004B4EEA /* Select.swift */,
B5D10DBF1AB42C85004B4EEA /* GroupBy.swift */,
);
name = Querying;
sourceTree = "<group>";
};
B595CAC01A9A0AC4009A397F /* Setting Up */ = {
isa = PBXGroup;
children = (
@@ -302,7 +346,6 @@
B595CAC71A9A161B009A397F /* WeakObject.swift */,
B5D808191A3495BD00A44484 /* NSObject+HardcoreData.swift */,
B5CFF23D19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift */,
B5D399F419FCF4E0000E91BB /* NSPersistentStoreCoordinator+HardcoreData.swift */,
);
name = Internal;
sourceTree = "<group>";
@@ -310,15 +353,11 @@
B5E126531A7DCCE400AD8B39 /* Fetching and Querying */ = {
isa = PBXGroup;
children = (
B57078AF1A50392D007E33F2 /* FetchClause.swift */,
B5E126541A7DCE1400AD8B39 /* Where.swift */,
B5E126561A7DCE5900AD8B39 /* SortedBy.swift */,
B5F409F01A8B27A600A228EA /* CustomizeQuery.swift */,
B54A9F021AA7640200AFEC05 /* AggregateFunction.swift */,
B54A9F041AA7644400AFEC05 /* AggregateResultType.swift */,
B582DF811A98B0E7003F09C6 /* HardcoreData+Querying.swift */,
B54A9F061AA7654400AFEC05 /* DataStack+Querying.swift */,
B5F409EE1A8B243D00A228EA /* BaseDataTransaction+Querying.swift */,
B54D53041AB350CF00D55BA8 /* Fetching */,
B54D53051AB350FF00D55BA8 /* Querying */,
B54A9EFF1AA763D100AFEC05 /* Internal */,
);
name = "Fetching and Querying";
@@ -467,7 +506,6 @@
files = (
B5CFF24019FD383100D6DFC4 /* BaseDataTransaction.swift in Sources */,
B5D19C011AA15E1F001D1A99 /* HardcoreData+Logging.swift in Sources */,
B5D399F519FCF4E0000E91BB /* NSPersistentStoreCoordinator+HardcoreData.swift in Sources */,
B5CFD36E1A0775F000B7885F /* SaveResult.swift in Sources */,
B5D022661A90CD340070CA63 /* DataStack+Transaction.swift in Sources */,
B582DF861A98B11B003F09C6 /* HardcoreData+Transaction.swift in Sources */,
@@ -476,27 +514,33 @@
B5CFF23E19FD1D1C00D6DFC4 /* NSManagedObjectContext+HardcoreData.swift in Sources */,
B54A9F071AA7654400AFEC05 /* DataStack+Querying.swift in Sources */,
B5E126571A7DCE5900AD8B39 /* SortedBy.swift in Sources */,
B54D53071AB3538500D55BA8 /* QueryClause.swift in Sources */,
B5F409EF1A8B243D00A228EA /* BaseDataTransaction+Querying.swift in Sources */,
2F291E2719C6D3CF007AF63F /* HardcoreData.swift in Sources */,
B5F409E91A8B11CE00A228EA /* HardcoreDataLogger.swift in Sources */,
B5D8081A1A3495BD00A44484 /* NSObject+HardcoreData.swift in Sources */,
B595CAC41A9A11C1009A397F /* NSManagedObjectContext+Setup.swift in Sources */,
B5D19BFF1AA14351001D1A99 /* SynchronousDataTransaction.swift in Sources */,
B5D10DC21AB4590F004B4EEA /* Select.swift in Sources */,
B5E209E01A0726460089C9D4 /* NSManagedObject+Transaction.swift in Sources */,
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 */,
B5398AA21AA8938D00B66388 /* DetachedDataTransaction.swift in Sources */,
B5F409F11A8B27A600A228EA /* CustomizeQuery.swift in Sources */,
B5F409F11A8B27A600A228EA /* CustomizeFetch.swift in Sources */,
B5F409EB1A8B199600A228EA /* DefaultLogger.swift in Sources */,
B54A9F031AA7640200AFEC05 /* AggregateFunction.swift in Sources */,
B52B68C01AAB9DAD00CE7F48 /* ManagedObjectListObserver.swift in Sources */,
B5D399F119FC818E000E91BB /* DataStack.swift in Sources */,
B5D808161A34947300A44484 /* NotificationObserver.swift in Sources */,
B52B68BE1AAB484C00CE7F48 /* DataStack+Observing.swift in Sources */,
B52B68C21AAF554600CE7F48 /* ManagedObjectListSectionInfo.swift in Sources */,
B57078B01A50392D007E33F2 /* FetchClause.swift in Sources */,
B5F409ED1A8B200700A228EA /* NSManagedObjectContext+Querying.swift in Sources */,
B5D10DC01AB42C85004B4EEA /* GroupBy.swift in Sources */,
B5E126551A7DCE1400AD8B39 /* Where.swift in Sources */,
B52B68BC1AAB46BD00CE7F48 /* ManagedObjectListController.swift in Sources */,
B52B68BA1AAB46AD00CE7F48 /* ManagedObjectController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

View File

@@ -1,99 +0,0 @@
//
// AggregateFunction.swift
// HardcoreData
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import Foundation
import CoreData
// MARK: - AggregateFunction
public enum AggregateFunction {
// MARK: Public
case Average(AttributeName)
case Sum(AttributeName)
case Count(AttributeName)
case Minimum(AttributeName)
case Maximum(AttributeName)
case Medium(AttributeName)
case Mode(AttributeName)
case StandardDeviation(AttributeName)
// MARK: Internal
internal func createExpression() -> NSExpression {
switch self {
case .Average(let attributeName):
return NSExpression(
forFunction: "average:",
arguments: [NSExpression(forKeyPath: "\(attributeName)")]
)
case .Sum(let attributeName):
return NSExpression(
forFunction: "sum:",
arguments: [NSExpression(forKeyPath: "\(attributeName)")]
)
case .Count(let attributeName):
return NSExpression(
forFunction: "count:",
arguments: [NSExpression(forKeyPath: "\(attributeName)")]
)
case .Minimum(let attributeName):
return NSExpression(
forFunction: "min:",
arguments: [NSExpression(forKeyPath: "\(attributeName)")]
)
case .Maximum(let attributeName):
return NSExpression(
forFunction: "max:",
arguments: [NSExpression(forKeyPath: "\(attributeName)")]
)
case .Medium(let attributeName):
return NSExpression(
forFunction: "medium:",
arguments: [NSExpression(forKeyPath: "\(attributeName)")]
)
case .Mode(let attributeName):
return NSExpression(
forFunction: "mode:",
arguments: [NSExpression(forKeyPath: "\(attributeName)")]
)
case .StandardDeviation(let attributeName):
return NSExpression(
forFunction: "stddev:",
arguments: [NSExpression(forKeyPath: "\(attributeName)")]
)
}
}
}

View File

@@ -1,232 +0,0 @@
//
// AggregateResultType.swift
// HardcoreData
//
// Copyright (c) 2015 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import Foundation
import CoreData
// MARK: - AggregateResultType
public protocol AggregateResultType {
static var attributeType: NSAttributeType { get }
static func fromResultObject(result: AnyObject) -> Self
}
extension Bool: AggregateResultType {
public static var attributeType: NSAttributeType {
return .BooleanAttributeType
}
public static func fromResultObject(result: AnyObject) -> Bool {
return (result as! NSNumber).boolValue
}
}
extension Int8: AggregateResultType {
public static var attributeType: NSAttributeType {
return .Integer64AttributeType
}
public static func fromResultObject(result: AnyObject) -> Int8 {
return numericCast((result as! NSNumber).longLongValue)
}
}
extension Int16: AggregateResultType {
public static var attributeType: NSAttributeType {
return .Integer64AttributeType
}
public static func fromResultObject(result: AnyObject) -> Int16 {
return numericCast((result as! NSNumber).longLongValue)
}
}
extension Int32: AggregateResultType {
public static var attributeType: NSAttributeType {
return .Integer64AttributeType
}
public static func fromResultObject(result: AnyObject) -> Int32 {
return numericCast((result as! NSNumber).longLongValue)
}
}
extension Int64: AggregateResultType {
public static var attributeType: NSAttributeType {
return .Integer64AttributeType
}
public static func fromResultObject(result: AnyObject) -> Int64 {
return numericCast((result as! NSNumber).longLongValue)
}
}
extension Int: AggregateResultType {
public static var attributeType: NSAttributeType {
return .Integer64AttributeType
}
public static func fromResultObject(result: AnyObject) -> Int {
return numericCast((result as! NSNumber).longLongValue)
}
}
extension UInt8: AggregateResultType {
public static var attributeType: NSAttributeType {
return .Integer64AttributeType
}
public static func fromResultObject(result: AnyObject) -> UInt8 {
return numericCast((result as! NSNumber).longLongValue)
}
}
extension UInt16: AggregateResultType {
public static var attributeType: NSAttributeType {
return .Integer64AttributeType
}
public static func fromResultObject(result: AnyObject) -> UInt16 {
return numericCast((result as! NSNumber).longLongValue)
}
}
extension UInt32: AggregateResultType {
public static var attributeType: NSAttributeType {
return .Integer64AttributeType
}
public static func fromResultObject(result: AnyObject) -> UInt32 {
return numericCast((result as! NSNumber).longLongValue)
}
}
extension UInt64: AggregateResultType {
public static var attributeType: NSAttributeType {
return .Integer64AttributeType
}
public static func fromResultObject(result: AnyObject) -> UInt64 {
return numericCast((result as! NSNumber).longLongValue)
}
}
extension UInt: AggregateResultType {
public static var attributeType: NSAttributeType {
return .Integer64AttributeType
}
public static func fromResultObject(result: AnyObject) -> UInt {
return numericCast((result as! NSNumber).longLongValue)
}
}
extension Double: AggregateResultType {
public static var attributeType: NSAttributeType {
return .DoubleAttributeType
}
public static func fromResultObject(result: AnyObject) -> Double {
return (result as! NSNumber).doubleValue
}
}
extension Float: AggregateResultType {
public static var attributeType: NSAttributeType {
return .FloatAttributeType
}
public static func fromResultObject(result: AnyObject) -> Float {
return (result as! NSNumber).floatValue
}
}
extension NSNumber: AggregateResultType {
public static var attributeType: NSAttributeType {
return .Integer64AttributeType
}
public class func fromResultObject(result: AnyObject) -> Self {
return self(bytes: result.bytes, objCType: result.objCType)
}
}
extension NSDate: AggregateResultType {
public static var attributeType: NSAttributeType {
return .DateAttributeType
}
public class func fromResultObject(result: AnyObject) -> Self {
return self(timeInterval: 0.0, sinceDate: result as! NSDate)
}
}

View File

@@ -41,7 +41,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
*/
public func commit(completion: (result: SaveResult) -> Void) {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to commit a <\(self.dynamicType)> outside a transaction queue.")
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to commit a <\(self.dynamicType)> outside its designated queue.")
HardcoreData.assert(!self.isCommitted, "Attempted to commit a <\(self.dynamicType)> more than once.")
self.isCommitted = true
@@ -62,7 +62,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
*/
public func commitAndWait() {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to commit a <\(self.dynamicType)> outside a transaction queue.")
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to commit a <\(self.dynamicType)> outside its designated queue.")
HardcoreData.assert(!self.isCommitted, "Attempted to commit a <\(self.dynamicType)> more than once.")
self.isCommitted = true
@@ -77,6 +77,7 @@ public final class AsynchronousDataTransaction: BaseDataTransaction {
*/
public func beginSynchronous(closure: (transaction: SynchronousDataTransaction) -> Void) -> SaveResult? {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to begin a child transaction from a <\(self.dynamicType)> outside its designated queue.")
HardcoreData.assert(!self.isCommitted, "Attempted to begin a child transaction from an already committed <\(self.dynamicType)>.")
return SynchronousDataTransaction(

View File

@@ -35,63 +35,113 @@ public extension BaseDataTransaction {
public func fetchOne<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> T? {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside its designated queue.")
return self.context.fetchOne(entity, queryClauses)
}
public func fetchOne<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> T? {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside its designated queue.")
return self.context.fetchOne(entity, queryClauses)
}
public func fetchAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> [T]? {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside its designated queue.")
return self.context.fetchAll(entity, queryClauses)
}
public func fetchAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> [T]? {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside its designated queue.")
return self.context.fetchAll(entity, queryClauses)
}
public func fetchCount<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> Int? {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside its designated queue.")
return self.context.fetchCount(entity, queryClauses)
}
public func fetchCount<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> Int? {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside its designated queue.")
return self.context.fetchCount(entity, queryClauses)
}
public func fetchObjectID<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> NSManagedObjectID? {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside its designated queue.")
return self.context.fetchObjectID(entity, queryClauses)
}
public func fetchObjectID<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> NSManagedObjectID? {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside its designated queue.")
return self.context.fetchObjectID(entity, queryClauses)
}
public func fetchObjectIDs<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> [NSManagedObjectID]? {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside its designated queue.")
return self.context.fetchObjectIDs(entity, queryClauses)
}
public func fetchObjectIDs<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> [NSManagedObjectID]? {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside its designated queue.")
return self.context.fetchObjectIDs(entity, queryClauses)
}
public func deleteAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> Int? {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to delete from a <\(self.dynamicType)> outside its designated queue.")
return self.context.deleteAll(entity, queryClauses)
}
public func deleteAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> Int? {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to delete from a <\(self.dynamicType)> outside its designated queue.")
return self.context.deleteAll(entity, queryClauses)
}
public func queryAggregate<T: NSManagedObject>(entity: T.Type, function: AggregateFunction, _ queryClauses: FetchClause...) -> Int? {
public func queryValue<T: NSManagedObject, U: SelectValueResultType>(entity: T.Type, _ selectClause: Select<U>, _ queryClauses: FetchClause...) -> U? {
let result = self.context.queryAggregate(entity, function: function, queryClauses)
return result
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to query from a <\(self.dynamicType)> outside its designated queue.")
return self.context.queryValue(entity, selectClause, queryClauses)
}
public func queryAggregate<T: NSManagedObject>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> Int? {
public func queryValue<T: NSManagedObject, U: SelectValueResultType>(entity: T.Type, _ selectClause: Select<U>, _ queryClauses: [FetchClause]) -> U? {
let result = self.context.queryAggregate(entity, function: function, queryClauses)
return result
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to query from a <\(self.dynamicType)> outside its designated queue.")
return self.context.queryValue(entity, selectClause, queryClauses)
}
public func queryAggregate<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: FetchClause...) -> U? {
public func queryAttributes<T: NSManagedObject>(entity: T.Type, _ selectClause: Select<NSDictionary>, _ queryClauses: QueryClause...) -> [[NSString: AnyObject]]? {
return self.context.queryAggregate(entity, function: function, queryClauses)
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to query from a <\(self.dynamicType)> outside its designated queue.")
return self.context.queryAttributes(entity, selectClause, queryClauses)
}
public func queryAggregate<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> U? {
public func queryAttributes<T: NSManagedObject>(entity: T.Type, _ selectClause: Select<NSDictionary>, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? {
return self.context.queryAggregate(entity, function: function, queryClauses)
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to query from a <\(self.dynamicType)> outside its designated queue.")
return self.context.queryAttributes(entity, selectClause, queryClauses)
}
}

View File

@@ -50,7 +50,7 @@ public /*abstract*/ class BaseDataTransaction {
*/
public func create<T: NSManagedObject>(entity: T.Type) -> T {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to create an entity of type <\(entity)> outside a transaction queue.")
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to create an entity of type <\(entity)> outside its designated queue.")
return T.createInContext(self.context)
}
@@ -63,7 +63,7 @@ public /*abstract*/ class BaseDataTransaction {
*/
public func fetch<T: NSManagedObject>(object: T) -> T? {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to update an entity of type <\(object.dynamicType)> outside a transaction queue.")
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to update an entity of type <\(object.dynamicType)> outside its designated queue.")
return object.inContext(self.context)
}
@@ -75,7 +75,7 @@ public /*abstract*/ class BaseDataTransaction {
*/
public func delete(object: NSManagedObject) {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to delete an entity of type <\(object.dynamicType)> outside a transaction queue.")
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to delete an entity of type <\(object.dynamicType)> outside its designated queue.")
object.deleteFromContext()
}
@@ -87,7 +87,7 @@ public /*abstract*/ class BaseDataTransaction {
*/
public func rollback() {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to rollback a <\(self.dynamicType)> outside a transaction queue.")
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to rollback a <\(self.dynamicType)> outside its designated queue.")
self.context.reset()
}

View File

@@ -1,5 +1,5 @@
//
// CustomizeQuery.swift
// CustomizeFetch.swift
// HardcoreData
//
// Copyright (c) 2015 John Rommel Estropia
@@ -27,9 +27,9 @@ import Foundation
import CoreData
// MARK: - CustomizeQuery
// MARK: - CustomizeFetch
public struct CustomizeQuery: FetchClause {
public struct CustomizeFetch: FetchClause {
// MARK: Public

View File

@@ -0,0 +1,50 @@
//
// DataStack+Observing.swift
// HardcoreData
//
// Copyright (c) 2015 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import Foundation
import CoreData
import GCDKit
// MARK: - DataStack
public extension DataStack {
// MARK: Public
public func observeObjectList<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> ManagedObjectListController<T> {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside the main queue.")
// TODO: sectionNameKeyPath and cacheResults
return ManagedObjectListController(
dataStack: self,
entity: entity,
sectionNameKeyPath: nil,
cacheResults: false,
queryClauses: queryClauses
)
}
}

View File

@@ -25,6 +25,7 @@
import Foundation
import CoreData
import GCDKit
// MARK: - DataStack
@@ -35,63 +36,113 @@ public extension DataStack {
public func fetchOne<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> T? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside the main queue.")
return self.mainContext.fetchOne(entity, queryClauses)
}
public func fetchOne<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> T? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside the main queue.")
return self.mainContext.fetchOne(entity, queryClauses)
}
public func fetchAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> [T]? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside the main queue.")
return self.mainContext.fetchAll(entity, queryClauses)
}
public func fetchAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> [T]? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside the main queue.")
return self.mainContext.fetchAll(entity, queryClauses)
}
public func fetchCount<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> Int? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside the main queue.")
return self.mainContext.fetchCount(entity, queryClauses)
}
public func fetchCount<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> Int? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside the main queue.")
return self.mainContext.fetchCount(entity, queryClauses)
}
public func fetchObjectID<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> NSManagedObjectID? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside the main queue.")
return self.mainContext.fetchObjectID(entity, queryClauses)
}
public func fetchObjectID<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> NSManagedObjectID? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside the main queue.")
return self.mainContext.fetchObjectID(entity, queryClauses)
}
public func fetchObjectIDs<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> [NSManagedObjectID]? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside the main queue.")
return self.mainContext.fetchObjectIDs(entity, queryClauses)
}
public func fetchObjectIDs<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> [NSManagedObjectID]? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to fetch from a <\(self.dynamicType)> outside the main queue.")
return self.mainContext.fetchObjectIDs(entity, queryClauses)
}
public func deleteAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> Int? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to delete from a <\(self.dynamicType)> outside the main queue.")
return self.mainContext.deleteAll(entity, queryClauses)
}
public func deleteAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> Int? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to delete from a <\(self.dynamicType)> outside the main queue.")
return self.mainContext.deleteAll(entity, queryClauses)
}
public func queryAggregate<T: NSManagedObject>(entity: T.Type, function: AggregateFunction, _ queryClauses: FetchClause...) -> Int? {
public func queryValue<T: NSManagedObject, U: SelectValueResultType>(entity: T.Type, _ selectClause: Select<U>, _ queryClauses: FetchClause...) -> U? {
let result = self.mainContext.queryAggregate(entity, function: function, queryClauses)
return result
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to query from a <\(self.dynamicType)> outside the main queue.")
return self.mainContext.queryValue(entity, selectClause, queryClauses)
}
public func queryAggregate<T: NSManagedObject>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> Int? {
public func queryValue<T: NSManagedObject, U: SelectValueResultType>(entity: T.Type, _ selectClause: Select<U>, _ queryClauses: [FetchClause]) -> U? {
let result = self.mainContext.queryAggregate(entity, function: function, queryClauses)
return result
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to query from a <\(self.dynamicType)> outside the main queue.")
return self.mainContext.queryValue(entity, selectClause, queryClauses)
}
public func queryAggregate<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: FetchClause...) -> U? {
public func queryAttributes<T: NSManagedObject>(entity: T.Type, _ selectClause: Select<NSDictionary>, _ queryClauses: QueryClause...) -> [[NSString: AnyObject]]? {
return self.mainContext.queryAggregate(entity, function: function, queryClauses)
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to query from a <\(self.dynamicType)> outside the main queue.")
return self.mainContext.queryAttributes(entity, selectClause, queryClauses)
}
public func queryAggregate<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> U? {
public func queryAttributes<T: NSManagedObject>(entity: T.Type, _ selectClause: Select<NSDictionary>, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? {
return self.mainContext.queryAggregate(entity, function: function, queryClauses)
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to query from a <\(self.dynamicType)> outside the main queue.")
return self.mainContext.queryAttributes(entity, selectClause, queryClauses)
}
}

View File

@@ -25,6 +25,7 @@
import Foundation
import CoreData
import GCDKit
// MARK: - DataStack
@@ -40,6 +41,8 @@ public extension DataStack {
*/
public func beginAsynchronous(closure: (transaction: AsynchronousDataTransaction) -> Void) {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to begin a transaction from a <\(self.dynamicType)> outside the main queue.")
AsynchronousDataTransaction(
mainContext: self.rootSavingContext,
queue: self.childTransactionQueue,
@@ -54,6 +57,8 @@ public extension DataStack {
*/
public func beginSynchronous(closure: (transaction: SynchronousDataTransaction) -> Void) -> SaveResult? {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to begin a transaction from a <\(self.dynamicType)> outside the main queue.")
return SynchronousDataTransaction(
mainContext: self.rootSavingContext,
queue: self.childTransactionQueue,
@@ -67,6 +72,8 @@ public extension DataStack {
*/
public func beginDetached() -> DetachedDataTransaction {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to begin a transaction from a <\(self.dynamicType)> outside the main queue.")
return DetachedDataTransaction(
mainContext: self.rootSavingContext,
queue: .Main)

View File

@@ -105,7 +105,7 @@ public final class DataStack {
var error: NSError?
var store: NSPersistentStore?
coordinator.performSynchronously {
coordinator.performBlockAndWait {
store = coordinator.addPersistentStoreWithType(
NSInMemoryStoreType,
@@ -203,7 +203,7 @@ public final class DataStack {
var store: NSPersistentStore?
var persistentStoreError: NSError?
coordinator.performSynchronously {
coordinator.performBlockAndWait {
store = coordinator.addPersistentStoreWithType(
NSSQLiteStoreType,
@@ -238,7 +238,7 @@ public final class DataStack {
error: nil)
var store: NSPersistentStore?
coordinator.performSynchronously {
coordinator.performBlockAndWait {
store = coordinator.addPersistentStoreWithType(
NSSQLiteStoreType,

View File

@@ -41,7 +41,7 @@ public final class DetachedDataTransaction: BaseDataTransaction {
*/
public func commit(completion: (result: SaveResult) -> Void) {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to commit a <\(self.dynamicType)> outside a transaction queue.")
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to commit a <\(self.dynamicType)> outside its designated queue.")
self.context.saveAsynchronouslyWithCompletion { (result) -> Void in

View File

@@ -29,7 +29,4 @@ import CoreData
// MARK: - FetchClause
public protocol FetchClause {
func applyToFetchRequest(fetchRequest: NSFetchRequest)
}
public protocol FetchClause: QueryClause { }

View File

@@ -0,0 +1,80 @@
//
// GroupBy.swift
// HardcoreData
//
// Copyright (c) 2015 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import Foundation
// MARK: - GroupBy
public struct GroupBy: QueryClause {
// MARK: Public
public init(_ keyPaths: [KeyPath]) {
self.keyPaths = keyPaths
}
public init() {
self.init([])
}
public init(_ keyPath: KeyPath, _ subKeyPaths: KeyPath...) {
self.init([keyPath] + subKeyPaths)
}
public let keyPaths: [KeyPath]
// MARK: QueryClause
public func applyToFetchRequest(fetchRequest: NSFetchRequest) {
if fetchRequest.propertiesToGroupBy != nil {
HardcoreData.log(.Warning, message: "An existing \"propertiesToGroupBy\" for the <\(NSFetchRequest.self)> was overwritten by <\(self.dynamicType)> query clause.")
}
fetchRequest.propertiesToGroupBy = self.keyPaths
// let entityDescription = fetchRequest.entity!
// let propertyMapping = entityDescription.propertiesByName
// fetchRequest.propertiesToGroupBy = self.keyPaths.reduce([AnyObject]()) { (var properties, keyPath) -> [AnyObject] in
//
// if let propertyDescription: AnyObject = propertyMapping[keyPath] {
//
// properties.append(propertyDescription)
// }
// else {
//
// HardcoreData.log(.Warning, message: "The property \"\(keyPath)\" does not exist in entity <\(entityDescription.managedObjectClassName)> and will be ignored by <\(self.dynamicType)> query clause.")
// }
//
// return properties
// }
}
}

View File

@@ -61,6 +61,26 @@ public extension HardcoreData {
return self.defaultStack.fetchCount(entity, queryClauses)
}
public static func fetchObjectID<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> NSManagedObjectID? {
return self.defaultStack.fetchObjectID(entity, queryClauses)
}
public static func fetchObjectID<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> NSManagedObjectID? {
return self.defaultStack.fetchObjectID(entity, queryClauses)
}
public static func fetchObjectIDs<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> [NSManagedObjectID]? {
return self.defaultStack.fetchObjectIDs(entity, queryClauses)
}
public static func fetchObjectIDs<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> [NSManagedObjectID]? {
return self.defaultStack.fetchObjectIDs(entity, queryClauses)
}
public static func deleteAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> Int? {
return self.defaultStack.deleteAll(entity, queryClauses)
@@ -71,25 +91,23 @@ public extension HardcoreData {
return self.defaultStack.deleteAll(entity, queryClauses)
}
public static func queryAggregate<T: NSManagedObject>(entity: T.Type, function: AggregateFunction, _ queryClauses: FetchClause...) -> Int? {
public static func queryValue<T: NSManagedObject, U: SelectValueResultType>(entity: T.Type, _ selectClause: Select<U>, _ queryClauses: FetchClause...) -> U? {
let result = self.defaultStack.queryAggregate(entity, function: function, queryClauses)
return result
return self.defaultStack.queryValue(entity, selectClause, queryClauses)
}
public static func queryAggregate<T: NSManagedObject>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> Int? {
public static func queryValue<T: NSManagedObject, U: SelectValueResultType>(entity: T.Type, _ selectClause: Select<U>, _ queryClauses: [FetchClause]) -> U? {
let result = self.defaultStack.queryAggregate(entity, function: function, queryClauses)
return result
return self.defaultStack.queryValue(entity, selectClause, queryClauses)
}
public static func queryAggregate<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: FetchClause...) -> U? {
public static func queryAttributes<T: NSManagedObject>(entity: T.Type, _ selectClause: Select<NSDictionary>, _ queryClauses: QueryClause...) -> [[NSString: AnyObject]]? {
return self.defaultStack.queryAggregate(entity, function: function, queryClauses)
return self.defaultStack.queryAttributes(entity, selectClause, queryClauses)
}
public static func queryAggregate<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> U? {
public static func queryAttributes<T: NSManagedObject>(entity: T.Type, _ selectClause: Select<NSDictionary>, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? {
return self.defaultStack.queryAggregate(entity, function: function, queryClauses)
return self.defaultStack.queryAttributes(entity, selectClause, queryClauses)
}
}

View File

@@ -0,0 +1,27 @@
//
// ManagedObjectController.swift
// HardcoreData
//
// Copyright (c) 2015 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import Foundation
import CoreData

View File

@@ -0,0 +1,125 @@
//
// ManagedObjectListController.swift
// HardcoreData
//
// Copyright (c) 2015 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import Foundation
import CoreData
import GCDKit
// MARK: - ManagedObjectListController
public final class ManagedObjectListController<T: NSManagedObject>: NSFetchedResultsControllerDelegate {
// MARK: Public
public typealias EntityType = T
public func addObserver<U: ManagedObjectListObserver where U.EntityType == EntityType>(observer: U) {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to add a <\(U.self)> outside the main queue.")
self.observers.addObject(observer)
}
public func removeObserver<U: ManagedObjectListObserver where U.EntityType == EntityType>(observer: U) {
HardcoreData.assert(GCDQueue.Main.isCurrentExecutionContext(), "Attempted to remove a <\(U.self)> outside the main queue.")
self.observers.removeObject(observer)
}
// MARK: NSFetchedResultsControllerDelegate
private func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
}
private func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
}
private func controllerWillChangeContent(controller: NSFetchedResultsController) {
}
private func controllerDidChangeContent(controller: NSFetchedResultsController) {
}
private func controller(controller: NSFetchedResultsController, sectionIndexTitleForSectionName sectionName: String?) -> String? {
return nil
}
// MARK: Internal
internal init(dataStack: DataStack, entity: T.Type, sectionNameKeyPath: KeyPath?, cacheResults: Bool, queryClauses: [FetchClause]) {
let context = dataStack.mainContext
let fetchRequest = NSFetchRequest()
fetchRequest.entity = context.entityDescriptionForEntityClass(entity)
fetchRequest.fetchLimit = 0
fetchRequest.resultType = .ManagedObjectResultType
for clause in queryClauses {
clause.applyToFetchRequest(fetchRequest)
}
let fetchedResultsController = NSFetchedResultsController(
fetchRequest: fetchRequest,
managedObjectContext: context,
sectionNameKeyPath: sectionNameKeyPath,
cacheName: (cacheResults
? "\(ManagedObjectListController<T>.self).\(NSUUID())"
: nil)
)
self.fetchedResultsController = fetchedResultsController
self.parentStack = dataStack
self.observers = NSHashTable.weakObjectsHashTable()
fetchedResultsController.delegate = self
var error: NSError?
if !fetchedResultsController.performFetch(&error) {
HardcoreData.handleError(
error ?? NSError(hardcoreDataErrorCode: .UnknownError),
"Failed to perform fetch on \(NSFetchedResultsController.self)")
}
}
// MARK: Private
private let fetchedResultsController: NSFetchedResultsController
private weak var parentStack: DataStack?
private let observers: NSHashTable
}

View File

@@ -0,0 +1,80 @@
//
// ManagedObjectListObserver.swift
// HardcoreData
//
// Copyright (c) 2015 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import Foundation
import CoreData
// MARK: - ManagedObjectListObserver
public protocol ManagedObjectListObserver: class {
typealias EntityType: NSManagedObject
func managedObjectListWillChange(listController: ManagedObjectListController<EntityType>)
// func managedObjectList(listController: ManagedObjectListController<EntityType>, didInsertSection sectionInfo: NSFetchedResultsSectionInfo, toSectionIndex sectionIndex: Int)
//
// func managedObjectList(listController: ManagedObjectListController<EntityType>, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int)
//
// func managedObjectList(listController: ManagedObjectListController<EntityType>, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int)
//
// func managedObjectList(listController: ManagedObjectListController<EntityType>, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int)
func managedObjectList(listController: ManagedObjectListController<EntityType>, didInsertObject object: EntityType, toItemIndex itemIndex: Int)
func managedObjectList(listController: ManagedObjectListController<EntityType>, didDeleteObject object: EntityType, fromItemIndex itemIndex: Int)
func managedObjectList(listController: ManagedObjectListController<EntityType>, didUpdateObject object: EntityType, atItemIndex itemIndex: Int)
func managedObjectList(listController: ManagedObjectListController<EntityType>, didMoveObject object: EntityType, fromItemIndex: Int, toItemIndex: Int)
func managedObjectListDidChange(listController: ManagedObjectListController<EntityType>)
// private func controller(controller: NSFetchedResultsController, didChangeObject anObject: AnyObject, atIndexPath indexPath: NSIndexPath?, forChangeType type: NSFetchedResultsChangeType, newIndexPath: NSIndexPath?) {
//
// }
//
// private func controller(controller: NSFetchedResultsController, didChangeSection sectionInfo: NSFetchedResultsSectionInfo, atIndex sectionIndex: Int, forChangeType type: NSFetchedResultsChangeType) {
//
// }
//
// private func controllerWillChangeContent(controller: NSFetchedResultsController) {
//
// }
//
// private func controllerDidChangeContent(controller: NSFetchedResultsController) {
//
// }
//
// private func controller(controller: NSFetchedResultsController, sectionIndexTitleForSectionName sectionName: String?) -> String? {
//
// return nil
// }
}

View File

@@ -0,0 +1,13 @@
//
// ManagedObjectListSectionInfo.swift
// HardcoreData
//
// Created by John Rommel Estropia on 2015/03/11.
// Copyright (c) 2015 John Rommel Estropia. All rights reserved.
//
import UIKit
class ManagedObjectListSectionInfo: NSObject {
}

View File

@@ -133,6 +133,74 @@ internal extension NSManagedObjectContext {
return count
}
internal func fetchObjectID<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> NSManagedObjectID? {
return self.fetchObjectID(entity, queryClauses)
}
internal func fetchObjectID<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> NSManagedObjectID? {
let fetchRequest = NSFetchRequest()
fetchRequest.entity = self.entityDescriptionForEntityClass(entity)
fetchRequest.fetchLimit = 1
fetchRequest.resultType = .ManagedObjectIDResultType
for clause in queryClauses {
clause.applyToFetchRequest(fetchRequest)
}
var fetchResults: [NSManagedObjectID]?
var error: NSError?
self.performBlockAndWait {
fetchResults = self.executeFetchRequest(fetchRequest, error: &error) as? [NSManagedObjectID]
}
if fetchResults == nil {
HardcoreData.handleError(
error ?? NSError(hardcoreDataErrorCode: .UnknownError),
"Failed executing fetch request.")
return nil
}
return fetchResults?.first
}
internal func fetchObjectIDs<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> [NSManagedObjectID]? {
return self.fetchObjectIDs(entity, queryClauses)
}
internal func fetchObjectIDs<T: NSManagedObject>(entity: T.Type, _ queryClauses: [FetchClause]) -> [NSManagedObjectID]? {
let fetchRequest = NSFetchRequest()
fetchRequest.entity = self.entityDescriptionForEntityClass(entity)
fetchRequest.fetchLimit = 0
fetchRequest.resultType = .ManagedObjectIDResultType
for clause in queryClauses {
clause.applyToFetchRequest(fetchRequest)
}
var fetchResults: [NSManagedObjectID]?
var error: NSError?
self.performBlockAndWait {
fetchResults = self.executeFetchRequest(fetchRequest, error: &error) as? [NSManagedObjectID]
}
if fetchResults == nil {
HardcoreData.handleError(
error ?? NSError(hardcoreDataErrorCode: .UnknownError),
"Failed executing fetch request.")
return nil
}
return fetchResults
}
internal func deleteAll<T: NSManagedObject>(entity: T.Type, _ queryClauses: FetchClause...) -> Int? {
return self.deleteAll(entity, queryClauses)
@@ -178,38 +246,18 @@ internal extension NSManagedObjectContext {
return numberOfDeletedObjects
}
internal func queryAggregate<T: NSManagedObject>(entity: T.Type, function: AggregateFunction, _ queryClauses: FetchClause...) -> Int? {
internal func queryValue<T: NSManagedObject, U: SelectValueResultType>(entity: T.Type, _ selectClause: Select<U>, _ queryClauses: FetchClause...) -> U? {
return self.queryAggregateImplementation(entity, function: function, queryClauses)
return self.queryValue(entity, selectClause, queryClauses)
}
internal func queryAggregate<T: NSManagedObject>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> Int? {
return self.queryAggregateImplementation(entity, function: function, queryClauses)
}
internal func queryAggregate<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: FetchClause...) -> U? {
return self.queryAggregateImplementation(entity, function: function, queryClauses)
}
internal func queryAggregate<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> U? {
return self.queryAggregateImplementation(entity, function: function, queryClauses)
}
internal func queryAggregateImplementation<T: NSManagedObject, U: AggregateResultType>(entity: T.Type, function: AggregateFunction, _ queryClauses: [FetchClause]) -> U? {
let expressionDescription = NSExpressionDescription()
expressionDescription.name = "queryAggregate"
expressionDescription.expressionResultType = U.attributeType
expressionDescription.expression = function.createExpression()
internal func queryValue<T: NSManagedObject, U: SelectValueResultType>(entity: T.Type, _ selectClause: Select<U>, _ queryClauses: [FetchClause]) -> U? {
let fetchRequest = NSFetchRequest()
fetchRequest.entity = self.entityDescriptionForEntityClass(entity)
fetchRequest.resultType = .DictionaryResultType
fetchRequest.propertiesToFetch = [expressionDescription]
fetchRequest.includesPendingChanges = false
fetchRequest.fetchLimit = 0
selectClause.applyToFetchRequest(fetchRequest)
for clause in queryClauses {
@@ -222,19 +270,54 @@ internal extension NSManagedObjectContext {
fetchResults = self.executeFetchRequest(fetchRequest, error: &error)
}
if fetchResults == nil {
if let fetchResults = fetchResults {
HardcoreData.handleError(
error ?? NSError(hardcoreDataErrorCode: .UnknownError),
"Failed executing fetch request.")
if let rawResult = fetchResults.first as? NSDictionary,
let rawObject: AnyObject = rawResult[selectClause.keyPathForFirstSelectTerm()] {
return Select<U>.ReturnType.fromResultObject(rawObject)
}
return nil
}
if let result: AnyObject = (fetchResults?.first as! [String: AnyObject])[expressionDescription.name] {
HardcoreData.handleError(
error ?? NSError(hardcoreDataErrorCode: .UnknownError),
"Failed executing fetch request.")
return nil
}
internal func queryAttributes<T: NSManagedObject>(entity: T.Type, _ selectClause: Select<NSDictionary>, _ queryClauses: QueryClause...) -> [[NSString: AnyObject]]? {
return self.queryAttributes(entity, selectClause, queryClauses)
}
internal func queryAttributes<T: NSManagedObject>(entity: T.Type, _ selectClause: Select<NSDictionary>, _ queryClauses: [QueryClause]) -> [[NSString: AnyObject]]? {
let fetchRequest = NSFetchRequest()
fetchRequest.entity = self.entityDescriptionForEntityClass(entity)
fetchRequest.fetchLimit = 0
selectClause.applyToFetchRequest(fetchRequest)
for clause in queryClauses {
return U.fromResultObject(result)
clause.applyToFetchRequest(fetchRequest)
}
var fetchResults: [AnyObject]?
var error: NSError?
self.performBlockAndWait {
fetchResults = self.executeFetchRequest(fetchRequest, error: &error)
}
if let fetchResults = fetchResults {
return Select<NSDictionary>.ReturnType.fromResultObjects(fetchResults)
}
HardcoreData.handleError(
error ?? NSError(hardcoreDataErrorCode: .UnknownError),
"Failed executing fetch request.")
return nil
}
}

View File

@@ -1,8 +1,8 @@
//
// NSPersistentStoreCoordinator+HardcoreData.swift
// QueryClause.swift
// HardcoreData
//
// Copyright (c) 2014 John Rommel Estropia
// Copyright (c) 2015 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
@@ -27,23 +27,9 @@ import Foundation
import CoreData
// MARK: - NSPersistentStoreCoordinator
// MARK: - QueryClause
internal extension NSPersistentStoreCoordinator {
public protocol QueryClause {
// MARK: Internal
internal func performSynchronously(closure: () -> Void) {
if self.respondsToSelector("performBlockAndWait:") {
self.performBlockAndWait(closure)
}
else {
self.lock()
autoreleasepool(closure)
self.unlock()
}
}
}
func applyToFetchRequest(fetchRequest: NSFetchRequest)
}

515
HardcoreData/Select.swift Normal file
View File

@@ -0,0 +1,515 @@
//
// Select.swift
// HardcoreData
//
// Copyright (c) 2015 John Rommel Estropia
//
// Permission is hereby granted, free of charge, to any person obtaining a copy
// of this software and associated documentation files (the "Software"), to deal
// in the Software without restriction, including without limitation the rights
// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
// copies of the Software, and to permit persons to whom the Software is
// furnished to do so, subject to the following conditions:
//
// The above copyright notice and this permission notice shall be included in all
// copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
// SOFTWARE.
//
import Foundation
import CoreData
// MARK: - SelectResultType
public protocol SelectResultType { }
// MARK: - SelectValueResultType
public protocol SelectValueResultType: SelectResultType {
static func fromResultObject(result: AnyObject) -> Self?
}
// MARK: - SelectAttributesResultType
public protocol SelectAttributesResultType: SelectResultType {
static func fromResultObjects(result: [AnyObject]) -> [[NSString: AnyObject]]
}
// MARK: - SelectTerm
public enum SelectTerm: StringLiteralConvertible {
case Attribute(KeyPath)
case Aggregate(function: String, KeyPath, As: String)
public static func Average(keyPath: KeyPath, As alias: KeyPath? = nil) -> SelectTerm {
return .Aggregate(
function: "average:",
keyPath,
As: alias ?? "average(\(keyPath))"
)
}
public static func Count(keyPath: KeyPath, As alias: KeyPath? = nil) -> SelectTerm {
return .Aggregate(
function: "count:",
keyPath,
As: alias ?? "count(\(keyPath))"
)
}
public static func Maximum(keyPath: KeyPath, As alias: KeyPath? = nil) -> SelectTerm {
return .Aggregate(
function: "max:",
keyPath,
As: alias ?? "max(\(keyPath))"
)
}
public static func Median(keyPath: KeyPath, As alias: KeyPath? = nil) -> SelectTerm {
return .Aggregate(
function: "median:",
keyPath, As:
alias ?? "median(\(keyPath))"
)
}
public static func Minimum(keyPath: KeyPath, As alias: KeyPath? = nil) -> SelectTerm {
return .Aggregate(
function: "min:",
keyPath,
As: alias ?? "min(\(keyPath))"
)
}
public static func StandardDeviation(keyPath: KeyPath, As alias: KeyPath? = nil) -> SelectTerm {
return .Aggregate(
function: "stddev:",
keyPath,
As: alias ?? "stddev(\(keyPath))"
)
}
public static func Sum(keyPath: KeyPath, As alias: KeyPath? = nil) -> SelectTerm {
return .Aggregate(
function: "sum:",
keyPath,
As: alias ?? "sum(\(keyPath))"
)
}
public init(stringLiteral value: KeyPath) {
self = .Attribute(value)
}
public init(unicodeScalarLiteral value: KeyPath) {
self = .Attribute(value)
}
public init(extendedGraphemeClusterLiteral value: KeyPath) {
self = .Attribute(value)
}
}
// MARK: - Select
public struct Select<T: SelectResultType> {
// MARK: Public
public typealias ReturnType = T
public init(_ selectTerm: SelectTerm, _ selectTerms: SelectTerm...) {
self.selectTerms = [selectTerm] + selectTerms
}
// MARK: Internal
internal func applyToFetchRequest(fetchRequest: NSFetchRequest) {
if fetchRequest.propertiesToFetch != nil {
HardcoreData.log(.Warning, message: "An existing \"propertiesToFetch\" for the <\(NSFetchRequest.self)> was overwritten by <\(self.dynamicType)> query clause.")
}
fetchRequest.includesPendingChanges = false
fetchRequest.resultType = .DictionaryResultType
let entityDescription = fetchRequest.entity!
let propertiesByName = entityDescription.propertiesByName
let attributesByName = entityDescription.attributesByName
var propertiesToFetch = [AnyObject]()
for term in self.selectTerms {
switch term {
case .Attribute(let keyPath):
if let propertyDescription = propertiesByName[keyPath] as? NSPropertyDescription {
propertiesToFetch.append(propertyDescription)
}
else {
HardcoreData.log(.Warning, message: "The property \"\(keyPath)\" does not exist in entity <\(entityDescription.managedObjectClassName)> and will be ignored by <\(self.dynamicType)> query clause.")
}
case .Aggregate(let function, let keyPath, let alias):
if let attributeDescription = attributesByName[keyPath] as? NSAttributeDescription {
let expressionDescription = NSExpressionDescription()
expressionDescription.name = alias
expressionDescription.expressionResultType = attributeDescription.attributeType
expressionDescription.expression = NSExpression(
forFunction: function,
arguments: [NSExpression(forKeyPath: keyPath)]
)
propertiesToFetch.append(expressionDescription)
}
else {
HardcoreData.log(.Warning, message: "The attribute \"\(keyPath)\" does not exist in entity <\(entityDescription.managedObjectClassName)> and will be ignored by <\(self.dynamicType)> query clause.")
}
}
}
fetchRequest.propertiesToFetch = propertiesToFetch
}
internal func keyPathForFirstSelectTerm() -> KeyPath {
switch self.selectTerms.first! {
case .Attribute(let keyPath):
return keyPath
case .Aggregate(_, _, let alias):
return alias
}
}
// MARK: Private
private let selectTerms: [SelectTerm]
}
// MARK: - Bool: SelectValueResultType
extension Bool: SelectValueResultType {
public static var attributeType: NSAttributeType {
return .BooleanAttributeType
}
public static func fromResultObject(result: AnyObject) -> Bool? {
return (result as? NSNumber)?.boolValue
}
}
// MARK: - Int8: SelectValueResultType
extension Int8: SelectValueResultType {
public static var attributeType: NSAttributeType {
return .Integer64AttributeType
}
public static func fromResultObject(result: AnyObject) -> Int8? {
if let value = (result as? NSNumber)?.longLongValue {
return numericCast(value) as Int8
}
return nil
}
}
// MARK: - Int16: SelectValueResultType
extension Int16: SelectValueResultType {
public static var attributeType: NSAttributeType {
return .Integer64AttributeType
}
public static func fromResultObject(result: AnyObject) -> Int16? {
if let value = (result as? NSNumber)?.longLongValue {
return numericCast(value) as Int16
}
return nil
}
}
// MARK: - Int32: SelectValueResultType
extension Int32: SelectValueResultType {
public static var attributeType: NSAttributeType {
return .Integer64AttributeType
}
public static func fromResultObject(result: AnyObject) -> Int32? {
if let value = (result as? NSNumber)?.longLongValue {
return numericCast(value) as Int32
}
return nil
}
}
// MARK: - Int64: SelectValueResultType
extension Int64: SelectValueResultType {
public static var attributeType: NSAttributeType {
return .Integer64AttributeType
}
public static func fromResultObject(result: AnyObject) -> Int64? {
return (result as? NSNumber)?.longLongValue
}
}
// MARK: - Int: SelectValueResultType
extension Int: SelectValueResultType {
public static var attributeType: NSAttributeType {
return .Integer64AttributeType
}
public static func fromResultObject(result: AnyObject) -> Int? {
if let value = (result as? NSNumber)?.longLongValue {
return numericCast(value) as Int
}
return nil
}
}
// MARK: - Double : SelectValueResultType
extension Double: SelectValueResultType {
public static var attributeType: NSAttributeType {
return .DoubleAttributeType
}
public static func fromResultObject(result: AnyObject) -> Double? {
return (result as? NSNumber)?.doubleValue
}
}
// MARK: - Float: SelectValueResultType
extension Float: SelectValueResultType {
public static var attributeType: NSAttributeType {
return .FloatAttributeType
}
public static func fromResultObject(result: AnyObject) -> Float? {
return (result as? NSNumber)?.floatValue
}
}
// MARK: - String: SelectValueResultType
extension String: SelectValueResultType {
public static var attributeType: NSAttributeType {
return .StringAttributeType
}
public static func fromResultObject(result: AnyObject) -> String? {
return result as? NSString as? String
}
}
// MARK: - NSNumber: SelectValueResultType
extension NSNumber: SelectValueResultType {
public class var attributeType: NSAttributeType {
return .Integer64AttributeType
}
public class func fromResultObject(result: AnyObject) -> Self? {
func forceCast<T: NSNumber>(object: AnyObject) -> T? {
return (object as? T)
}
return forceCast(result)
}
}
// MARK: - NSString: SelectValueResultType
extension NSString: SelectValueResultType {
public class var attributeType: NSAttributeType {
return .StringAttributeType
}
public class func fromResultObject(result: AnyObject) -> Self? {
func forceCast<T: NSString>(object: AnyObject) -> T? {
return (object as? T)
}
return forceCast(result)
}
}
// MARK: - NSDecimalNumber: SelectValueResultType
extension NSDecimalNumber: SelectValueResultType {
public override class var attributeType: NSAttributeType {
return .DecimalAttributeType
}
public override class func fromResultObject(result: AnyObject) -> Self? {
func forceCast<T: NSDecimalNumber>(object: AnyObject) -> T? {
return (object as? T)
}
return forceCast(result)
}
}
// MARK: - NSDate: SelectValueResultType
extension NSDate: SelectValueResultType {
public class var attributeType: NSAttributeType {
return .DateAttributeType
}
public class func fromResultObject(result: AnyObject) -> Self? {
func forceCast<T: NSDate>(object: AnyObject) -> T? {
return (object as? T)
}
return forceCast(result)
}
}
// MARK: - NSData: SelectValueResultType
extension NSData: SelectValueResultType {
public class var attributeType: NSAttributeType {
return .BinaryDataAttributeType
}
public class func fromResultObject(result: AnyObject) -> Self? {
func forceCast<T: NSData>(object: AnyObject) -> T? {
return (object as? T)
}
return forceCast(result)
}
}
// MARK: - NSManagedObjectID: SelectValueResultType
extension NSManagedObjectID: SelectValueResultType {
public class var attributeType: NSAttributeType {
return .ObjectIDAttributeType
}
public class func fromResultObject(result: AnyObject) -> Self? {
func forceCast<T: NSManagedObjectID>(object: AnyObject) -> T? {
return (object as? T)
}
return forceCast(result)
}
}
// MARK: - NSManagedObjectID: SelectAttributesResultType
extension NSDictionary: SelectAttributesResultType {
// MARK: SelectAttributesResultType
public class func fromResultObjects(result: [AnyObject]) -> [[NSString: AnyObject]] {
return result as! [[NSString: AnyObject]]
}
}

View File

@@ -32,17 +32,17 @@ public func +(left: SortedBy, right: SortedBy) -> SortedBy {
}
// MARK: - AttributeName
// MARK: - KeyPath
public typealias AttributeName = Selector
public typealias KeyPath = String
// MARK: - SortOrder
public enum SortOrder {
case Ascending(AttributeName)
case Descending(AttributeName)
case Ascending(KeyPath)
case Descending(KeyPath)
}
@@ -69,21 +69,19 @@ public struct SortedBy: FetchClause {
public init(_ order: [SortOrder]) {
self.init(order.map { sortOrder -> NSSortDescriptor in
switch sortOrder {
self.init(
order.map { sortOrder -> NSSortDescriptor in
case .Ascending(let attributeName):
return NSSortDescriptor(
key: NSStringFromSelector(attributeName),
ascending: true)
case .Descending(let attributeName):
return NSSortDescriptor(
key: NSStringFromSelector(attributeName),
ascending: false)
switch sortOrder {
case .Ascending(let keyPath):
return NSSortDescriptor(key: keyPath, ascending: true)
case .Descending(let keyPath):
return NSSortDescriptor(key: keyPath, ascending: false)
}
}
})
)
}
public init(_ order: SortOrder, _ subOrder: SortOrder...) {

View File

@@ -20,7 +20,7 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
*/
public func commitAndWait() {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to commit a <\(self.dynamicType)> outside a transaction queue.")
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to commit a <\(self.dynamicType)> outside its designated queue.")
HardcoreData.assert(!self.isCommitted, "Attempted to commit a <\(self.dynamicType)> more than once.")
self.isCommitted = true
@@ -35,6 +35,7 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
*/
public func beginSynchronous(closure: (transaction: SynchronousDataTransaction) -> Void) -> SaveResult? {
HardcoreData.assert(self.transactionQueue.isCurrentExecutionContext(), "Attempted to begin a child transaction from a <\(self.dynamicType)> outside its designated queue.")
HardcoreData.assert(!self.isCommitted, "Attempted to begin a child transaction from an already committed <\(self.dynamicType)>.")
return SynchronousDataTransaction(

View File

@@ -68,16 +68,16 @@ public struct Where: FetchClause {
self.init(NSPredicate(format: format, argumentArray: args))
}
public init(_ format: String, argumentArray: [AnyObject]?) {
public init(_ format: String, argumentArray: [NSObject]?) {
self.init(NSPredicate(format: format, argumentArray: argumentArray))
}
public init(_ attributeName: AttributeName, isEqualTo value: NSObject?) {
public init(_ keyPath: KeyPath, isEqualTo value: NSObject?) {
self.init(value == nil
? NSPredicate(format: "\(attributeName) == nil")
: NSPredicate(format: "\(attributeName) == %@", value!))
? NSPredicate(format: "\(keyPath) == nil")
: NSPredicate(format: "\(keyPath) == %@", value!))
}
public let predicate: NSPredicate

View File

@@ -76,9 +76,9 @@ class HardcoreDataTests: XCTestCase {
obj1.testNumber = 42
obj1.testDate = NSDate()
let count = transaction.queryAggregate(
let count = transaction.queryValue(
TestEntity1.self,
function: .Count("testNumber")
Select<Int>(.Count("testNumber"))
)
XCTAssertTrue(count == 0, "count == 0 (actual: \(count))") // counts only objects in store
@@ -90,10 +90,16 @@ class HardcoreDataTests: XCTestCase {
let obj3 = transaction.create(TestEntity2)
obj3.testEntityID = 3
obj3.testString = "hohoho"
obj3.testString = "hahaha"
obj3.testNumber = 90
obj3.testDate = NSDate()
let obj4 = transaction.create(TestEntity2)
obj4.testEntityID = 5
obj4.testString = "hohoho"
obj4.testNumber = 80
obj4.testDate = NSDate()
transaction.beginSynchronous { (transaction) -> Void in
@@ -106,12 +112,23 @@ class HardcoreDataTests: XCTestCase {
let objs4test = transaction.fetchOne(
TestEntity2.self,
Where("testEntityID", isEqualTo: 4),
CustomizeQuery { (fetchRequest) -> Void in
CustomizeFetch { (fetchRequest) -> Void in
fetchRequest.includesPendingChanges = true
}
)
XCTAssertNotNil(objs4test, "objs4test != nil")
let objs5test = transaction.fetchOne(
TestEntity2.self,
Where("testEntityID", isEqualTo: 4),
CustomizeFetch { (fetchRequest) -> Void in
fetchRequest.includesPendingChanges = false
}
)
XCTAssertNil(objs5test, "objs5test == nil")
// Dont commit1
}
@@ -119,12 +136,16 @@ class HardcoreDataTests: XCTestCase {
let objs4test = HardcoreData.fetchOne(
TestEntity2.self,
Where("testEntityID", isEqualTo: 4)
Where("testEntityID", isEqualTo: 4),
CustomizeFetch { (fetchRequest) -> Void in
fetchRequest.includesPendingChanges = false
}
)
XCTAssertNil(objs4test, "objs4test == nil")
let objs5test = detachedTransaction.fetchCount(TestEntity2)
XCTAssertTrue(objs5test == 2, "objs5test == 2")
XCTAssertTrue(objs5test == 3, "objs5test == 3")
XCTAssertTrue(NSThread.isMainThread(), "NSThread.isMainThread()")
switch result {
@@ -149,7 +170,7 @@ class HardcoreDataTests: XCTestCase {
TestEntity2.self,
Where("testNumber", isEqualTo: 100) || Where("%K == %@", "testNumber", 90),
SortedBy(.Ascending("testEntityID"), .Descending("testString")),
CustomizeQuery { (fetchRequest) -> Void in
CustomizeFetch { (fetchRequest) -> Void in
fetchRequest.includesPendingChanges = true
}
@@ -159,6 +180,13 @@ class HardcoreDataTests: XCTestCase {
transaction.commit { (result) -> Void in
let counts = HardcoreData.queryAttributes(
TestEntity2.self,
Select(.Count("testString", As: "count"), "testString"),
GroupBy("testString")
)
println(counts)
XCTAssertTrue(NSThread.isMainThread(), "NSThread.isMainThread()")
switch result {
@@ -174,15 +202,15 @@ class HardcoreDataTests: XCTestCase {
self.waitForExpectationsWithTimeout(100, handler: nil)
let max1 = HardcoreData.queryAggregate(
let max1 = HardcoreData.queryValue(
TestEntity2.self,
function: .Maximum("testNumber")
Select<Int>(.Maximum("testNumber"))
)
XCTAssertTrue(max1 == 100, "max == 100 (actual: \(max1))")
let max2 = HardcoreData.queryAggregate(
let max2 = HardcoreData.queryValue(
TestEntity2.self,
function: .Maximum("testNumber"),
Select<NSNumber>(.Maximum("testNumber")),
Where("%K > %@", "testEntityID", 2)
)
XCTAssertTrue(max2 == 90, "max == 90 (actual: \(max2))")
@@ -196,7 +224,7 @@ class HardcoreDataTests: XCTestCase {
TestEntity2.self,
Where("%K > %@", "testEntityID", 2)
)
XCTAssertTrue(numberOfDeletedObjects2 == 1, "numberOfDeletedObjects2 == 1 (actual: \(numberOfDeletedObjects2))")
XCTAssertTrue(numberOfDeletedObjects2 == 2, "numberOfDeletedObjects2 == 2 (actual: \(numberOfDeletedObjects2))")
transaction.commitAndWait()
}
@@ -225,9 +253,9 @@ class HardcoreDataTests: XCTestCase {
case .Success(let hasChanges):
XCTAssertTrue(hasChanges, "hasChanges == true")
let count = HardcoreData.queryAggregate(
let count: Int? = HardcoreData.queryValue(
TestEntity1.self,
function: .Count("testNumber")
Select(.Count("testNumber"))
)
XCTAssertTrue(count == 1, "count == 1 (actual: \(count))")
@@ -245,9 +273,9 @@ class HardcoreDataTests: XCTestCase {
case .Success(let hasChanges):
XCTAssertTrue(hasChanges, "hasChanges == true")
let count = HardcoreData.queryAggregate(
let count = HardcoreData.queryValue(
TestEntity1.self,
function: .Count("testNumber")
Select<Int>(.Count("testNumber"))
)
XCTAssertTrue(count == 2, "count == 2 (actual: \(count))")

View File

@@ -32,5 +32,4 @@ class TestEntity1: NSManagedObject {
@NSManaged var testString: String?
@NSManaged var testNumber: NSNumber?
@NSManaged var testDate: NSDate?
}

View File

@@ -33,4 +33,5 @@ class TestEntity2: NSManagedObject {
@NSManaged var testNumber: NSNumber?
@NSManaged var testDate: NSDate?
var testProperty: NSNumber?
}