mirror of
https://github.com/JohnEstropia/CoreStore.git
synced 2026-03-18 23:44:14 +01:00
added tool to convert existing NSManagedObjectModels to the new CoreStoreSchema
This commit is contained in:
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)
|
||||
}
|
||||
|
||||
/**
|
||||
|
||||
Reference in New Issue
Block a user