diff --git a/.swift-version b/.swift-version index cb2b00e..8c50098 100644 --- a/.swift-version +++ b/.swift-version @@ -1 +1 @@ -3.0.1 +3.1 diff --git a/CoreStore.xcodeproj/project.pbxproj b/CoreStore.xcodeproj/project.pbxproj index 3d8e387..56633b4 100644 --- a/CoreStore.xcodeproj/project.pbxproj +++ b/CoreStore.xcodeproj/project.pbxproj @@ -413,6 +413,13 @@ B5C976E81C6E3A5D00B1AF90 /* CoreStoreFetchedResultsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C976E61C6E3A5900B1AF90 /* CoreStoreFetchedResultsController.swift */; }; B5C976E91C6E3A5E00B1AF90 /* CoreStoreFetchedResultsController.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5C976E61C6E3A5900B1AF90 /* CoreStoreFetchedResultsController.swift */; }; B5D1E22C19FA9FBC003B2874 /* CoreStoreError.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D1E22B19FA9FBC003B2874 /* CoreStoreError.swift */; }; + B5D339AF1E925BF200C880DE /* Prototype.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D339AE1E925BF200C880DE /* Prototype.swift */; }; + B5D339B01E925BF200C880DE /* Prototype.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D339AE1E925BF200C880DE /* Prototype.swift */; }; + B5D339B11E925BF200C880DE /* Prototype.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D339AE1E925BF200C880DE /* Prototype.swift */; }; + B5D339B21E925BF200C880DE /* Prototype.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D339AE1E925BF200C880DE /* Prototype.swift */; }; + B5D339B41E925C2B00C880DE /* DynamicModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D339B31E925C2B00C880DE /* DynamicModelTests.swift */; }; + B5D339B51E925C2B00C880DE /* DynamicModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D339B31E925C2B00C880DE /* DynamicModelTests.swift */; }; + B5D339B61E925C2B00C880DE /* DynamicModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D339B31E925C2B00C880DE /* DynamicModelTests.swift */; }; B5D372841A39CD6900F583D9 /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B5D372821A39CD6900F583D9 /* Model.xcdatamodeld */; }; B5D39A0219FD00C9000E91BB /* Foundation.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = B5D39A0119FD00C9000E91BB /* Foundation.framework */; }; B5D3F6451C887C0A00C7492A /* LegacySQLiteStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D3F6441C887C0A00C7492A /* LegacySQLiteStore.swift */; }; @@ -693,6 +700,8 @@ B5C976E21C6C9F6A00B1AF90 /* UnsafeDataTransaction+Observing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "UnsafeDataTransaction+Observing.swift"; sourceTree = ""; }; B5C976E61C6E3A5900B1AF90 /* CoreStoreFetchedResultsController.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreStoreFetchedResultsController.swift; sourceTree = ""; }; B5D1E22B19FA9FBC003B2874 /* CoreStoreError.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreStoreError.swift; sourceTree = ""; }; + B5D339AE1E925BF200C880DE /* Prototype.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Prototype.swift; sourceTree = ""; }; + B5D339B31E925C2B00C880DE /* DynamicModelTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DynamicModelTests.swift; sourceTree = ""; }; B5D372831A39CD6900F583D9 /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = ""; }; B5D39A0119FD00C9000E91BB /* Foundation.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = Foundation.framework; path = System/Library/Frameworks/Foundation.framework; sourceTree = SDKROOT; }; B5D3F6441C887C0A00C7492A /* LegacySQLiteStore.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LegacySQLiteStore.swift; sourceTree = ""; }; @@ -892,6 +901,7 @@ B5489F4A1CF5F743008B4978 /* BaseTests */, B5DBE2DD1C9939E100B5CEFA /* BridgingTests.h */, B5DBE2DE1C9939E100B5CEFA /* BridgingTests.m */, + B5D339B31E925C2B00C880DE /* DynamicModelTests.swift */, B5519A3F1CA1B17B002BEF78 /* ErrorTests.swift */, B52557871D02DE8100E51965 /* FetchTests.swift */, B5489F4F1CF603D5008B4978 /* FromTests.swift */, @@ -1041,6 +1051,7 @@ B57358D71E5A7F9B0094B50A /* Dynamic Models */ = { isa = PBXGroup; children = ( + B5D339AE1E925BF200C880DE /* Prototype.swift */, ); path = "Dynamic Models"; sourceTree = ""; @@ -1656,6 +1667,7 @@ B5E222231CA4E12600BA2E95 /* CSSynchronousDataTransaction.swift in Sources */, B5E84F281AFF84920064E85B /* NSManagedObject+Convenience.swift in Sources */, B51BE06A1B47FC4B0069F532 /* NSManagedObjectModel+Setup.swift in Sources */, + B5D339AF1E925BF200C880DE /* Prototype.swift in Sources */, B5AEFAB51C9962AE00AD137F /* CoreStoreBridge.swift in Sources */, B5E2222A1CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift in Sources */, B5E84F391AFF85470064E85B /* NSManagedObjectContext+Querying.swift in Sources */, @@ -1703,6 +1715,7 @@ B5DC47C61C93D22900FA3BF3 /* MigrationChainTests.swift in Sources */, B525576C1CFAF18F00E51965 /* IntoTests.swift in Sources */, B5220E0C1D0D0D19009BC71E /* ImportTests.swift in Sources */, + B5D339B41E925C2B00C880DE /* DynamicModelTests.swift in Sources */, B5D372841A39CD6900F583D9 /* Model.xcdatamodeld in Sources */, B52557881D02DE8100E51965 /* FetchTests.swift in Sources */, B5489F501CF603D5008B4978 /* FromTests.swift in Sources */, @@ -1812,6 +1825,7 @@ 82BA18C41C4BBD5300A0916E /* ListMonitor.swift in Sources */, 82BA18BA1C4BBD4A00A0916E /* Select.swift in Sources */, B5AEFAB61C9962AE00AD137F /* CoreStoreBridge.swift in Sources */, + B5D339B01E925BF200C880DE /* Prototype.swift in Sources */, B5E2222C1CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift in Sources */, 82BA18A71C4BBD2900A0916E /* CoreStore+Logging.swift in Sources */, 82BA18D81C4BBD7100A0916E /* WeakObject.swift in Sources */, @@ -1859,6 +1873,7 @@ B5DC47C71C93D22900FA3BF3 /* MigrationChainTests.swift in Sources */, B5DBE2E01C9939E100B5CEFA /* BridgingTests.m in Sources */, B5220E0D1D0D0D19009BC71E /* ImportTests.swift in Sources */, + B5D339B51E925C2B00C880DE /* DynamicModelTests.swift in Sources */, B525576D1CFAF18F00E51965 /* IntoTests.swift in Sources */, B580857B1CDF808D004C2EEB /* SetupTests.swift in Sources */, B52557891D02DE8100E51965 /* FetchTests.swift in Sources */, @@ -1968,6 +1983,7 @@ B5220E1A1D130791009BC71E /* CoreStoreFetchedResultsController.swift in Sources */, B53FBA0F1CAB5E6500F0D40A /* CSCoreStore+Migrating.swift in Sources */, B59FA0B21CCBACA8007C9BCA /* ICloudStore.swift in Sources */, + B5D339B21E925BF200C880DE /* Prototype.swift in Sources */, B52DD19A1BE1F92800949AFE /* CoreStore+Logging.swift in Sources */, B52DD1A71BE1F93200949AFE /* BaseDataTransaction+Querying.swift in Sources */, B546F96C1C9AF26D00D5AC55 /* CSInMemoryStore.swift in Sources */, @@ -2015,6 +2031,7 @@ B5DBE2E11C9939E100B5CEFA /* BridgingTests.m in Sources */, B5220E0E1D0D0D19009BC71E /* ImportTests.swift in Sources */, B525576E1CFAF18F00E51965 /* IntoTests.swift in Sources */, + B5D339B61E925C2B00C880DE /* DynamicModelTests.swift in Sources */, B580857C1CDF808F004C2EEB /* SetupTests.swift in Sources */, B525578A1D02DE8100E51965 /* FetchTests.swift in Sources */, B5220E281D1308E5009BC71E /* SectionByTests.swift in Sources */, @@ -2124,6 +2141,7 @@ B56321A21BD65216006C9394 /* ListObserver.swift in Sources */, B563218A1BD65216006C9394 /* SynchronousDataTransaction.swift in Sources */, B5AEFAB71C9962AE00AD137F /* CoreStoreBridge.swift in Sources */, + B5D339B11E925BF200C880DE /* Prototype.swift in Sources */, B5E2222D1CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift in Sources */, B563219F1BD65216006C9394 /* ObjectMonitor.swift in Sources */, B56321B61BD6521C006C9394 /* WeakObject.swift in Sources */, diff --git a/CoreStoreTests/DynamicModelTests.swift b/CoreStoreTests/DynamicModelTests.swift new file mode 100644 index 0000000..47d3c4c --- /dev/null +++ b/CoreStoreTests/DynamicModelTests.swift @@ -0,0 +1,81 @@ +// +// DynamicModelTests.swift +// CoreStore +// +// Created by John Estropia on 2017/04/03. +// Copyright © 2017 John Rommel Estropia. All rights reserved. +// + +import XCTest + +import CoreData +import CoreStore + + +class DynamicModelTests: XCTestCase { + + override func setUp() { + super.setUp() + // Put setup code here. This method is called before the invocation of each test method in the class. + } + + override func tearDown() { + // Put teardown code here. This method is called after the invocation of each test method in the class. + super.tearDown() + } + + func testDynamicModels_CanBeDeclaredCorrectly() { + + class Bird: CoreStoreManagedObject { + + let species = Attribute.Required("species", default: "Swift") + } + class Mascot: Bird { + + let nickname = Attribute.Optional("nickname") + let year = Attribute.Required("year", default: 2016) + } + + let k1 = Bird.keyPath({ $0.species }) + XCTAssertEqual(k1, "species") + + let k2 = Mascot.keyPath({ $0.species }) + XCTAssertEqual(k2, "species") + + let k3 = Mascot.keyPath({ $0.nickname }) + XCTAssertEqual(k3, "nickname") + + let entities = [ + "Bird": Entity("Bird").entityDescription, + "Mascot": Entity("Mascot").entityDescription + ] + enum Static { + + static let context = NSManagedObjectContext(concurrencyType: .mainQueueConcurrencyType) + } + let rawBird = NSManagedObject(entity: entities["Bird"]!, insertInto: Static.context) + let rawMascot = NSManagedObject(entity: entities["Mascot"]!, insertInto: Static.context) + + + let bird = Bird(rawBird) + XCTAssertEqual(bird.species*, "Swift") + XCTAssertTrue(type(of: bird.species*) == String.self) + + bird.species .= "Sparrow" + XCTAssertEqual(bird.species*, "Sparrow") + + let mascot = Mascot(rawMascot) + XCTAssertEqual(mascot.species*, "Swift") + XCTAssertEqual(mascot.nickname*, nil) + + mascot.nickname .= "Riko" + XCTAssertEqual(mascot.nickname*, "Riko") + + + let p1 = (Bird.meta.species == "Swift") + XCTAssertEqual(p1, Where("%K == %@", "species", "Swift").predicate) + + let p2 = (Mascot.meta.nickname == "Riko") + XCTAssertEqual(p2, Where("%K == %@", "nickname", "Riko").predicate) + } +} diff --git a/Sources/Fetching and Querying/QueryableAttributeType.swift b/Sources/Fetching and Querying/QueryableAttributeType.swift index 12a19f0..68757ce 100644 --- a/Sources/Fetching and Querying/QueryableAttributeType.swift +++ b/Sources/Fetching and Querying/QueryableAttributeType.swift @@ -24,8 +24,8 @@ // import Foundation -import CoreGraphics import CoreData +import CoreGraphics // MARK: - QueryableAttributeType @@ -44,155 +44,6 @@ public protocol QueryableAttributeType: Hashable, SelectResultType { } -// MARK: - NSManagedObjectID - -extension NSManagedObjectID: QueryableAttributeType { - - public typealias QueryableNativeType = NSManagedObjectID - - public static let cs_rawAttributeType: NSAttributeType = .objectIDAttributeType - - @nonobjc @inline(__always) - public class func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Self? { - - @inline(__always) - func forceCast(_ value: Any) -> T? { - - return value as? T - } - return forceCast(value) - } - - @nonobjc @inline(__always) - public func cs_toQueryableNativeType() -> QueryableNativeType { - - return self - } -} - - -// MARK: - NSNumber - -extension NSNumber: QueryableAttributeType { - - public typealias QueryableNativeType = NSNumber - - public class var cs_rawAttributeType: NSAttributeType { - - return .integer64AttributeType - } - - @nonobjc @inline(__always) - public class func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Self? { - - @inline(__always) - func forceCast(_ value: Any) -> T? { - - return value as? T - } - return forceCast(value) - } - - @nonobjc @inline(__always) - public func cs_toQueryableNativeType() -> QueryableNativeType { - - return self - } -} - - -// MARK: - NSNumber - -extension NSDecimalNumber /*: QueryableAttributeType */ { - - public override class var cs_rawAttributeType: NSAttributeType { - - return .decimalAttributeType - } -} - - -// MARK: - NSString - -extension NSString: QueryableAttributeType { - - public typealias QueryableNativeType = NSString - - public static let cs_rawAttributeType: NSAttributeType = .stringAttributeType - - @nonobjc @inline(__always) - public class func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Self? { - - @inline(__always) - func forceCast(_ value: Any) -> T? { - - return value as? T - } - return forceCast(value) - } - - @nonobjc @inline(__always) - public func cs_toQueryableNativeType() -> QueryableNativeType { - - return self - } -} - - -// MARK: - NSDate - -extension NSDate: QueryableAttributeType { - - public typealias QueryableNativeType = NSDate - - public static let cs_rawAttributeType: NSAttributeType = .dateAttributeType - - @nonobjc @inline(__always) - public class func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Self? { - - @inline(__always) - func forceCast(_ value: Any) -> T? { - - return value as? T - } - return forceCast(value) - } - - @nonobjc @inline(__always) - public func cs_toQueryableNativeType() -> QueryableNativeType { - - return self - } -} - - -// MARK: - NSData - -extension NSData: QueryableAttributeType { - - public typealias QueryableNativeType = NSData - - public static let cs_rawAttributeType: NSAttributeType = .binaryDataAttributeType - - @nonobjc @inline(__always) - public class func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Self? { - - @inline(__always) - func forceCast(_ value: Any) -> T? { - - return value as? T - } - return forceCast(value) - } - - @nonobjc @inline(__always) - public func cs_toQueryableNativeType() -> QueryableNativeType { - - return self - } -} - - // MARK: - Bool extension Bool: QueryableAttributeType { @@ -224,6 +75,138 @@ extension Bool: QueryableAttributeType { } +// MARK: - CGFloat + +extension CGFloat: QueryableAttributeType { + + public typealias QueryableNativeType = NSNumber + + public static let cs_rawAttributeType: NSAttributeType = .doubleAttributeType + + @inline(__always) + public static func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> CGFloat? { + + return CGFloat(value.doubleValue) + } + + @inline(__always) + public func cs_toQueryableNativeType() -> QueryableNativeType { + + return self as NSNumber + } +} + + +// MARK: - Data + +extension Data: QueryableAttributeType { + + public typealias QueryableNativeType = NSData + + public static let cs_rawAttributeType: NSAttributeType = .binaryDataAttributeType + + @inline(__always) + public static func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Data? { + + return value as Data + } + + @inline(__always) + public func cs_toQueryableNativeType() -> QueryableNativeType { + + return self as NSData + } +} + + +// MARK: - Date + +extension Date: QueryableAttributeType { + + public typealias QueryableNativeType = NSDate + + public static let cs_rawAttributeType: NSAttributeType = .dateAttributeType + + @inline(__always) + public static func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Date? { + + return value as Date + } + + @inline(__always) + public func cs_toQueryableNativeType() -> QueryableNativeType { + + return self as NSDate + } +} + + +// MARK: - Double + +extension Double: QueryableAttributeType { + + public typealias QueryableNativeType = NSNumber + + public static let cs_rawAttributeType: NSAttributeType = .doubleAttributeType + + @inline(__always) + public static func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Double? { + + return value.doubleValue + } + + @inline(__always) + public func cs_toQueryableNativeType() -> QueryableNativeType { + + return self as NSNumber + } +} + + +// MARK: - Float + +extension Float: QueryableAttributeType { + + public typealias QueryableNativeType = NSNumber + + public static let cs_rawAttributeType: NSAttributeType = .floatAttributeType + + @inline(__always) + public static func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Float? { + + return value.floatValue + } + + @inline(__always) + public func cs_toQueryableNativeType() -> QueryableNativeType { + + return self as NSNumber + } +} + + +// MARK: - Int + +extension Int: QueryableAttributeType { + + public typealias QueryableNativeType = NSNumber + + public static let cs_rawAttributeType: NSAttributeType = .integer64AttributeType + + @inline(__always) + public static func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Int? { + + return value.intValue + } + + @inline(__always) + public func cs_toQueryableNativeType() -> QueryableNativeType { + + return self as NSNumber + } +} + + // MARK: - Int8 extension Int8: QueryableAttributeType { @@ -312,112 +295,217 @@ extension Int64: QueryableAttributeType { } -// MARK: - Int +// MARK: - NSData -extension Int: QueryableAttributeType { +extension NSData: QueryableAttributeType { - public typealias QueryableNativeType = NSNumber + public typealias QueryableNativeType = NSData - public static let cs_rawAttributeType: NSAttributeType = .integer64AttributeType + public static let cs_rawAttributeType: NSAttributeType = .binaryDataAttributeType - @inline(__always) - public static func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Int? { + @nonobjc @inline(__always) + public class func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Self? { - return value.intValue + @inline(__always) + func forceCast(_ value: Any) -> T? { + + return value as? T + } + return forceCast(value) } - @inline(__always) + @nonobjc @inline(__always) public func cs_toQueryableNativeType() -> QueryableNativeType { - return self as NSNumber + return self } } -// MARK: - Double +// MARK: - NSDate -extension Double: QueryableAttributeType { - - public typealias QueryableNativeType = NSNumber - - public static let cs_rawAttributeType: NSAttributeType = .doubleAttributeType - - @inline(__always) - public static func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Double? { - - return value.doubleValue - } - - @inline(__always) - public func cs_toQueryableNativeType() -> QueryableNativeType { - - return self as NSNumber - } -} - - -// MARK: - Float - -extension Float: QueryableAttributeType { - - public typealias QueryableNativeType = NSNumber - - public static let cs_rawAttributeType: NSAttributeType = .floatAttributeType - - @inline(__always) - public static func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Float? { - - return value.floatValue - } - - @inline(__always) - public func cs_toQueryableNativeType() -> QueryableNativeType { - - return self as NSNumber - } -} - - -// MARK: - CGFloat - -extension CGFloat: QueryableAttributeType { - - public typealias QueryableNativeType = NSNumber - - public static let cs_rawAttributeType: NSAttributeType = .doubleAttributeType - - @inline(__always) - public static func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> CGFloat? { - - return CGFloat(value.doubleValue) - } - - @inline(__always) - public func cs_toQueryableNativeType() -> QueryableNativeType { - - return self as NSNumber - } -} - - -// MARK: - Date - -extension Date: QueryableAttributeType { +extension NSDate: QueryableAttributeType { public typealias QueryableNativeType = NSDate public static let cs_rawAttributeType: NSAttributeType = .dateAttributeType - @inline(__always) - public static func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Date? { + @nonobjc @inline(__always) + public class func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Self? { - return value as Date + @inline(__always) + func forceCast(_ value: Any) -> T? { + + return value as? T + } + return forceCast(value) } - @inline(__always) + @nonobjc @inline(__always) public func cs_toQueryableNativeType() -> QueryableNativeType { - return self as NSDate + return self + } +} + + +// MARK: - NSDecimalNumber + +extension NSDecimalNumber /*: QueryableAttributeType */ { + + public override class var cs_rawAttributeType: NSAttributeType { + + return .decimalAttributeType + } +} + + +// MARK: - NSManagedObjectID + +extension NSManagedObjectID: QueryableAttributeType { + + public typealias QueryableNativeType = NSManagedObjectID + + public static let cs_rawAttributeType: NSAttributeType = .objectIDAttributeType + + @nonobjc @inline(__always) + public class func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Self? { + + @inline(__always) + func forceCast(_ value: Any) -> T? { + + return value as? T + } + return forceCast(value) + } + + @nonobjc @inline(__always) + public func cs_toQueryableNativeType() -> QueryableNativeType { + + return self + } +} + + +// MARK: - NSNull + +extension NSNull: QueryableAttributeType { + + public typealias QueryableNativeType = NSNull + + public static let cs_rawAttributeType: NSAttributeType = .undefinedAttributeType + + @nonobjc @inline(__always) + public static func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Self? { + + return self.init() + } + + @nonobjc @inline(__always) + public func cs_toQueryableNativeType() -> QueryableNativeType { + + return self + } +} + + +// MARK: - NSNumber + +extension NSNumber: QueryableAttributeType { + + public typealias QueryableNativeType = NSNumber + + public class var cs_rawAttributeType: NSAttributeType { + + return .integer64AttributeType + } + + @nonobjc @inline(__always) + public class func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Self? { + + @inline(__always) + func forceCast(_ value: Any) -> T? { + + return value as? T + } + return forceCast(value) + } + + @nonobjc @inline(__always) + public func cs_toQueryableNativeType() -> QueryableNativeType { + + return self + } +} + + +// MARK: - NSString + +extension NSString: QueryableAttributeType { + + public typealias QueryableNativeType = NSString + + public static let cs_rawAttributeType: NSAttributeType = .stringAttributeType + + @nonobjc @inline(__always) + public class func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Self? { + + @inline(__always) + func forceCast(_ value: Any) -> T? { + + return value as? T + } + return forceCast(value) + } + + @nonobjc @inline(__always) + public func cs_toQueryableNativeType() -> QueryableNativeType { + + return self + } +} + + +// MARK: - NSURL + +extension NSURL: QueryableAttributeType { + + public typealias QueryableNativeType = NSString + + public static let cs_rawAttributeType: NSAttributeType = .stringAttributeType + + @nonobjc @inline(__always) + public static func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Self? { + + return self.init(string: value as String) + } + + @nonobjc @inline(__always) + public func cs_toQueryableNativeType() -> QueryableNativeType { + + return (self as URL).absoluteString as QueryableNativeType + } +} + + +// MARK: - NSUUID + +extension NSUUID: QueryableAttributeType { + + public typealias QueryableNativeType = NSString + + public static let cs_rawAttributeType: NSAttributeType = .stringAttributeType + + @nonobjc @inline(__always) + public static func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Self? { + + return self.init(uuidString: value.lowercased) + } + + @nonobjc @inline(__always) + public func cs_toQueryableNativeType() -> QueryableNativeType { + + return self.uuidString.lowercased() as QueryableNativeType } } @@ -444,45 +532,45 @@ extension String: QueryableAttributeType { } -// MARK: - Data +// MARK: - URL -extension Data: QueryableAttributeType { +extension URL: QueryableAttributeType { - public typealias QueryableNativeType = NSData + public typealias QueryableNativeType = NSString - public static let cs_rawAttributeType: NSAttributeType = .binaryDataAttributeType + public static let cs_rawAttributeType: NSAttributeType = .stringAttributeType @inline(__always) - public static func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Data? { + public static func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> URL? { - return value as Data + return self.init(string: value as String) } @inline(__always) public func cs_toQueryableNativeType() -> QueryableNativeType { - return self as NSData + return self.absoluteString as QueryableNativeType } } -// MARK: - NSNull +// MARK: - UUID -extension NSNull: QueryableAttributeType { +extension UUID: QueryableAttributeType { - public typealias QueryableNativeType = NSNull + public typealias QueryableNativeType = NSString - public static let cs_rawAttributeType: NSAttributeType = .undefinedAttributeType + public static let cs_rawAttributeType: NSAttributeType = .stringAttributeType - @nonobjc @inline(__always) - public static func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> Self? { + @inline(__always) + public static func cs_fromQueryableNativeType(_ value: QueryableNativeType) -> UUID? { - return self.init() + return self.init(uuidString: value.lowercased) } - @nonobjc @inline(__always) + @inline(__always) public func cs_toQueryableNativeType() -> QueryableNativeType { - return self + return self.uuidString.lowercased() as QueryableNativeType } } diff --git a/Sources/Importing/ImportableAttributeType.swift b/Sources/Importing/ImportableAttributeType.swift index 70b4f63..dc026e9 100644 --- a/Sources/Importing/ImportableAttributeType.swift +++ b/Sources/Importing/ImportableAttributeType.swift @@ -25,10 +25,36 @@ import Foundation import CoreData +import CoreGraphics // MARK: - ImportableAttributeType +/** + Types supported by CoreStore as `NSManagedObject` attribute types. + Supported default types: + - Bool + - CGFloat + - Data + - Date + - Double + - Float + - Int + - Int8 + - Int16 + - Int32 + - Int64 + - NSData + - NSDate + - NSDecimalNumber + - NSNumber + - NSString + - NSURL + - NSUUID + - String + - URL + - UUID + */ public protocol ImportableAttributeType: QueryableAttributeType { associatedtype ImportableNativeType: QueryableNativeType @@ -44,110 +70,6 @@ public protocol ImportableAttributeType: QueryableAttributeType { } -// MARK: - NSNumber - -extension NSNumber: ImportableAttributeType { - - public typealias ImportableNativeType = NSNumber - - @nonobjc @inline(__always) - public class func cs_emptyValue() -> Self { - - return self.init() - } - - @nonobjc @inline(__always) - public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? { - - return self.cs_fromQueryableNativeType(value) - } - - @nonobjc @inline(__always) - public func cs_toImportableNativeType() -> ImportableNativeType { - - return self.cs_toQueryableNativeType() - } -} - - -// MARK: - NSString - -extension NSString: ImportableAttributeType { - - public typealias ImportableNativeType = NSString - - @nonobjc @inline(__always) - public class func cs_emptyValue() -> Self { - - return self.init() - } - - @nonobjc @inline(__always) - public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? { - - return self.cs_fromQueryableNativeType(value) - } - - @nonobjc @inline(__always) - public func cs_toImportableNativeType() -> ImportableNativeType { - - return self.cs_toQueryableNativeType() - } -} - - -// MARK: - NSDate - -extension NSDate: ImportableAttributeType { - - public typealias ImportableNativeType = NSDate - - @nonobjc @inline(__always) - public class func cs_emptyValue() -> Self { - - return self.init(timeIntervalSinceReferenceDate: 0) - } - - @nonobjc @inline(__always) - public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? { - - return self.cs_fromQueryableNativeType(value) - } - - @nonobjc @inline(__always) - public func cs_toImportableNativeType() -> ImportableNativeType { - - return self.cs_toQueryableNativeType() - } -} - - -// MARK: - NSData - -extension NSData: ImportableAttributeType { - - public typealias ImportableNativeType = NSData - - @nonobjc @inline(__always) - public class func cs_emptyValue() -> Self { - - return self.init() - } - - @nonobjc @inline(__always) - public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? { - - return self.cs_fromQueryableNativeType(value) - } - - @nonobjc @inline(__always) - public func cs_toImportableNativeType() -> ImportableNativeType { - - return self.cs_toQueryableNativeType() - } -} - - // MARK: - Bool extension Bool: ImportableAttributeType { @@ -174,6 +96,188 @@ extension Bool: ImportableAttributeType { } +// MARK: - CGFloat + +extension CGFloat: ImportableAttributeType { + + public typealias ImportableNativeType = NSNumber + + @inline(__always) + public static func cs_emptyValue() -> CGFloat { + + return 0 + } + + @inline(__always) + public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> CGFloat? { + + return self.cs_fromQueryableNativeType(value) + } + + @inline(__always) + public func cs_toImportableNativeType() -> ImportableNativeType { + + return self.cs_toQueryableNativeType() + } +} + + +// MARK: - Data + +extension Data: ImportableAttributeType { + + public typealias ImportableNativeType = NSData + + @inline(__always) + public static func cs_emptyValue() -> Data { + + return Data() + } + + @inline(__always) + public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Data? { + + return self.cs_fromQueryableNativeType(value) + } + + @inline(__always) + public func cs_toImportableNativeType() -> ImportableNativeType { + + return self.cs_toQueryableNativeType() + } +} + + +// MARK: - Date + +extension Date: ImportableAttributeType { + + public typealias ImportableNativeType = NSDate + + @inline(__always) + public static func cs_emptyValue() -> Date { + + return Date(timeIntervalSinceReferenceDate: 0) + } + + @inline(__always) + public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Date? { + + return self.cs_fromQueryableNativeType(value) + } + + @inline(__always) + public func cs_toImportableNativeType() -> ImportableNativeType { + + return self.cs_toQueryableNativeType() + } +} + + +// MARK: - Double + +extension Double: ImportableAttributeType { + + public typealias ImportableNativeType = NSNumber + + @inline(__always) + public static func cs_emptyValue() -> Double { + + return 0 + } + + @inline(__always) + public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Double? { + + return self.cs_fromQueryableNativeType(value) + } + + @inline(__always) + public func cs_toImportableNativeType() -> ImportableNativeType { + + return self.cs_toQueryableNativeType() + } +} + + +// MARK: - Float + +extension Float: ImportableAttributeType { + + public typealias ImportableNativeType = NSNumber + + @inline(__always) + public static func cs_emptyValue() -> Float { + + return 0 + } + + @inline(__always) + public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Float? { + + return self.cs_fromQueryableNativeType(value) + } + + @inline(__always) + public func cs_toImportableNativeType() -> ImportableNativeType { + + return self.cs_toQueryableNativeType() + } +} + + +// MARK: - Int + +extension Int: ImportableAttributeType { + + public typealias ImportableNativeType = NSNumber + + @inline(__always) + public static func cs_emptyValue() -> Int { + + return 0 + } + + @inline(__always) + public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Int? { + + return self.cs_fromQueryableNativeType(value) + } + + @inline(__always) + public func cs_toImportableNativeType() -> ImportableNativeType { + + return self.cs_toQueryableNativeType() + } +} + + +// MARK: - Int8 + +extension Int8: ImportableAttributeType { + + public typealias ImportableNativeType = NSNumber + + @inline(__always) + public static func cs_emptyValue() -> Int8 { + + return 0 + } + + @inline(__always) + public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Int8? { + + return self.cs_fromQueryableNativeType(value) + } + + @inline(__always) + public func cs_toImportableNativeType() -> ImportableNativeType { + + return self.cs_toQueryableNativeType() + } +} + + // MARK: - Int16 extension Int16: ImportableAttributeType { @@ -252,25 +356,25 @@ extension Int64: ImportableAttributeType { } -// MARK: - Double +// MARK: - NSData -extension Double: ImportableAttributeType { +extension NSData: ImportableAttributeType { - public typealias ImportableNativeType = NSNumber + public typealias ImportableNativeType = NSData - @inline(__always) - public static func cs_emptyValue() -> Double { + @nonobjc @inline(__always) + public class func cs_emptyValue() -> Self { - return 0 + return self.init() } - @inline(__always) - public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Double? { + @nonobjc @inline(__always) + public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? { return self.cs_fromQueryableNativeType(value) } - @inline(__always) + @nonobjc @inline(__always) public func cs_toImportableNativeType() -> ImportableNativeType { return self.cs_toQueryableNativeType() @@ -278,51 +382,132 @@ extension Double: ImportableAttributeType { } -// MARK: - Float +// MARK: - NSDate -extension Float: ImportableAttributeType { - - public typealias ImportableNativeType = NSNumber - - @inline(__always) - public static func cs_emptyValue() -> Float { - - return 0 - } - - @inline(__always) - public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Float? { - - return self.cs_fromQueryableNativeType(value) - } - - @inline(__always) - public func cs_toImportableNativeType() -> ImportableNativeType { - - return self.cs_toQueryableNativeType() - } -} - - -// MARK: - Date - -extension Date: ImportableAttributeType { +extension NSDate: ImportableAttributeType { public typealias ImportableNativeType = NSDate - @inline(__always) - public static func cs_emptyValue() -> Date { + @nonobjc @inline(__always) + public class func cs_emptyValue() -> Self { - return Date(timeIntervalSinceReferenceDate: 0) + return self.init(timeIntervalSinceReferenceDate: 0) } - @inline(__always) - public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Date? { + @nonobjc @inline(__always) + public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? { return self.cs_fromQueryableNativeType(value) } - @inline(__always) + @nonobjc @inline(__always) + public func cs_toImportableNativeType() -> ImportableNativeType { + + return self.cs_toQueryableNativeType() + } +} + + +// MARK: - NSNumber + +extension NSNumber: ImportableAttributeType { + + public typealias ImportableNativeType = NSNumber + + @nonobjc @inline(__always) + public class func cs_emptyValue() -> Self { + + return self.init() + } + + @nonobjc @inline(__always) + public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? { + + return self.cs_fromQueryableNativeType(value) + } + + @nonobjc @inline(__always) + public func cs_toImportableNativeType() -> ImportableNativeType { + + return self.cs_toQueryableNativeType() + } +} + + +// MARK: - NSString + +extension NSString: ImportableAttributeType { + + public typealias ImportableNativeType = NSString + + @nonobjc @inline(__always) + public class func cs_emptyValue() -> Self { + + return self.init() + } + + @nonobjc @inline(__always) + public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? { + + return self.cs_fromQueryableNativeType(value) + } + + @nonobjc @inline(__always) + public func cs_toImportableNativeType() -> ImportableNativeType { + + return self.cs_toQueryableNativeType() + } +} + + +// MARK: - NSURL + +extension NSURL: ImportableAttributeType { + + public typealias ImportableNativeType = NSString + + @nonobjc @inline(__always) + public class func cs_emptyValue() -> Self { + + return self.init(string: "")! + } + + @nonobjc @inline(__always) + public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? { + + return self.cs_fromQueryableNativeType(value) + } + + @nonobjc @inline(__always) + public func cs_toImportableNativeType() -> ImportableNativeType { + + return self.cs_toQueryableNativeType() + } +} + + +// MARK: - NSUUID + +extension NSUUID: ImportableAttributeType { + + public typealias ImportableNativeType = NSString + + public class func cs_emptyValue() -> Self { + + enum Static { + + static var zero = Array(repeating: 0, count: 16) + } + return self.init(uuidBytes: &Static.zero) + } + + @nonobjc @inline(__always) + public class func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Self? { + + return self.cs_fromQueryableNativeType(value) + } + + @nonobjc @inline(__always) public func cs_toImportableNativeType() -> ImportableNativeType { return self.cs_toQueryableNativeType() @@ -356,20 +541,56 @@ extension String: ImportableAttributeType { } -// MARK: - Data +// MARK: - URL -extension Data: ImportableAttributeType { +extension URL: ImportableAttributeType { - public typealias ImportableNativeType = NSData + public typealias ImportableNativeType = NSString - @inline(__always) - public static func cs_emptyValue() -> Data { + public static func cs_emptyValue() -> URL { - return Data() + enum Static { + + static let empty = URL(string: "")! + } + return Static.empty } @inline(__always) - public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> Data? { + public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> URL? { + + return self.cs_fromQueryableNativeType(value) + } + + @inline(__always) + public func cs_toImportableNativeType() -> ImportableNativeType { + + return self.cs_toQueryableNativeType() + } +} + + +// MARK: - UUID + +extension UUID: ImportableAttributeType { + + public typealias ImportableNativeType = NSString + + public static func cs_emptyValue() -> UUID { + + enum Static { + + static let empty: UUID = { + + var zero = Array(repeating: 0, count: 16) + return NSUUID(uuidBytes: &zero) as UUID + }() + } + return Static.empty + } + + @inline(__always) + public static func cs_fromImportableNativeType(_ value: ImportableNativeType) -> UUID? { return self.cs_fromQueryableNativeType(value) } diff --git a/Sources/Setup/Dynamic Models/Prototype.swift b/Sources/Setup/Dynamic Models/Prototype.swift new file mode 100644 index 0000000..fc8b7de --- /dev/null +++ b/Sources/Setup/Dynamic Models/Prototype.swift @@ -0,0 +1,266 @@ +// +// Prototype.swift +// CoreStore +// +// Created by John Estropia on 2017/04/03. +// Copyright © 2017 John Rommel Estropia. All rights reserved. +// + +import CoreGraphics +import Foundation + + +public protocol ManagedObjectProtocol: class {} + +public protocol EntityProtocol { + + var entityDescription: NSEntityDescription { get } +} + +protocol AttributeProtocol: class { + + static var attributeType: NSAttributeType { get } + var keyPath: String { get } + var defaultValue: Any? { get } + var accessRawObject: () -> NSManagedObject { get set } +} + +open class CoreStoreManagedObject: ManagedObjectProtocol { + + let rawObject: NSManagedObject? + let isMeta: Bool + + public required init(_ object: NSManagedObject?) { + + self.isMeta = object == nil + self.rawObject = object + + guard let object = object else { + + return + } + self.initializeAttributes(Mirror(reflecting: self), { [unowned object] in object }) + } + + private func initializeAttributes(_ mirror: Mirror, _ accessRawObject: @escaping () -> NSManagedObject) { + + _ = mirror.superclassMirror.flatMap({ self.initializeAttributes($0, accessRawObject) }) + for child in mirror.children { + + guard case let property as AttributeProtocol = child.value else { + + continue + } + property.accessRawObject = accessRawObject + } + } +} + +public struct Entity: EntityProtocol { + + public let entityDescription: NSEntityDescription + + public init(_ entityName: String) { + + let entityDescription = NSEntityDescription() + entityDescription.name = entityName + entityDescription.managedObjectClassName = NSStringFromClass(NSManagedObject.self) + entityDescription.properties = type(of: self).initializeAttributes(Mirror(reflecting: O.meta)) + + self.entityDescription = entityDescription + } + + private static func initializeAttributes(_ mirror: Mirror) -> [NSAttributeDescription] { + + var attributeDescriptions: [NSAttributeDescription] = [] + for child in mirror.children { + + guard case let property as AttributeProtocol = child.value else { + + continue + } + let attributeDescription = NSAttributeDescription() + attributeDescription.name = property.keyPath + attributeDescription.attributeType = type(of: property).attributeType + attributeDescription.isOptional = false + attributeDescription.defaultValue = property.defaultValue + attributeDescriptions.append(attributeDescription) + } + if let baseEntityAttributeDescriptions = mirror.superclassMirror.flatMap(self.initializeAttributes) { + + attributeDescriptions.append(contentsOf: baseEntityAttributeDescriptions) + } + return attributeDescriptions + } +} + +public enum AttributeContainer { + + public final class Required: AttributeProtocol { + + static var attributeType: NSAttributeType { return V.cs_rawAttributeType } + + let keyPath: String + let defaultValue: Any? + var accessRawObject: () -> NSManagedObject = { fatalError("\(O.self) property values should not be accessed") } + + var value: V { + + get { + + let object = self.accessRawObject() + let key = self.keyPath + let value = object.value(forKey: key)! as! V.ImportableNativeType + return V.cs_fromImportableNativeType(value)! + } + set { + + let object = self.accessRawObject() + let key = self.keyPath + object.setValue(newValue.cs_toImportableNativeType(), forKey: key) + } + } + + public init(_ keyPath: String, `default`: V = V.cs_emptyValue()) { + + self.keyPath = keyPath + self.defaultValue = `default` + } + } + + public final class Optional: AttributeProtocol { + + static var attributeType: NSAttributeType { return V.cs_rawAttributeType } + + let keyPath: String + let defaultValue: Any? + var accessRawObject: () -> NSManagedObject = { fatalError("\(O.self) property values should not be accessed") } + + var value: V? { + + get { + + let object = self.accessRawObject() + let key = self.keyPath + guard let value = object.value(forKey: key) as! V.ImportableNativeType? else { + + return nil + } + return V.cs_fromImportableNativeType(value) + } + set { + + let object = self.accessRawObject() + let key = self.keyPath + object.setValue(newValue?.cs_toImportableNativeType(), forKey: key) + } + } + + public init(_ keyPath: String, `default`: V? = nil) { + + self.keyPath = keyPath + self.defaultValue = `default` + } + } +} + +public extension ManagedObjectProtocol where Self: CoreStoreManagedObject { + + public typealias Attribute = AttributeContainer + + public static var meta: Self { + + return self.init(nil) + } + + @inline(__always) + public static func keyPath(_ attribute: (Self) -> AttributeContainer.Required) -> String { + + return attribute(self.meta).keyPath + } + + @inline(__always) + public static func keyPath(_ attribute: (Self) -> AttributeContainer.Optional) -> String { + + return attribute(self.meta).keyPath + } +} + + +//: ### Convenience Operators + +infix operator .= : AssignmentPrecedence +public func .= (_ attribute: AttributeContainer.Required, _ value: V) { + + attribute.value = value +} +public func .= (_ attribute: AttributeContainer.Optional, _ value: V?) { + + attribute.value = value +} + +postfix operator * +public postfix func * (_ attribute: AttributeContainer.Required) -> V { + + return attribute.value +} +public postfix func * (_ attribute: AttributeContainer.Optional) -> V? { + + return attribute.value +} + +public extension AttributeContainer.Required where V: CVarArg { + + public static func == (_ attribute: AttributeContainer.Required, _ value: V) -> NSPredicate { + + return NSPredicate(format: "%K == %@", argumentArray: [attribute.keyPath, value]) + } + public static func < (_ attribute: AttributeContainer.Required, _ value: V) -> NSPredicate { + + return NSPredicate(format: "%K < %@", argumentArray: [attribute.keyPath, value]) + } + public static func > (_ attribute: AttributeContainer.Required, _ value: V) -> NSPredicate { + + return NSPredicate(format: "%K > %@", argumentArray: [attribute.keyPath, value]) + } + public static func <= (_ attribute: AttributeContainer.Required, _ value: V) -> NSPredicate { + + return NSPredicate(format: "%K <= %@", argumentArray: [attribute.keyPath, value]) + } + public static func >= (_ attribute: AttributeContainer.Required, _ value: V) -> NSPredicate { + + return NSPredicate(format: "%K >= %@", argumentArray: [attribute.keyPath, value]) + } + public static func != (_ attribute: AttributeContainer.Required, _ value: V) -> NSPredicate { + + return NSPredicate(format: "%K != %@", argumentArray: [attribute.keyPath, value]) + } +} +public extension AttributeContainer.Optional where V: CVarArg { + + public static func == (_ attribute: AttributeContainer.Optional, _ value: V?) -> NSPredicate { + + if let value = value { + + return NSPredicate(format: "%K == %@", argumentArray: [attribute.keyPath, value]) + } + else { + + return NSPredicate(format: "%K == nil", attribute.keyPath) + } + } +} + +protocol ModelVersionProtocol: class { + + static var version: String { get } + static var entities: [EntityProtocol] { get } +} + +extension ModelVersionProtocol { + + static func entity(for type: O.Type) -> Entity { + + return self.entities.first(where: { $0 is Entity })! as! Entity + } +}