mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-01-15 05:33:31 +01:00
added tool to convert existing NSManagedObjectModels to the new CoreStoreSchema
This commit is contained in:
@@ -535,6 +535,10 @@
|
||||
B5E41EBC1EA8C3B7006240F0 /* MigrationMappingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E41EBA1EA8C3B7006240F0 /* MigrationMappingProvider.swift */; };
|
||||
B5E41EBD1EA8C3B7006240F0 /* MigrationMappingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E41EBA1EA8C3B7006240F0 /* MigrationMappingProvider.swift */; };
|
||||
B5E41EBE1EA8C3B7006240F0 /* MigrationMappingProvider.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E41EBA1EA8C3B7006240F0 /* MigrationMappingProvider.swift */; };
|
||||
B5E41EC01EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E41EBF1EA9BB37006240F0 /* DynamicSchema+Convenience.swift */; };
|
||||
B5E41EC11EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E41EBF1EA9BB37006240F0 /* DynamicSchema+Convenience.swift */; };
|
||||
B5E41EC21EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E41EBF1EA9BB37006240F0 /* DynamicSchema+Convenience.swift */; };
|
||||
B5E41EC31EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E41EBF1EA9BB37006240F0 /* DynamicSchema+Convenience.swift */; };
|
||||
B5E834B91B76311F001D3D50 /* BaseDataTransaction+Importing.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E834B81B76311F001D3D50 /* BaseDataTransaction+Importing.swift */; };
|
||||
B5E834BB1B7691F3001D3D50 /* Functions.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E834BA1B7691F3001D3D50 /* Functions.swift */; };
|
||||
B5E84EDF1AFF84500064E85B /* DataStack.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5E84EDB1AFF84500064E85B /* DataStack.swift */; };
|
||||
@@ -801,6 +805,7 @@
|
||||
B5E222221CA4E12600BA2E95 /* CSSynchronousDataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSSynchronousDataTransaction.swift; sourceTree = "<group>"; };
|
||||
B5E222291CA51B6E00BA2E95 /* CSUnsafeDataTransaction.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSUnsafeDataTransaction.swift; sourceTree = "<group>"; };
|
||||
B5E41EBA1EA8C3B7006240F0 /* MigrationMappingProvider.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MigrationMappingProvider.swift; sourceTree = "<group>"; };
|
||||
B5E41EBF1EA9BB37006240F0 /* DynamicSchema+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "DynamicSchema+Convenience.swift"; sourceTree = "<group>"; };
|
||||
B5E834B81B76311F001D3D50 /* BaseDataTransaction+Importing.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "BaseDataTransaction+Importing.swift"; sourceTree = "<group>"; };
|
||||
B5E834BA1B7691F3001D3D50 /* Functions.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Functions.swift; sourceTree = "<group>"; };
|
||||
B5E84ED81AFF82360064E85B /* README.md */ = {isa = PBXFileReference; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = SOURCE_ROOT; };
|
||||
@@ -1343,6 +1348,7 @@
|
||||
B5FAD6A81B50A4B300714891 /* Progress+Convenience.swift */,
|
||||
B5202CF91C04688100DED140 /* NSFetchedResultsController+Convenience.swift */,
|
||||
B5A9921E1EA898710091A2E3 /* UserInfo.swift */,
|
||||
B5E41EBF1EA9BB37006240F0 /* DynamicSchema+Convenience.swift */,
|
||||
);
|
||||
path = Convenience;
|
||||
sourceTree = "<group>";
|
||||
@@ -1759,6 +1765,7 @@
|
||||
B56965241B356B820075EE4A /* MigrationResult.swift in Sources */,
|
||||
B5FE4DAC1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */,
|
||||
B501FDE71CA8D20500BE22EF /* CSListObserver.swift in Sources */,
|
||||
B5E41EC01EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */,
|
||||
B501FDE21CA8D1F500BE22EF /* CSListMonitor.swift in Sources */,
|
||||
2F291E2719C6D3CF007AF63F /* CoreStore.swift in Sources */,
|
||||
B5ECDC111CA816E500C7F112 /* CSTweak.swift in Sources */,
|
||||
@@ -1933,6 +1940,7 @@
|
||||
82BA18D41C4BBD7100A0916E /* NSManagedObjectContext+Querying.swift in Sources */,
|
||||
82BA18D51C4BBD7100A0916E /* NSManagedObjectContext+Setup.swift in Sources */,
|
||||
B501FDE91CA8D20500BE22EF /* CSListObserver.swift in Sources */,
|
||||
B5E41EC11EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */,
|
||||
B501FDE41CA8D1F500BE22EF /* CSListMonitor.swift in Sources */,
|
||||
B5FE4DA31C8481E100FA6A91 /* StorageInterface.swift in Sources */,
|
||||
B5ECDC131CA816E500C7F112 /* CSTweak.swift in Sources */,
|
||||
@@ -2107,6 +2115,7 @@
|
||||
B52DD19D1BE1F92C00949AFE /* BaseDataTransaction.swift in Sources */,
|
||||
B5220E131D1305ED009BC71E /* SectionBy.swift in Sources */,
|
||||
B559CD4D1CAA8C6D00E4D58B /* CSStorageInterface.swift in Sources */,
|
||||
B5E41EC31EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */,
|
||||
B5ECDBE91CA6BEA300C7F112 /* CSClauseTypes.swift in Sources */,
|
||||
B52DD1B81BE1F94000949AFE /* DataStack+Migration.swift in Sources */,
|
||||
B5ECDC091CA8138100C7F112 /* CSOrderBy.swift in Sources */,
|
||||
@@ -2281,6 +2290,7 @@
|
||||
B56321B31BD6521C006C9394 /* NSManagedObjectContext+Setup.swift in Sources */,
|
||||
B501FDEA1CA8D20500BE22EF /* CSListObserver.swift in Sources */,
|
||||
B501FDE51CA8D1F500BE22EF /* CSListMonitor.swift in Sources */,
|
||||
B5E41EC21EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */,
|
||||
B5ECDC141CA816E500C7F112 /* CSTweak.swift in Sources */,
|
||||
B56321AE1BD6521C006C9394 /* NotificationObserver.swift in Sources */,
|
||||
B56321931BD65216006C9394 /* DataStack+Querying.swift in Sources */,
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
import UIKit
|
||||
|
||||
import CoreStore
|
||||
|
||||
// MARK: - AppDelegate
|
||||
|
||||
@@ -22,6 +23,47 @@ class AppDelegate: UIResponder, UIApplicationDelegate {
|
||||
|
||||
application.statusBarStyle = .lightContent
|
||||
|
||||
/// Generated by CoreStore on 4/21/17, 2:41 PM
|
||||
class Place: CoreStoreObject {
|
||||
|
||||
let latitude = Value.Optional<Double>("latitude", default: 0.0)
|
||||
let title = Value.Optional<String>("title")
|
||||
let longitude = Value.Optional<Double>("longitude", default: 0.0)
|
||||
let subtitle = Value.Optional<String>("subtitle")
|
||||
}
|
||||
class Palette: CoreStoreObject {
|
||||
|
||||
let saturation = Value.Optional<Float>("saturation", default: 0.0)
|
||||
let hue = Value.Optional<Int32>("hue", default: 0)
|
||||
let brightness = Value.Optional<Float>("brightness", default: 0.0)
|
||||
let colorName = Value.Optional<String>("colorName", isTransient: true)
|
||||
}
|
||||
class TimeZone: CoreStoreObject {
|
||||
|
||||
let secondsFromGMT = Value.Optional<Int32>("secondsFromGMT", default: 0)
|
||||
let name = Value.Optional<String>("name")
|
||||
let daylightSavingTimeOffset = Value.Optional<Double>("daylightSavingTimeOffset", default: 0.0)
|
||||
let abbreviation = Value.Optional<String>("abbreviation")
|
||||
let hasDaylightSavingTime = Value.Optional<Bool>("hasDaylightSavingTime")
|
||||
}
|
||||
|
||||
|
||||
|
||||
let schema = CoreStoreSchema(
|
||||
modelVersion: "CoreStoreDemo",
|
||||
entities: [
|
||||
Entity<Place>("Place"),
|
||||
Entity<Palette>("Palette"),
|
||||
Entity<TimeZone>("TimeZone"),
|
||||
],
|
||||
versionLock: [
|
||||
"Place": [0x25cb5bd001887b92, 0xfe86dd433a5e0430, 0xcca50ac3f3659b68, 0xfe4e494ff66439b0],
|
||||
"Palette": [0xa306515d026d3c43, 0x1b299716733e56f6, 0x53bff8954221a1b6, 0xa74d6b1e613923ab],
|
||||
"TimeZone": [0x92e08db969e46163, 0xae9cf1ab738868c5, 0xb6a269249771a562, 0x58a357eab4c99ed5]
|
||||
]
|
||||
)
|
||||
|
||||
print(schema.printCoreStoreSchema())
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ class Palette: NSManagedObject {
|
||||
}
|
||||
set {
|
||||
|
||||
self.setValue(newValue, forKvcKey: #keyPath(Palette.colorName))
|
||||
self.setValue(newValue.cs_toImportableNativeType(), forKvcKey: #keyPath(Palette.colorName))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
249
Sources/Convenience/DynamicSchema+Convenience.swift
Normal file
249
Sources/Convenience/DynamicSchema+Convenience.swift
Normal file
@@ -0,0 +1,249 @@
|
||||
//
|
||||
// DynamicSchema+Convenience.swift
|
||||
// CoreStore
|
||||
//
|
||||
// Copyright © 2017 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 CoreData
|
||||
import Foundation
|
||||
|
||||
|
||||
// MARK: - DynamicSchema
|
||||
|
||||
public extension DynamicSchema {
|
||||
|
||||
public func printCoreStoreSchema() -> String {
|
||||
|
||||
let model = self.rawModel()
|
||||
let entitiesByName = model.entitiesByName
|
||||
|
||||
var output = "/// Generated by CoreStore on \(DateFormatter.localizedString(from: Date(), dateStyle: .short, timeStyle: .short))\n"
|
||||
var addedInverse: Set<String> = []
|
||||
for (entityName, entity) in entitiesByName {
|
||||
|
||||
let superName: String
|
||||
if let superEntity = entity.superentity {
|
||||
|
||||
superName = superEntity.name!
|
||||
}
|
||||
else {
|
||||
|
||||
superName = String(describing: CoreStoreObject.self)
|
||||
}
|
||||
output.append("class \(entityName): \(superName) {\n")
|
||||
defer {
|
||||
|
||||
output.append("}\n")
|
||||
}
|
||||
|
||||
let attributesByName = entity.attributesByName
|
||||
if !attributesByName.isEmpty {
|
||||
|
||||
output.append(" \n")
|
||||
for (attributeName, attribute) in attributesByName {
|
||||
|
||||
let containerType: String
|
||||
if attribute.isOptional {
|
||||
|
||||
containerType = "Value.Optional"
|
||||
}
|
||||
else {
|
||||
|
||||
containerType = "Value.Required"
|
||||
}
|
||||
let valueType: Any.Type
|
||||
var defaultString = ""
|
||||
switch attribute.attributeType {
|
||||
|
||||
case .integer16AttributeType:
|
||||
valueType = Int16.self
|
||||
if let defaultValue = (attribute.defaultValue as! Int16.ImportableNativeType?).flatMap(Int16.cs_fromImportableNativeType),
|
||||
defaultValue != Int16.cs_emptyValue() {
|
||||
|
||||
defaultString = ", default: \(defaultValue)"
|
||||
}
|
||||
case .integer32AttributeType:
|
||||
valueType = Int32.self
|
||||
if let defaultValue = (attribute.defaultValue as! Int32.ImportableNativeType?).flatMap(Int32.cs_fromImportableNativeType),
|
||||
defaultValue != Int32.cs_emptyValue() {
|
||||
|
||||
defaultString = ", default: \(defaultValue)"
|
||||
}
|
||||
case .integer64AttributeType:
|
||||
valueType = Int64.self
|
||||
if let defaultValue = (attribute.defaultValue as! Int64.ImportableNativeType?).flatMap(Int64.cs_fromImportableNativeType),
|
||||
defaultValue != Int54.cs_emptyValue() {
|
||||
|
||||
defaultString = ", default: \(defaultValue)"
|
||||
}
|
||||
case .decimalAttributeType:
|
||||
valueType = NSDecimalNumber.self
|
||||
if let defaultValue = (attribute.defaultValue as! NSDecimalNumber.ImportableNativeType?).flatMap(NSDecimalNumber.cs_fromImportableNativeType),
|
||||
defaultValue != NSDecimalNumber.cs_emptyValue() {
|
||||
|
||||
defaultString = ", default: NSDecimalNumber(string: \"\(defaultValue.description(withLocale: nil))\")"
|
||||
}
|
||||
case .doubleAttributeType:
|
||||
valueType = Double.self
|
||||
if let defaultValue = (attribute.defaultValue as! Double.ImportableNativeType?).flatMap(Double.cs_fromImportableNativeType),
|
||||
defaultValue != Double.cs_emptyValue() {
|
||||
|
||||
defaultString = ", default: \(defaultValue)"
|
||||
}
|
||||
case .floatAttributeType:
|
||||
valueType = Float.self
|
||||
if let defaultValue = (attribute.defaultValue as! Float.ImportableNativeType?).flatMap(Float.cs_fromImportableNativeType),
|
||||
defaultValue != Float.cs_emptyValue() {
|
||||
|
||||
defaultString = ", default: \(defaultValue)"
|
||||
}
|
||||
case .stringAttributeType:
|
||||
valueType = String.self
|
||||
if let defaultValue = (attribute.defaultValue as! String.ImportableNativeType?).flatMap(String.cs_fromImportableNativeType),
|
||||
defaultValue != String.cs_emptyValue() {
|
||||
|
||||
// TODO: escape strings
|
||||
defaultString = ", default: \"\(defaultValue)\""
|
||||
}
|
||||
case .booleanAttributeType:
|
||||
valueType = Bool.self
|
||||
if let defaultValue = (attribute.defaultValue as! Bool.ImportableNativeType?).flatMap(Bool.cs_fromImportableNativeType),
|
||||
defaultValue != Bool.cs_emptyValue() {
|
||||
|
||||
defaultString = ", default: \(defaultValue ? "true" : "false")"
|
||||
}
|
||||
case .dateAttributeType:
|
||||
valueType = Date.self
|
||||
if let defaultValue = (attribute.defaultValue as! Date.ImportableNativeType?).flatMap(Date.cs_fromImportableNativeType),
|
||||
defaultValue != Date.cs_emptyValue() {
|
||||
|
||||
defaultString = ", default: Date(timeIntervalSinceReferenceDate: \(defaultValue.timeIntervalSinceReferenceDate))"
|
||||
}
|
||||
case .binaryDataAttributeType:
|
||||
valueType = Data.self
|
||||
if let defaultValue = (attribute.defaultValue as! Data.ImportableNativeType?).flatMap(Data.cs_fromImportableNativeType),
|
||||
defaultValue != Data.cs_emptyValue() {
|
||||
|
||||
let count = defaultValue.count
|
||||
let bytes = defaultValue.withUnsafeBytes { (pointer: UnsafePointer<UInt8>) in
|
||||
|
||||
return (0 ..< (count / MemoryLayout<UInt8>.size))
|
||||
.map({ "\("0x\(String(pointer[$0], radix: 16, uppercase: false))")" })
|
||||
}
|
||||
defaultString = ", default: Data(bytes: [\(bytes.joined(separator: ", "))])"
|
||||
}
|
||||
default:
|
||||
fatalError("Unsupported attribute type: \(attribute.attributeType)")
|
||||
}
|
||||
let transientString = attribute.isTransient ? ", isTransient: true" : ""
|
||||
output.append(" let \(attributeName) = \(containerType)<\(String(describing: valueType))>(\"\(attributeName)\"\(defaultString)\(transientString))\n")
|
||||
}
|
||||
}
|
||||
|
||||
let relationshipsByName = entity.relationshipsByName
|
||||
if !relationshipsByName.isEmpty {
|
||||
|
||||
output.append(" \n")
|
||||
for (relationshipName, relationship) in relationshipsByName {
|
||||
|
||||
let containerType: String
|
||||
var minCountString = ""
|
||||
var maxCountString = ""
|
||||
if relationship.isToMany {
|
||||
|
||||
let minCount = relationship.minCount
|
||||
let maxCount = relationship.maxCount
|
||||
if relationship.isOrdered {
|
||||
|
||||
containerType = "Relationship.ToManyOrdered"
|
||||
}
|
||||
else {
|
||||
|
||||
containerType = "Relationship.ToManyUnordered"
|
||||
}
|
||||
if minCount > 0 {
|
||||
|
||||
minCountString = ", minCount: \(minCount)"
|
||||
}
|
||||
if maxCount > 0 {
|
||||
|
||||
maxCountString = ", maxCount: \(maxCount)"
|
||||
}
|
||||
}
|
||||
else {
|
||||
|
||||
containerType = "Relationship.ToOne"
|
||||
}
|
||||
var inverseString = ""
|
||||
let relationshipQualifier = "\(entityName).\(relationshipName)"
|
||||
if !addedInverse.contains(relationshipQualifier),
|
||||
let inverseRelationship = relationship.inverseRelationship {
|
||||
|
||||
inverseString = ", inverse: { $0.\(inverseRelationship.name) }"
|
||||
addedInverse.insert("\(relationship.destinationEntity!.name!).\(inverseRelationship.name)")
|
||||
}
|
||||
var deleteRuleString = ""
|
||||
if relationship.deleteRule != .nullifyDeleteRule {
|
||||
|
||||
switch relationship.deleteRule {
|
||||
|
||||
case .cascadeDeleteRule:
|
||||
deleteRuleString = ", deleteRule: .cascade"
|
||||
|
||||
case .denyDeleteRule:
|
||||
deleteRuleString = ", deleteRule: .deny"
|
||||
|
||||
case .nullifyDeleteRule:
|
||||
deleteRuleString = ", deleteRule: .nullify"
|
||||
|
||||
default:
|
||||
fatalError("Unsupported delete rule \((relationship.deleteRule)) for relationship \"\(relationshipQualifier)\"")
|
||||
}
|
||||
}
|
||||
output.append(" let \(relationshipName) = \(containerType)<\(relationship.destinationEntity!.name!)>(\"\(relationshipName)\"\(inverseString)\(deleteRuleString)\(minCountString)\(maxCountString))\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
output.append("\n\n\n")
|
||||
output.append("CoreStoreSchema(\n")
|
||||
output.append(" modelVersion: \"\(self.modelVersion)\",\n")
|
||||
output.append(" entities: [\n")
|
||||
for (entityName, entity) in entitiesByName {
|
||||
|
||||
var abstractString = ""
|
||||
if entity.isAbstract {
|
||||
|
||||
abstractString = ", isAbstract: true"
|
||||
}
|
||||
var versionHashModifierString = ""
|
||||
if let versionHashModifier = entity.versionHashModifier {
|
||||
|
||||
versionHashModifierString = ", versionHashModifier: \"\(versionHashModifier)\""
|
||||
}
|
||||
output.append(" Entity<\(entityName)>(\"\(entityName)\"\(abstractString)\(versionHashModifierString)),\n")
|
||||
}
|
||||
output.append(" ],\n")
|
||||
output.append(" versionLock: \(VersionLock(entityVersionHashesByName: model.entityVersionHashesByName).description.components(separatedBy: "\n").joined(separator: "\n "))\n")
|
||||
output.append(")\n\n")
|
||||
return output
|
||||
}
|
||||
}
|
||||
@@ -78,29 +78,29 @@ public extension NSManagedObject {
|
||||
}
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
public func getValue(forKvcKey kvcKey: KeyPath) -> Any? {
|
||||
public func getValue(forKvcKey kvcKey: KeyPath) -> CoreDataNativeType? {
|
||||
|
||||
self.willAccessValue(forKey: kvcKey)
|
||||
defer {
|
||||
|
||||
self.didAccessValue(forKey: kvcKey)
|
||||
}
|
||||
return self.primitiveValue(forKey: kvcKey)
|
||||
return self.primitiveValue(forKey: kvcKey) as! CoreDataNativeType?
|
||||
}
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
public func getValue<T>(forKvcKey kvcKey: KeyPath, didGetValue: (Any?) throws -> T) rethrows -> T {
|
||||
public func getValue<T>(forKvcKey kvcKey: KeyPath, didGetValue: (CoreDataNativeType?) throws -> T) rethrows -> T {
|
||||
|
||||
self.willAccessValue(forKey: kvcKey)
|
||||
defer {
|
||||
|
||||
self.didAccessValue(forKey: kvcKey)
|
||||
}
|
||||
return try didGetValue(self.primitiveValue(forKey: kvcKey))
|
||||
return try didGetValue(self.primitiveValue(forKey: kvcKey) as! CoreDataNativeType?)
|
||||
}
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
public func getValue<T>(forKvcKey kvcKey: KeyPath, willGetValue: () throws -> Void, didGetValue: (Any?) throws -> T) rethrows -> T {
|
||||
public func getValue<T>(forKvcKey kvcKey: KeyPath, willGetValue: () throws -> Void, didGetValue: (CoreDataNativeType?) throws -> T) rethrows -> T {
|
||||
|
||||
self.willAccessValue(forKey: kvcKey)
|
||||
defer {
|
||||
@@ -108,12 +108,11 @@ public extension NSManagedObject {
|
||||
self.didAccessValue(forKey: kvcKey)
|
||||
}
|
||||
try willGetValue()
|
||||
return try didGetValue(self.primitiveValue(forKey: kvcKey))
|
||||
return try didGetValue(self.primitiveValue(forKey: kvcKey) as! CoreDataNativeType?)
|
||||
}
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
@discardableResult
|
||||
public func setValue(_ value: Any?, forKvcKey KVCKey: KeyPath) -> Any? {
|
||||
public func setValue(_ value: CoreDataNativeType?, forKvcKey KVCKey: KeyPath) {
|
||||
|
||||
self.willChangeValue(forKey: KVCKey)
|
||||
defer {
|
||||
@@ -121,12 +120,10 @@ public extension NSManagedObject {
|
||||
self.didChangeValue(forKey: KVCKey)
|
||||
}
|
||||
self.setPrimitiveValue(value, forKey: KVCKey)
|
||||
return value
|
||||
}
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
@discardableResult
|
||||
public func setValue<T>(_ value: T, forKvcKey KVCKey: KeyPath, willSetValue: (T) throws -> Any?) rethrows -> T {
|
||||
public func setValue<T>(_ value: T, forKvcKey KVCKey: KeyPath, willSetValue: (T) throws -> CoreDataNativeType?) rethrows {
|
||||
|
||||
self.willChangeValue(forKey: KVCKey)
|
||||
defer {
|
||||
@@ -134,20 +131,6 @@ public extension NSManagedObject {
|
||||
self.didChangeValue(forKey: KVCKey)
|
||||
}
|
||||
self.setPrimitiveValue(try willSetValue(value), forKey: KVCKey)
|
||||
return value
|
||||
}
|
||||
|
||||
@nonobjc @inline(__always)
|
||||
@discardableResult
|
||||
public func setValue<T>(_ value: T, forKvcKey KVCKey: KeyPath, willSetValue: (T) throws -> Any?, didSetValue: (T) -> T = { $0 }) rethrows -> T {
|
||||
|
||||
self.willChangeValue(forKey: KVCKey)
|
||||
defer {
|
||||
|
||||
self.didChangeValue(forKey: KVCKey)
|
||||
}
|
||||
self.setPrimitiveValue(try willSetValue(value), forKey: KVCKey)
|
||||
return didSetValue(value)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
@@ -45,7 +45,7 @@ public typealias ModelConfiguration = String?
|
||||
// MARK: - ModelVersion
|
||||
|
||||
/**
|
||||
An `String` that pertains to the name of a versioned *.xcdatamodeld file (without the file extension).
|
||||
An `String` that pertains to the name of a versioned *.xcdatamodeld file (without the file extension). Model version strings don't necessarily have to be numeric or ordered in any way. The migration sequence will always be decided by (or the lack of) the `MigrationChain`.
|
||||
*/
|
||||
public typealias ModelVersion = String
|
||||
|
||||
|
||||
@@ -190,7 +190,7 @@ public extension ImportableUniqueObject where Self: DynamicObject {
|
||||
.setValue(
|
||||
newValue,
|
||||
forKvcKey: type(of: self).uniqueIDKeyPath,
|
||||
willSetValue: { $0.cs_toImportableNativeType() }
|
||||
willSetValue: { ($0.cs_toImportableNativeType() as! CoreDataNativeType) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,23 +38,29 @@ internal extension NSEntityDescription {
|
||||
|
||||
guard let userInfo = self.userInfo,
|
||||
let typeName = userInfo[UserInfoKey.CoreStoreManagedObjectTypeName] as! String?,
|
||||
let entityName = userInfo[UserInfoKey.CoreStoreManagedObjectEntityName] as! String? else {
|
||||
let entityName = userInfo[UserInfoKey.CoreStoreManagedObjectEntityName] as! String?,
|
||||
let isAbstract = userInfo[UserInfoKey.CoreStoreManagedObjectIsAbstract] as! Bool? else {
|
||||
|
||||
return nil
|
||||
}
|
||||
return CoreStoreSchema.AnyEntity(
|
||||
type: NSClassFromString(typeName) as! CoreStoreObject.Type,
|
||||
entityName: entityName
|
||||
entityName: entityName,
|
||||
isAbstract: isAbstract,
|
||||
versionHashModifier: userInfo[UserInfoKey.CoreStoreManagedObjectVersionHashModifier] as! String?
|
||||
)
|
||||
}
|
||||
set {
|
||||
|
||||
if let newValue = newValue {
|
||||
|
||||
self.userInfo = [
|
||||
var userInfo: [AnyHashable : Any] = [
|
||||
UserInfoKey.CoreStoreManagedObjectTypeName: NSStringFromClass(newValue.type),
|
||||
UserInfoKey.CoreStoreManagedObjectEntityName: newValue.entityName
|
||||
UserInfoKey.CoreStoreManagedObjectEntityName: newValue.entityName,
|
||||
UserInfoKey.CoreStoreManagedObjectIsAbstract: newValue.isAbstract
|
||||
]
|
||||
userInfo[UserInfoKey.CoreStoreManagedObjectVersionHashModifier] = newValue.versionHashModifier
|
||||
self.userInfo = userInfo
|
||||
}
|
||||
else {
|
||||
|
||||
@@ -72,5 +78,7 @@ internal extension NSEntityDescription {
|
||||
|
||||
fileprivate static let CoreStoreManagedObjectTypeName = "CoreStoreManagedObjectTypeName"
|
||||
fileprivate static let CoreStoreManagedObjectEntityName = "CoreStoreManagedObjectEntityName"
|
||||
fileprivate static let CoreStoreManagedObjectIsAbstract = "CoreStoreManagedObjectIsAbstract"
|
||||
fileprivate static let CoreStoreManagedObjectVersionHashModifier = "CoreStoreManagedObjectVersionHashModifier"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,6 +48,25 @@ internal extension NSManagedObject {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@nonobjc
|
||||
internal func isEditableInContext() -> Bool? {
|
||||
|
||||
guard let context = self.managedObjectContext else {
|
||||
|
||||
return nil
|
||||
}
|
||||
if context.isTransactionContext {
|
||||
|
||||
return true
|
||||
}
|
||||
if context.isDataStackContext {
|
||||
|
||||
return false
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TODO: test before release (rolled back)
|
||||
// @nonobjc
|
||||
// internal static func cs_swizzleMethodsForLogging() {
|
||||
|
||||
@@ -31,6 +31,78 @@ import Foundation
|
||||
|
||||
public protocol MigrationMappingProvider {
|
||||
|
||||
associatedtype SourceType: DynamicObject
|
||||
associatedtype DestinationType: DynamicObject
|
||||
associatedtype SourceSchema: DynamicSchema
|
||||
associatedtype DestinationSchema: DynamicSchema
|
||||
|
||||
var sourceSchema: SourceSchema { get }
|
||||
var destinationSchema: DestinationSchema { get }
|
||||
|
||||
init(source: SourceSchema, destination: DestinationSchema)
|
||||
|
||||
// func migrate(
|
||||
// from oldObject: SourceType,
|
||||
// to newObject: DestinationType,
|
||||
// transaction: UnsafeDataTransaction
|
||||
// )
|
||||
//
|
||||
// func forEachPropertyMapping(
|
||||
// from oldObject: SourceType,
|
||||
// to newObject: DestinationType,
|
||||
// removed: (_ keyPath: KeyPath) -> Void,
|
||||
// added: (_ keyPath: KeyPath) -> Void,
|
||||
// transformed: (_ keyPath: KeyPath) -> Void,
|
||||
// copied: (_ keyPath: KeyPath) -> Void
|
||||
// )
|
||||
}
|
||||
|
||||
public extension MigrationMappingProvider {
|
||||
|
||||
// func migrate(from oldObject: SourceType, to newObject: DestinationType, transaction: UnsafeDataTransaction) {
|
||||
//
|
||||
//
|
||||
// }
|
||||
//
|
||||
// func forEachPropertyMapping(from oldObject: SourceType, to newObject: DestinationType, removed: (_ keyPath: KeyPath) -> Void, added: (_ keyPath: KeyPath) -> Void, transformed: (_ keyPath: KeyPath) -> Void) {
|
||||
//
|
||||
// let oldAttributes = oldObject.cs_toRaw().entity.attributesByName
|
||||
// let newAttributes = newObject.cs_toRaw().entity.attributesByName
|
||||
// let oldAttributeKeys = Set(oldAttributes.keys)
|
||||
// let newAttributeKeys = Set(newAttributes.keys)
|
||||
// for keyPath in
|
||||
// }
|
||||
}
|
||||
|
||||
public extension MigrationMappingProvider {
|
||||
|
||||
}
|
||||
|
||||
|
||||
// MARK: - UnsafeMigrationProxyObject
|
||||
|
||||
public final class UnsafeMigrationProxyObject {
|
||||
|
||||
public subscript(kvcKey: KeyPath) -> CoreDataNativeType? {
|
||||
|
||||
get {
|
||||
|
||||
return self.rawObject.cs_accessValueForKVCKey(kvcKey)
|
||||
}
|
||||
set {
|
||||
|
||||
self.rawObject.cs_setValue(newValue, forKVCKey: kvcKey)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// MARK: Internal
|
||||
|
||||
internal init(_ rawObject: NSManagedObject) {
|
||||
|
||||
self.rawObject = rawObject
|
||||
}
|
||||
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private let rawObject: NSManagedObject
|
||||
}
|
||||
|
||||
@@ -38,7 +38,7 @@ public extension NSManagedObject {
|
||||
- returns: the primitive value for the KVC key
|
||||
*/
|
||||
@objc
|
||||
public func cs_accessValueForKVCKey(_ kvcKey: KeyPath) -> Any? {
|
||||
public func cs_accessValueForKVCKey(_ kvcKey: KeyPath) -> CoreDataNativeType? {
|
||||
|
||||
return self.getValue(forKvcKey: kvcKey)
|
||||
}
|
||||
@@ -50,7 +50,7 @@ public extension NSManagedObject {
|
||||
- parameter KVCKey: the KVC key
|
||||
*/
|
||||
@objc
|
||||
public func cs_setValue(_ value: Any?, forKVCKey KVCKey: KeyPath) {
|
||||
public func cs_setValue(_ value: CoreDataNativeType?, forKVCKey KVCKey: KeyPath) {
|
||||
|
||||
self.setValue(value, forKvcKey: KVCKey)
|
||||
}
|
||||
|
||||
@@ -96,6 +96,14 @@ public final class DataStack: Equatable {
|
||||
return self.schemaHistory.currentModelVersion
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the `DataStack`'s model schema.
|
||||
*/
|
||||
public var modelSchema: DynamicSchema {
|
||||
|
||||
return self.schemaHistory.schemaByVersion[self.schemaHistory.currentModelVersion]!
|
||||
}
|
||||
|
||||
/**
|
||||
Returns the entity name-to-class type mapping from the `DataStack`'s model.
|
||||
*/
|
||||
|
||||
@@ -31,7 +31,7 @@ import Foundation
|
||||
|
||||
public final class CoreStoreSchema: DynamicSchema {
|
||||
|
||||
public convenience init(modelVersion: String, entities: [DynamicEntity], versionLock: VersionLock? = nil) {
|
||||
public convenience init(modelVersion: ModelVersion, entities: [DynamicEntity], versionLock: VersionLock? = nil) {
|
||||
|
||||
self.init(
|
||||
modelVersion: modelVersion,
|
||||
@@ -40,7 +40,7 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
)
|
||||
}
|
||||
|
||||
public required init(modelVersion: String, entitiesByConfiguration: [String: [DynamicEntity]], versionLock: VersionLock? = nil) {
|
||||
public required init(modelVersion: ModelVersion, entitiesByConfiguration: [String: [DynamicEntity]], versionLock: VersionLock? = nil) {
|
||||
|
||||
var actualEntitiesByConfiguration: [String: Set<AnyEntity>] = [:]
|
||||
for (configuration, entities) in entitiesByConfiguration {
|
||||
@@ -129,14 +129,20 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
|
||||
internal init(_ entity: DynamicEntity) {
|
||||
|
||||
self.type = entity.type
|
||||
self.entityName = entity.entityName
|
||||
self.init(
|
||||
type: entity.type,
|
||||
entityName: entity.entityName,
|
||||
isAbstract: entity.isAbstract,
|
||||
versionHashModifier: entity.versionHashModifier
|
||||
)
|
||||
}
|
||||
|
||||
internal init(type: CoreStoreObject.Type, entityName: String) {
|
||||
internal init(type: CoreStoreObject.Type, entityName: String, isAbstract: Bool, versionHashModifier: String?) {
|
||||
|
||||
self.type = type
|
||||
self.entityName = entityName
|
||||
self.isAbstract = isAbstract
|
||||
self.versionHashModifier = versionHashModifier
|
||||
}
|
||||
|
||||
|
||||
@@ -146,6 +152,8 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
|
||||
return lhs.type == rhs.type
|
||||
&& lhs.entityName == rhs.entityName
|
||||
&& lhs.isAbstract == rhs.isAbstract
|
||||
&& lhs.versionHashModifier == rhs.versionHashModifier
|
||||
}
|
||||
|
||||
// MARK: Hashable
|
||||
@@ -154,12 +162,16 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
|
||||
return ObjectIdentifier(self.type).hashValue
|
||||
^ self.entityName.hashValue
|
||||
^ self.isAbstract.hashValue
|
||||
^ (self.versionHashModifier ?? "").hashValue
|
||||
}
|
||||
|
||||
// MARK: DynamicEntity
|
||||
|
||||
internal let type: CoreStoreObject.Type
|
||||
internal let entityName: EntityName
|
||||
internal let isAbstract: Bool
|
||||
internal let versionHashModifier: String?
|
||||
}
|
||||
|
||||
|
||||
@@ -193,6 +205,7 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
let entityDescription = NSEntityDescription()
|
||||
entityDescription.anyEntity = entity
|
||||
entityDescription.name = entity.entityName
|
||||
entityDescription.isAbstract = entity.isAbstract
|
||||
entityDescription.managedObjectClassName = NSStringFromClass(NSManagedObject.self)
|
||||
|
||||
func createProperties(for type: CoreStoreObject.Type) -> [NSPropertyDescription] {
|
||||
@@ -216,8 +229,8 @@ public final class CoreStoreSchema: DynamicSchema {
|
||||
case let relationship as RelationshipProtocol:
|
||||
let description = NSRelationshipDescription()
|
||||
description.name = relationship.keyPath
|
||||
description.minCount = 0
|
||||
description.maxCount = relationship.isToMany ? 0 : 1
|
||||
description.minCount = relationship.minCount
|
||||
description.maxCount = relationship.maxCount
|
||||
description.isOrdered = relationship.isOrdered
|
||||
description.deleteRule = relationship.deleteRule
|
||||
// TODO: versionHash, renamingIdentifier, etc
|
||||
|
||||
@@ -34,6 +34,8 @@ public protocol DynamicEntity {
|
||||
|
||||
var type: CoreStoreObject.Type { get }
|
||||
var entityName: EntityName { get }
|
||||
var isAbstract: Bool { get }
|
||||
var versionHashModifier: String? { get }
|
||||
}
|
||||
|
||||
|
||||
@@ -41,15 +43,17 @@ public protocol DynamicEntity {
|
||||
|
||||
public struct Entity<O: CoreStoreObject>: DynamicEntity, Hashable {
|
||||
|
||||
public init(_ entityName: String) {
|
||||
public init(_ entityName: String, isAbstract: Bool = false, versionHashModifier: String? = nil) {
|
||||
|
||||
self.init(O.self, entityName)
|
||||
self.init(O.self, entityName, isAbstract: isAbstract, versionHashModifier: versionHashModifier)
|
||||
}
|
||||
|
||||
public init(_ type: O.Type, _ entityName: String) {
|
||||
public init(_ type: O.Type, _ entityName: String, isAbstract: Bool = false, versionHashModifier: String? = nil) {
|
||||
|
||||
self.type = type
|
||||
self.entityName = entityName
|
||||
self.isAbstract = isAbstract
|
||||
self.versionHashModifier = versionHashModifier
|
||||
}
|
||||
|
||||
|
||||
@@ -57,6 +61,8 @@ public struct Entity<O: CoreStoreObject>: DynamicEntity, Hashable {
|
||||
|
||||
public let type: CoreStoreObject.Type
|
||||
public let entityName: EntityName
|
||||
public let isAbstract: Bool
|
||||
public let versionHashModifier: String?
|
||||
|
||||
|
||||
// MARK: Equatable
|
||||
@@ -65,6 +71,8 @@ public struct Entity<O: CoreStoreObject>: DynamicEntity, Hashable {
|
||||
|
||||
return lhs.type == rhs.type
|
||||
&& lhs.entityName == rhs.entityName
|
||||
&& lhs.isAbstract == rhs.isAbstract
|
||||
&& lhs.versionHashModifier == rhs.versionHashModifier
|
||||
}
|
||||
|
||||
// MARK: Hashable
|
||||
@@ -72,5 +80,8 @@ public struct Entity<O: CoreStoreObject>: DynamicEntity, Hashable {
|
||||
public var hashValue: Int {
|
||||
|
||||
return ObjectIdentifier(self.type).hashValue
|
||||
^ self.entityName.hashValue
|
||||
^ self.isAbstract.hashValue
|
||||
^ (self.versionHashModifier ?? "").hashValue
|
||||
}
|
||||
}
|
||||
|
||||
@@ -100,6 +100,10 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
self.accessRawObject().isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
self.accessRawObject().isEditableInContext() == true,
|
||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||
)
|
||||
self.accessRawObject()
|
||||
.setValue(
|
||||
newValue,
|
||||
@@ -117,6 +121,8 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
internal let isToMany = false
|
||||
internal let isOrdered = false
|
||||
internal let deleteRule: NSDeleteRule
|
||||
internal let minCount: Int = 0
|
||||
internal let maxCount: Int = 1
|
||||
internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?)
|
||||
|
||||
internal var accessRawObject: () -> NSManagedObject = {
|
||||
@@ -157,24 +163,24 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
relationship.value = relationship2.value
|
||||
}
|
||||
|
||||
public convenience init(_ keyPath: KeyPath, deleteRule: DeleteRule = .nullify) {
|
||||
public convenience init(_ keyPath: KeyPath, deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0) {
|
||||
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { nil }, deleteRule: deleteRule)
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { nil }, deleteRule: deleteRule, minCount: minCount, maxCount: maxCount)
|
||||
}
|
||||
|
||||
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToOne<O>, deleteRule: DeleteRule = .nullify) {
|
||||
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToOne<O>, deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0) {
|
||||
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule)
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, minCount: minCount, maxCount: maxCount)
|
||||
}
|
||||
|
||||
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToManyOrdered<O>, deleteRule: DeleteRule = .nullify) {
|
||||
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToManyOrdered<O>, deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0) {
|
||||
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule)
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, minCount: minCount, maxCount: maxCount)
|
||||
}
|
||||
|
||||
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToManyUnordered<O>, deleteRule: DeleteRule = .nullify) {
|
||||
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToManyUnordered<O>, deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0) {
|
||||
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule)
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, minCount: minCount, maxCount: maxCount)
|
||||
}
|
||||
|
||||
// TODO: add subscripts, indexed operations for more performant single updates
|
||||
@@ -206,6 +212,10 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
self.accessRawObject().isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
self.accessRawObject().isEditableInContext() == true,
|
||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||
)
|
||||
self.accessRawObject()
|
||||
.setValue(
|
||||
newValue,
|
||||
@@ -224,6 +234,8 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
internal let isOptional = true
|
||||
internal let isOrdered = true
|
||||
internal let deleteRule: NSDeleteRule
|
||||
internal let minCount: Int
|
||||
internal let maxCount: Int
|
||||
internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?)
|
||||
|
||||
internal var accessRawObject: () -> NSManagedObject = {
|
||||
@@ -234,11 +246,15 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private init(keyPath: String, inverseKeyPath: @escaping () -> String?, deleteRule: DeleteRule) {
|
||||
private init(keyPath: String, inverseKeyPath: @escaping () -> String?, deleteRule: DeleteRule, minCount: Int, maxCount: Int) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.deleteRule = deleteRule.nativeValue
|
||||
self.inverse = (D.self, inverseKeyPath)
|
||||
|
||||
let range = (max(0, minCount) ... maxCount)
|
||||
self.minCount = range.lowerBound
|
||||
self.maxCount = range.upperBound
|
||||
}
|
||||
}
|
||||
|
||||
@@ -269,24 +285,24 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
relationship.value = Set(relationship2.value)
|
||||
}
|
||||
|
||||
public convenience init(_ keyPath: KeyPath, deleteRule: DeleteRule = .nullify) {
|
||||
public convenience init(_ keyPath: KeyPath, deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0) {
|
||||
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { nil }, deleteRule: deleteRule)
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { nil }, deleteRule: deleteRule, minCount: minCount, maxCount: maxCount)
|
||||
}
|
||||
|
||||
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToOne<O>, deleteRule: DeleteRule = .nullify) {
|
||||
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToOne<O>, deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0) {
|
||||
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule)
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, minCount: minCount, maxCount: maxCount)
|
||||
}
|
||||
|
||||
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToManyOrdered<O>, deleteRule: DeleteRule = .nullify) {
|
||||
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToManyOrdered<O>, deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0) {
|
||||
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule)
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, minCount: minCount, maxCount: maxCount)
|
||||
}
|
||||
|
||||
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToManyUnordered<O>, deleteRule: DeleteRule = .nullify) {
|
||||
public convenience init(_ keyPath: KeyPath, inverse: @escaping (D) -> RelationshipContainer<D>.ToManyUnordered<O>, deleteRule: DeleteRule = .nullify, minCount: Int = 0, maxCount: Int = 0) {
|
||||
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule)
|
||||
self.init(keyPath: keyPath, inverseKeyPath: { inverse(D.meta).keyPath }, deleteRule: deleteRule, minCount: minCount, maxCount: maxCount)
|
||||
}
|
||||
|
||||
// TODO: add subscripts, indexed operations for more performant single updates
|
||||
@@ -318,6 +334,10 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
self.accessRawObject().isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
self.accessRawObject().isEditableInContext() == true,
|
||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||
)
|
||||
self.accessRawObject()
|
||||
.setValue(
|
||||
newValue,
|
||||
@@ -336,6 +356,8 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
internal let isOptional = true
|
||||
internal let isOrdered = true
|
||||
internal let deleteRule: NSDeleteRule
|
||||
internal let minCount: Int
|
||||
internal let maxCount: Int
|
||||
internal let inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?)
|
||||
|
||||
internal var accessRawObject: () -> NSManagedObject = {
|
||||
@@ -346,11 +368,15 @@ public enum RelationshipContainer<O: CoreStoreObject> {
|
||||
|
||||
// MARK: Private
|
||||
|
||||
private init(keyPath: KeyPath, inverseKeyPath: @escaping () -> KeyPath?, deleteRule: DeleteRule) {
|
||||
private init(keyPath: KeyPath, inverseKeyPath: @escaping () -> KeyPath?, deleteRule: DeleteRule, minCount: Int, maxCount: Int) {
|
||||
|
||||
self.keyPath = keyPath
|
||||
self.deleteRule = deleteRule.nativeValue
|
||||
self.inverse = (D.self, inverseKeyPath)
|
||||
|
||||
let range = (max(0, minCount) ... maxCount)
|
||||
self.minCount = range.lowerBound
|
||||
self.maxCount = range.upperBound
|
||||
}
|
||||
}
|
||||
|
||||
@@ -386,4 +412,6 @@ internal protocol RelationshipProtocol: class {
|
||||
var deleteRule: NSDeleteRule { get }
|
||||
var inverse: (type: CoreStoreObject.Type, keyPath: () -> KeyPath?) { get }
|
||||
var accessRawObject: () -> NSManagedObject { get set }
|
||||
var minCount: Int { get }
|
||||
var maxCount: Int { get }
|
||||
}
|
||||
|
||||
@@ -86,11 +86,15 @@ public enum ValueContainer<O: CoreStoreObject> {
|
||||
self.accessRawObject().isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
self.accessRawObject().isEditableInContext() == true,
|
||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||
)
|
||||
self.accessRawObject()
|
||||
.setValue(
|
||||
newValue,
|
||||
forKvcKey: self.keyPath,
|
||||
willSetValue: { $0.cs_toImportableNativeType() }
|
||||
willSetValue: { ($0.cs_toImportableNativeType() as! CoreDataNativeType) }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -163,11 +167,15 @@ public enum ValueContainer<O: CoreStoreObject> {
|
||||
self.accessRawObject().isRunningInAllowedQueue() == true,
|
||||
"Attempted to access \(cs_typeName(O.self))'s value outside it's designated queue."
|
||||
)
|
||||
CoreStore.assert(
|
||||
self.accessRawObject().isEditableInContext() == true,
|
||||
"Attempted to update a \(cs_typeName(O.self))'s value from outside a transaction."
|
||||
)
|
||||
self.accessRawObject()
|
||||
.setValue(
|
||||
newValue,
|
||||
forKvcKey: self.keyPath,
|
||||
willSetValue: { $0?.cs_toImportableNativeType() }
|
||||
willSetValue: { ($0?.cs_toImportableNativeType() as! CoreDataNativeType?) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user