diff --git a/CoreStore.podspec b/CoreStore.podspec index 88d695f..41aec0e 100644 --- a/CoreStore.podspec +++ b/CoreStore.podspec @@ -1,6 +1,6 @@ Pod::Spec.new do |s| s.name = "CoreStore" - s.version = "0.1.1" + s.version = "0.1.2" s.license = "MIT" s.summary = "Simple, elegant, and smart Core Data programming with Swift" s.homepage = "https://github.com/JohnEstropia/CoreStore" diff --git a/CoreStore/Fetching and Querying/Concrete Clauses/Select.swift b/CoreStore/Fetching and Querying/Concrete Clauses/Select.swift index ebe8b43..ba032ee 100644 --- a/CoreStore/Fetching and Querying/Concrete Clauses/Select.swift +++ b/CoreStore/Fetching and Querying/Concrete Clauses/Select.swift @@ -108,7 +108,8 @@ public enum SelectTerm: StringLiteralConvertible { return ._Aggregate( function: "average:", keyPath, - As: alias ?? "average(\(keyPath))" + As: alias ?? "average(\(keyPath))", + nativeType: .DecimalAttributeType ) } @@ -129,7 +130,8 @@ public enum SelectTerm: StringLiteralConvertible { return ._Aggregate( function: "count:", keyPath, - As: alias ?? "count(\(keyPath))" + As: alias ?? "count(\(keyPath))", + nativeType: .Integer64AttributeType ) } @@ -150,28 +152,8 @@ public enum SelectTerm: StringLiteralConvertible { return ._Aggregate( function: "max:", keyPath, - As: alias ?? "max(\(keyPath))" - ) - } - - /** - Provides a `SelectTerm` to a `Select` clause for querying the median value for an attribute. - - let medianAge = CoreStore.queryValue( - From(MyPersonEntity), - Select(.Median("age")) - ) - - :param: keyPath the attribute name - :param: alias the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "max()" is used - :returns: a `SelectTerm` to a `Select` clause for querying the median value for an attribute - */ - public static func Median(keyPath: KeyPath, As alias: KeyPath? = nil) -> SelectTerm { - - return ._Aggregate( - function: "median:", - keyPath, As: - alias ?? "median(\(keyPath))" + As: alias ?? "max(\(keyPath))", + nativeType: .UndefinedAttributeType ) } @@ -180,7 +162,7 @@ public enum SelectTerm: StringLiteralConvertible { let minimumAge = CoreStore.queryValue( From(MyPersonEntity), - Select(.Median("age")) + Select(.Minimum("age")) ) :param: keyPath the attribute name @@ -192,28 +174,8 @@ public enum SelectTerm: StringLiteralConvertible { return ._Aggregate( function: "min:", keyPath, - As: alias ?? "min(\(keyPath))" - ) - } - - /** - Provides a `SelectTerm` to a `Select` clause for querying the standard deviation value for an attribute. - - let stddevAge = CoreStore.queryValue( - From(MyPersonEntity), - Select(.StandardDeviation("age")) - ) - - :param: keyPath the attribute name - :param: alias the dictionary key to use to access the result. Ignored when the query return value is not an `NSDictionary`. If `nil`, the default key "stddev()" is used - :returns: a `SelectTerm` to a `Select` clause for querying the standard deviation value for an attribute - */ - public static func StandardDeviation(keyPath: KeyPath, As alias: KeyPath? = nil) -> SelectTerm { - - return ._Aggregate( - function: "stddev:", - keyPath, - As: alias ?? "stddev(\(keyPath))" + As: alias ?? "min(\(keyPath))", + nativeType: .UndefinedAttributeType ) } @@ -234,7 +196,8 @@ public enum SelectTerm: StringLiteralConvertible { return ._Aggregate( function: "sum:", keyPath, - As: alias ?? "sum(\(keyPath))" + As: alias ?? "sum(\(keyPath))", + nativeType: .DecimalAttributeType ) } @@ -260,7 +223,7 @@ public enum SelectTerm: StringLiteralConvertible { // MARK: Internal case _Attribute(KeyPath) - case _Aggregate(function: String, KeyPath, As: String) + case _Aggregate(function: String, KeyPath, As: String, nativeType: NSAttributeType) } @@ -358,12 +321,19 @@ public struct Select { CoreStore.log(.Warning, message: "The property \"\(keyPath)\" does not exist in entity <\(entityDescription.managedObjectClassName)> and will be ignored by \(typeName(self)) query clause.") } - case ._Aggregate(let function, let keyPath, let alias): + case ._Aggregate(let function, let keyPath, let alias, let nativeType): if let attributeDescription = attributesByName[keyPath] as? NSAttributeDescription { let expressionDescription = NSExpressionDescription() expressionDescription.name = alias - expressionDescription.expressionResultType = attributeDescription.attributeType + if nativeType == .UndefinedAttributeType { + + expressionDescription.expressionResultType = attributeDescription.attributeType + } + else { + + expressionDescription.expressionResultType = nativeType + } expressionDescription.expression = NSExpression( forFunction: function, arguments: [NSExpression(forKeyPath: keyPath)] @@ -388,7 +358,7 @@ public struct Select { case ._Attribute(let keyPath): return keyPath - case ._Aggregate(_, _, let alias): + case ._Aggregate(_, _, let alias, _): return alias } } diff --git a/CoreStore/Info.plist b/CoreStore/Info.plist index 4f315e0..6f05cb5 100644 --- a/CoreStore/Info.plist +++ b/CoreStore/Info.plist @@ -15,7 +15,7 @@ CFBundlePackageType FMWK CFBundleShortVersionString - 0.1.1 + 0.1.2 CFBundleSignature ???? CFBundleVersion diff --git a/CoreStoreDemo/CoreStoreDemo/CoreStoreDemo.xcdatamodeld/CoreStoreDemo.xcdatamodel/contents b/CoreStoreDemo/CoreStoreDemo/CoreStoreDemo.xcdatamodeld/CoreStoreDemo.xcdatamodel/contents index cd058b7..0b5abaa 100644 --- a/CoreStoreDemo/CoreStoreDemo/CoreStoreDemo.xcdatamodeld/CoreStoreDemo.xcdatamodel/contents +++ b/CoreStoreDemo/CoreStoreDemo/CoreStoreDemo.xcdatamodeld/CoreStoreDemo.xcdatamodel/contents @@ -32,7 +32,7 @@ - + \ No newline at end of file diff --git a/CoreStoreDemo/CoreStoreDemo/Loggers Demo/CustomLoggerViewController.swift b/CoreStoreDemo/CoreStoreDemo/Loggers Demo/CustomLoggerViewController.swift index 068ee9b..e2fd58a 100644 --- a/CoreStoreDemo/CoreStoreDemo/Loggers Demo/CustomLoggerViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/Loggers Demo/CustomLoggerViewController.swift @@ -22,13 +22,15 @@ class CustomLoggerViewController: UIViewController, CoreStoreLogger { CoreStore.logger = DefaultLogger() } + let dataStack = DataStack() // MARK: UIViewController override func viewDidLoad() { super.viewDidLoad() - + + self.dataStack.addSQLiteStore("emptyStore.sqlite") CoreStore.logger = self } @@ -52,7 +54,15 @@ class CustomLoggerViewController: UIViewController, CoreStoreLogger { GCDQueue.Main.async { [weak self] in - self?.textView?.insertText("\(fileName.stringValue.lastPathComponent):\(lineNumber) \(functionName)\n ↪︎ [Log] \(message)\n\n") + let levelString: String + switch level { + + case .Trace: levelString = "Trace" + case .Notice: levelString = "Notice" + case .Warning: levelString = "Warning" + case .Fatal: levelString = "Fatal" + } + self?.textView?.insertText("\(fileName.stringValue.lastPathComponent):\(lineNumber) \(functionName)\n ↪︎ [Log:\(levelString)] \(message)\n\n") } } @@ -88,21 +98,23 @@ class CustomLoggerViewController: UIViewController, CoreStoreLogger { switch self.segmentedControl?.selectedSegmentIndex { case .Some(0): - CoreStore.beginAsynchronous { (transaction) -> Void in + self.dataStack.beginAsynchronous { (transaction) -> Void in + transaction.create(Into(UserAccount)) } case .Some(1): - CoreStore.addSQLiteStore("dummy.sqlite", configuration: "test1") - CoreStore.addSQLiteStore("dummy.sqlite", configuration: "test2") + self.dataStack.addSQLiteStore("emptyStore.sqlite", configuration: "invalidStore") case .Some(2): - CoreStore.beginAsynchronous { (transaction) -> Void in + self.dataStack.beginAsynchronous { (transaction) -> Void in + transaction.commit() transaction.commit() } - default: return + default: + return } } } diff --git a/CoreStoreDemo/CoreStoreDemo/Stack Setup Demo/StackSetupDemoViewController.swift b/CoreStoreDemo/CoreStoreDemo/Stack Setup Demo/StackSetupDemoViewController.swift index c929832..4da88cc 100644 --- a/CoreStoreDemo/CoreStoreDemo/Stack Setup Demo/StackSetupDemoViewController.swift +++ b/CoreStoreDemo/CoreStoreDemo/Stack Setup Demo/StackSetupDemoViewController.swift @@ -168,9 +168,16 @@ class StackSetupDemoViewController: UITableViewController { switch section { - case 0: return "Facebook Accounts" - case 1: return "Twitter Accounts" - default: return nil + case 0: + let count = Static.facebookStack.fetchCount(From(UserAccount)) ?? 0 + return "Facebook Accounts (\(count) users)" + + case 1: + let count = Static.twitterStack.fetchCount(From(UserAccount)) ?? 0 + return "Twitter Accounts (\(count) users)" + + default: + return nil } } diff --git a/README.md b/README.md index 583ed3d..7328a02 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ # CoreStore [![Version](https://img.shields.io/cocoapods/v/CoreStore.svg?style=flat)](http://cocoadocs.org/docsets/CoreStore) [![Platform](https://img.shields.io/cocoapods/p/CoreStore.svg?style=flat)](http://cocoadocs.org/docsets/CoreStore) -[![License](https://img.shields.io/github/license/JohnEstropia/CoreStore.svg?style=flat)](http://cocoadocs.org/docsets/CoreStore) +[![License](https://img.shields.io/cocoapods/l/CoreStore.svg?style=flat)](https://raw.githubusercontent.com/JohnEstropia/CoreStore/master/LICENSE) Simple, elegant, and smart Core Data programming with Swift (Swift, iOS 8+) @@ -11,8 +11,8 @@ Simple, elegant, and smart Core Data programming with Swift ## What CoreStore does better: - Heavily supports multiple persistent stores per data stack, just the way .xcdatamodeld files are designed to. CoreStore will also manage one data stack by default, but you can create and manage as many as you need. -- Ability to plug-in your own logging framework (or any of your favorite 3rd-party logger) -- Gets around a limitation with other Core Data wrappers where the entity name should be the same as the `NSManagedObject` subclass name. CoreStore loads entity-to-class mappings from the .xcdatamodeld file, so you are free to name them independently. +- Ability to plug-in your own logging framework +- Gets around a limitation with other Core Data wrappers where the entity name should be the same as the `NSManagedObject` subclass name. CoreStore loads entity-to-class mappings from the managed object model file, so you are free to name them independently. - Provides type-safe, easy to configure observers to replace `NSFetchedResultsController` and KVO - Exposes API not just for fetching, but also for querying aggregates and property values - Makes it hard to fall into common concurrency mistakes. All `NSManagedObjectContext` tasks are encapsulated into safer, higher-level abstractions without sacrificing flexibility and customizability. @@ -31,9 +31,9 @@ CoreStore.addSQLiteStore("MyStore.sqlite") Simple transactions: ```swift CoreStore.beginAsynchronous { (transaction) -> Void in - let object = transaction.create(Into(MyEntity)) - object.entityID = 1 - object.name = "test entity" + let person = transaction.create(Into(MyPersonEntity)) + person.name = "John Smith" + person.age = 42 transaction.commit { (result) -> Void in switch result { @@ -46,24 +46,24 @@ CoreStore.beginAsynchronous { (transaction) -> Void in Easy fetching: ```swift -let objects = CoreStore.fetchAll(From(MyEntity)) +let people = CoreStore.fetchAll(From(MyPersonEntity)) ``` ```swift -let objects = CoreStore.fetchAll( - From(MyEntity), - Where("entityID", isEqualTo: 1), - OrderBy(.Ascending("entityID"), .Descending("name")), +let people = CoreStore.fetchAll( + From(MyPersonEntity), + Where("age > 30"), + OrderBy(.Ascending("name"), .Descending("age")), Tweak { (fetchRequest) -> Void in - fetchRequest.includesPendingChanges = true + fetchRequest.includesPendingChanges = false } ) ``` Simple queries: ```swift -let count = CoreStore.queryValue( - From(MyEntity), - Select(.Count("entityID")) +let maxAge = CoreStore.queryValue( + From(MyPersonEntity), + Select(.Maximum("age")) ) ``` @@ -497,9 +497,7 @@ If you only need a value for a particular attribute, you can just specify the ke - `.Average(...)` - `.Count(...)` - `.Maximum(...)` -- `.Median(...)` - `.Minimum(...)` -- `.StandardDeviation(...)` - `.Sum(...)` ```swift @@ -572,7 +570,7 @@ which now returns: **`GroupBy` clause** -The `GroupBy` clause lets you group results by a specified attribute/aggregate. This is only useful only for `queryAttributes(...)` since `queryValue(...)` just returns the first value anyway. +The `GroupBy` clause lets you group results by a specified attribute/aggregate. This is useful only for `queryAttributes(...)` since `queryValue(...)` just returns the first value. ```swift let personJSON = CoreStore.queryAttributes( From(MyPersonEntity),