From 5af0d17de4289c0540d23b2511c35f7c1127ddd9 Mon Sep 17 00:00:00 2001 From: John Estropia Date: Sat, 12 Oct 2019 07:20:09 +0900 Subject: [PATCH] remov stateIDs --- .../UserInterfaceState.xcuserstate | Bin 9251 -> 0 bytes CoreStore.xcodeproj/project.pbxproj | 64 ++++++-- .../xcschemes/CoreStore iOS.xcscheme | 26 ++- .../CoreStoreDemo.xcodeproj/project.pbxproj | 3 + .../SwiftUIContainerViewController.swift | 2 +- .../SwiftUI Demo/SwiftUIView.swift | 53 +++++-- CoreStoreTests/DynamicModelTests.swift | 90 ++++++++--- CoreStoreTests/LiveListTests.swift | 2 +- Sources/AttributeProtocol.swift | 3 +- Sources/CoreStoreObject+DataSources.swift | 2 +- Sources/CoreStoreObject.swift | 38 +++++ Sources/CoreStoreSchema.swift | 9 +- Sources/DataStack+DataSources.swift | 2 +- Sources/DiffableDataSource.TableView.swift | 6 +- .../DiffableDataSourceSnapshotProtocol.swift | 34 ++++ Sources/DynamicObject.swift | 8 +- Sources/EnvironmentKeys.swift | 25 ++- ...Internals.DiffableDataSourceSnapshot.swift | 149 +++++------------- ...edDiffableDataSourceSnapshotDelegate.swift | 46 +----- Sources/ListSnapshot.swift | 22 +-- Sources/LiveList.swift | 29 ++-- Sources/LiveObject.swift | 29 ++-- Sources/LiveQuery.swift | 4 +- Sources/LiveResult.swift | 3 +- Sources/ObjectRepresentation.swift | 9 ++ Sources/PropertyProtocol.swift | 35 ++++ Sources/Relationship.swift | 22 ++- Sources/RelationshipProtocol.swift | 5 +- Sources/SnapshotResult.swift | 1 - Sources/Transformable.swift | 12 +- Sources/Value.swift | 14 +- 31 files changed, 456 insertions(+), 291 deletions(-) delete mode 100644 .swiftpm/xcode/package.xcworkspace/xcuserdata/JohnEstropia.xcuserdatad/UserInterfaceState.xcuserstate create mode 100644 Sources/DiffableDataSourceSnapshotProtocol.swift create mode 100644 Sources/ObjectRepresentation.swift create mode 100644 Sources/PropertyProtocol.swift diff --git a/.swiftpm/xcode/package.xcworkspace/xcuserdata/JohnEstropia.xcuserdatad/UserInterfaceState.xcuserstate b/.swiftpm/xcode/package.xcworkspace/xcuserdata/JohnEstropia.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index 78e6720d2b5bf2dfee54fb95d4faa6df0117cb73..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 9251 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 - - - - - - + + + + + + - - ()) - palette.setInitialValues(in: transaction) - }, - completion: { _ in } - ) - }, - label: { - Image(systemName: "plus") - } - ) + for palette in try transaction.fetchAll(From()) { + + palette.hue .= Palette.randomHue() + palette.colorName .= nil + } + }, + completion: { _ in } + ) + }, + label: { + Image(systemName: "goforward") + } + ) + .frame(width: 30) + Button( + action: { + + self.dataStack.perform( + asynchronous: { transaction in + + let palette = transaction.create(Into()) + palette.setInitialValues(in: transaction) + }, + completion: { _ in } + ) + }, + label: { + Image(systemName: "plus") + } + ) + .frame(width: 30) + } ) .alert( isPresented: $needsShowAlert, diff --git a/CoreStoreTests/DynamicModelTests.swift b/CoreStoreTests/DynamicModelTests.swift index 2283795..1147710 100644 --- a/CoreStoreTests/DynamicModelTests.swift +++ b/CoreStoreTests/DynamicModelTests.swift @@ -164,24 +164,72 @@ class DynamicModelTests: BaseTestDataTestCase { animal.color .= .yellow XCTAssertEqual(animal.color.value, Color.yellow) + + for property in Animal.metaProperties(includeSuperclasses: true) { + + switch property.keyPath { + + case String(keyPath: \Animal.species): + XCTAssertTrue(property is ValueContainer.Required) + + case String(keyPath: \Animal.master): + XCTAssertTrue(property is RelationshipContainer.ToOne) + + case String(keyPath: \Animal.color): + XCTAssertTrue(property is TransformableContainer.Optional) + + default: + XCTFail("Unknown KeyPath: \"\(property.keyPath)\"") + } + } let dog = transaction.create(Into()) XCTAssertEqual(dog.species.value, "Swift") XCTAssertEqual(dog.nickname.value, nil) XCTAssertEqual(dog.age.value, 1) - #if swift(>=5.1) + for property in Dog.metaProperties(includeSuperclasses: true) { - let dogKeyPathBuilder = Dog.keyPathBuilder() - XCTAssertEqual(dogKeyPathBuilder.species.keyPathString, "SELF.species") - XCTAssertEqual(dogKeyPathBuilder.master.title.keyPathString, "SELF.master.title") - let a = dogKeyPathBuilder.master - let b = dogKeyPathBuilder.master.spouse - let c = dogKeyPathBuilder.master.spouse.pets - let d = dogKeyPathBuilder.master.spouse.pets.color - XCTAssertEqual(dogKeyPathBuilder.master.spouse.pets.color.keyPathString, "SELF.master.spouse.pets.color") + switch property.keyPath { - #endif + case String(keyPath: \Dog.species): + XCTAssertTrue(property is ValueContainer.Required) + + case String(keyPath: \Dog.master): + XCTAssertTrue(property is RelationshipContainer.ToOne) + + case String(keyPath: \Dog.color): + XCTAssertTrue(property is TransformableContainer.Optional) + + case String(keyPath: \Dog.nickname): + XCTAssertTrue(property is ValueContainer.Optional) + + case String(keyPath: \Dog.age): + XCTAssertTrue(property is ValueContainer.Required) + + case String(keyPath: \Dog.friends): + XCTAssertTrue(property is RelationshipContainer.ToManyOrdered) + + case String(keyPath: \Dog.friendedBy): + XCTAssertTrue(property is RelationshipContainer.ToManyUnordered) + + default: + XCTFail("Unknown KeyPath: \"\(property.keyPath)\"") + } + } + +// #if swift(>=5.1) +// +// let dogKeyPathBuilder = Dog.keyPathBuilder() +// XCTAssertEqual(dogKeyPathBuilder.species.keyPathString, "SELF.species") +// XCTAssertEqual(dogKeyPathBuilder.master.title.keyPathString, "SELF.master.title") +// let a = dogKeyPathBuilder.master +// let b = dogKeyPathBuilder.master.spouse +// let c = dogKeyPathBuilder.master.spouse.pets +// let d = dogKeyPathBuilder.master.spouse.pets.color +// XCTAssertEqual(dogKeyPathBuilder.master.spouse.pets.color.keyPathString, "SELF.master.spouse.pets.color") +// +// #endif let didSetObserver = dog.species.observe(options: [.new, .old]) { (object, change) in @@ -244,22 +292,22 @@ class DynamicModelTests: BaseTestDataTestCase { XCTAssertEqual(person.name.value, "John") XCTAssertEqual(person.displayName.value, "Mr. John") // Custom getter - let personSnapshot1 = person.createSnapshot() - XCTAssertEqual(person.name.value, personSnapshot1.name) - XCTAssertEqual(person.title.value, personSnapshot1.title) - XCTAssertEqual(person.displayName.value, personSnapshot1.displayName) +// let personSnapshot1 = person.createSnapshot() +// XCTAssertEqual(person.name.value, personSnapshot1.name) +// XCTAssertEqual(person.title.value, personSnapshot1.title) +// XCTAssertEqual(person.displayName.value, personSnapshot1.displayName) person.title .= "Sir" XCTAssertEqual(person.displayName.value, "Sir John") - XCTAssertEqual(personSnapshot1.name, "John") - XCTAssertEqual(personSnapshot1.title, "Mr.") - XCTAssertEqual(personSnapshot1.displayName, "Mr. John") +// XCTAssertEqual(personSnapshot1.name, "John") +// XCTAssertEqual(personSnapshot1.title, "Mr.") +// XCTAssertEqual(personSnapshot1.displayName, "Mr. John") - let personSnapshot2 = person.createSnapshot() - XCTAssertEqual(person.name.value, personSnapshot2.name) - XCTAssertEqual(person.title.value, personSnapshot2.title) - XCTAssertEqual(person.displayName.value, personSnapshot2.displayName) +// let personSnapshot2 = person.createSnapshot() +// XCTAssertEqual(person.name.value, personSnapshot2.name) +// XCTAssertEqual(person.title.value, personSnapshot2.title) +// XCTAssertEqual(person.displayName.value, personSnapshot2.displayName) person.pets.value.insert(dog) XCTAssertEqual(person.pets.count, 1) diff --git a/CoreStoreTests/LiveListTests.swift b/CoreStoreTests/LiveListTests.swift index e9ad339..0d8184c 100644 --- a/CoreStoreTests/LiveListTests.swift +++ b/CoreStoreTests/LiveListTests.swift @@ -33,7 +33,7 @@ import CoreStore // MARK: - LiveListTests -@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) class LiveListTests: BaseTestDataTestCase { @objc diff --git a/Sources/AttributeProtocol.swift b/Sources/AttributeProtocol.swift index 0890817..37aa359 100644 --- a/Sources/AttributeProtocol.swift +++ b/Sources/AttributeProtocol.swift @@ -29,11 +29,10 @@ import CoreData // MARK: - AttributeProtocol -internal protocol AttributeProtocol: AnyObject { +internal protocol AttributeProtocol: PropertyProtocol { static var attributeType: NSAttributeType { get } - var keyPath: KeyPathString { get } var isOptional: Bool { get } var isTransient: Bool { get } var allowsExternalBinaryDataStorage: Bool { get } diff --git a/Sources/CoreStoreObject+DataSources.swift b/Sources/CoreStoreObject+DataSources.swift index 4080be2..3147b2c 100644 --- a/Sources/CoreStoreObject+DataSources.swift +++ b/Sources/CoreStoreObject+DataSources.swift @@ -13,7 +13,7 @@ import Combine // MARK: - LiveList: ObservableObject -@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) extension CoreStoreObject: ObservableObject { // MARK: ObservableObject diff --git a/Sources/CoreStoreObject.swift b/Sources/CoreStoreObject.swift index 34b5de7..43d73f3 100644 --- a/Sources/CoreStoreObject.swift +++ b/Sources/CoreStoreObject.swift @@ -116,6 +116,27 @@ open /*abstract*/ class CoreStoreObject: DynamicObject, Hashable { internal let rawObject: CoreStoreManagedObject? internal let isMeta: Bool + + internal class func metaProperties(includeSuperclasses: Bool) -> [PropertyProtocol] { + + func keyPaths(_ allKeyPaths: inout [PropertyProtocol], for dynamicType: CoreStoreObject.Type) { + + allKeyPaths.append(contentsOf: dynamicType.meta.propertyProtocolsByName()) + guard + includeSuperclasses, + case let superType as CoreStoreObject.Type = (dynamicType as AnyClass).superclass(), + superType != CoreStoreObject.self + else { + + return + } + keyPaths(&allKeyPaths, for: superType) + } + + var allKeyPaths: [PropertyProtocol] = [] + keyPaths(&allKeyPaths, for: self) + return allKeyPaths + } // MARK: Private @@ -144,6 +165,22 @@ open /*abstract*/ class CoreStoreObject: DynamicObject, Hashable { } } } + + private func propertyProtocolsByName() -> [PropertyProtocol] { + + Internals.assert(self.isMeta, "'propertyProtocolsByName()' accessed from non-meta instance of \(Internals.typeName(self))") + + let cacheKey = ObjectIdentifier(Self.self) + if let properties = Static.propertiesCache[cacheKey] { + + return properties + } + let values: [PropertyProtocol] = Mirror(reflecting: self) + .children + .compactMap({ $0.value as? PropertyProtocol }) + Static.propertiesCache[cacheKey] = values + return values + } } @@ -187,4 +224,5 @@ fileprivate enum Static { // MARK: FilePrivate fileprivate static var metaCache: [ObjectIdentifier: Any] = [:] + fileprivate static var propertiesCache: [ObjectIdentifier: [PropertyProtocol]] = [:] } diff --git a/Sources/CoreStoreSchema.swift b/Sources/CoreStoreSchema.swift index e2def5d..91905e5 100644 --- a/Sources/CoreStoreSchema.swift +++ b/Sources/CoreStoreSchema.swift @@ -283,9 +283,9 @@ public final class CoreStoreSchema: DynamicSchema { func createProperties(for type: CoreStoreObject.Type) -> [NSPropertyDescription] { var propertyDescriptions: [NSPropertyDescription] = [] - for child in Mirror(reflecting: type.meta).children { + for property in type.metaProperties(includeSuperclasses: false) { - switch child.value { + switch property { case let attribute as AttributeProtocol: Internals.assert( @@ -378,9 +378,10 @@ public final class CoreStoreSchema: DynamicSchema { for (entity, entityDescription) in entityDescriptionsByEntity { let relationshipsByName = relationshipsByNameByEntity[entity]! - for child in Mirror(reflecting: (entity.type as! CoreStoreObject.Type).meta).children { + let entityType = entity.type as! CoreStoreObject.Type + for property in entityType.metaProperties(includeSuperclasses: false) { - switch child.value { + switch property { case let relationship as RelationshipProtocol: let (destinationType, destinationKeyPath) = relationship.inverse diff --git a/Sources/DataStack+DataSources.swift b/Sources/DataStack+DataSources.swift index 3b0c24d..34ce792 100644 --- a/Sources/DataStack+DataSources.swift +++ b/Sources/DataStack+DataSources.swift @@ -31,7 +31,7 @@ import CoreData // MARK: - DataStack -@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) extension DataStack { public func liveList(_ from: From, _ fetchClauses: FetchClause...) -> LiveList { diff --git a/Sources/DiffableDataSource.TableView.swift b/Sources/DiffableDataSource.TableView.swift index 2ff6cd9..9be4987 100644 --- a/Sources/DiffableDataSource.TableView.swift +++ b/Sources/DiffableDataSource.TableView.swift @@ -48,7 +48,7 @@ import CoreData // self.tableView = tableView // self.cellProvider = cellProvider // -// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) { +// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) { // // self.rawDataSource = UITableViewDiffableDataSource( // tableView: tableView, @@ -71,7 +71,7 @@ import CoreData // public func apply(_ snapshot: ListSnapshot, animatingDifferences: Bool = true) { // // let dataSource = UITableViewDiffableDataSource. -// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) { +// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) { // // self.rawDataSource! as! UITableViewDiffableDataSource // @@ -95,7 +95,7 @@ import CoreData // private let cellProvider: (UITableView, IndexPath, ObjectType) -> UITableViewCell? // private let rawDataSource: Any? // -// @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) +// @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) // private var diffableDataSource: UITableViewDiffableDataSource { // // return self.rawDataSource! as! UITableViewDiffableDataSource diff --git a/Sources/DiffableDataSourceSnapshotProtocol.swift b/Sources/DiffableDataSourceSnapshotProtocol.swift new file mode 100644 index 0000000..46872bc --- /dev/null +++ b/Sources/DiffableDataSourceSnapshotProtocol.swift @@ -0,0 +1,34 @@ +// +// DiffableDataSourceSnapshotProtocol.swift +// CoreStore +// +// Copyright © 2018 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation +import CoreData + + +// MARK: - DiffableDataSourceSnapshotProtocol + +internal protocol DiffableDataSourceSnapshotProtocol { + +} diff --git a/Sources/DynamicObject.swift b/Sources/DynamicObject.swift index d5054aa..b0eebc4 100644 --- a/Sources/DynamicObject.swift +++ b/Sources/DynamicObject.swift @@ -74,10 +74,10 @@ extension DynamicObject { // MARK: Internal - internal static func keyPathBuilder() -> DynamicObjectMeta { - - return .init(keyPathString: "SELF") - } +// internal static func keyPathBuilder() -> DynamicObjectMeta { +// +// return .init(keyPathString: "SELF") +// } internal func runtimeType() -> Self.Type { diff --git a/Sources/EnvironmentKeys.swift b/Sources/EnvironmentKeys.swift index 2472e96..882e012 100644 --- a/Sources/EnvironmentKeys.swift +++ b/Sources/EnvironmentKeys.swift @@ -2,8 +2,25 @@ // EnvironmentKeys.swift // CoreStore // -// Created by John Rommel Estropia on 2019/10/05. -// Copyright © 2019 John Rommel Estropia. All rights reserved. +// Copyright © 2018 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. // #if canImport(SwiftUI) @@ -13,7 +30,7 @@ import SwiftUI // MARK: - DataStackEnvironmentKey -@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) public struct DataStackEnvironmentKey: EnvironmentKey { // MARK: Public @@ -27,7 +44,7 @@ public struct DataStackEnvironmentKey: EnvironmentKey { // MARK: - EnvironmentValues -@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) extension EnvironmentValues { // MARK: Public diff --git a/Sources/Internals.DiffableDataSourceSnapshot.swift b/Sources/Internals.DiffableDataSourceSnapshot.swift index f5afcf9..deb0a74 100644 --- a/Sources/Internals.DiffableDataSourceSnapshot.swift +++ b/Sources/Internals.DiffableDataSourceSnapshot.swift @@ -48,18 +48,14 @@ extension Internals { // MARK: Internal - internal let nextStateTag: UUID - init() { self.structure = .init() - self.nextStateTag = .init() } - init(sections: [NSFetchedResultsSectionInfo], previousStateTag: UUID, nextStateTag: UUID) { + init(sections: [NSFetchedResultsSectionInfo]) { - self.structure = .init(sections: sections, previousStateTag: previousStateTag) - self.nextStateTag = nextStateTag + self.structure = .init(sections: sections) } var numberOfItems: Int { @@ -77,21 +73,11 @@ extension Internals { return self.structure.allSectionIDs } - var allSectionStateIDs: [SectionStateID] { - - return self.structure.allSectionStateIDs - } - var allItemIDs: [NSManagedObjectID] { return self.structure.allItemIDs } - var allItemStateIDs: [ItemStateID] { - - return self.structure.allItemStateIDs - } - func numberOfItems(inSection identifier: String) -> Int { return self.itemIDs(inSection: identifier).count @@ -102,21 +88,11 @@ extension Internals { return self.structure.items(in: identifier) } - func itemStateIDs(inSection identifier: String) -> [ItemStateID] { - - return self.structure.itemStateIDs(in: identifier) - } - func sectionIDs(containingItem identifier: NSManagedObjectID) -> String? { return self.structure.section(containing: identifier) } - func sectionStateIDs(containingItem identifier: NSManagedObjectID) -> SectionStateID? { - - return self.structure.sectionStateID(containing: identifier) - } - func indexOfItemID(_ identifier: NSManagedObjectID) -> Int? { return self.structure.allItemIDs.firstIndex(of: identifier) @@ -127,24 +103,19 @@ extension Internals { return self.structure.allSectionIDs.firstIndex(of: identifier) } - func itemIDs(where stateCondition: @escaping (UUID) -> Bool) -> [NSManagedObjectID] { + mutating func appendItems(_ identifiers: [NSManagedObjectID], toSection sectionIdentifier: String?) { - return self.structure.itemsIDs(where: stateCondition) + self.structure.append(itemIDs: identifiers, to: sectionIdentifier) } - mutating func appendItems(_ identifiers: [NSManagedObjectID], toSection sectionIdentifier: String?, nextStateTag: UUID) { + mutating func insertItems(_ identifiers: [NSManagedObjectID], beforeItem beforeIdentifier: NSManagedObjectID) { - self.structure.append(itemIDs: identifiers, to: sectionIdentifier, nextStateTag: nextStateTag) + self.structure.insert(itemIDs: identifiers, before: beforeIdentifier) } - mutating func insertItems(_ identifiers: [NSManagedObjectID], beforeItem beforeIdentifier: NSManagedObjectID, nextStateTag: UUID) { + mutating func insertItems(_ identifiers: [NSManagedObjectID], afterItem afterIdentifier: NSManagedObjectID) { - self.structure.insert(itemIDs: identifiers, before: beforeIdentifier, nextStateTag: nextStateTag) - } - - mutating func insertItems(_ identifiers: [NSManagedObjectID], afterItem afterIdentifier: NSManagedObjectID, nextStateTag: UUID) { - - self.structure.insert(itemIDs: identifiers, after: afterIdentifier, nextStateTag: nextStateTag) + self.structure.insert(itemIDs: identifiers, after: afterIdentifier) } mutating func deleteItems(_ identifiers: [NSManagedObjectID]) { @@ -172,19 +143,19 @@ extension Internals { self.structure.update(itemIDs: identifiers, nextStateTag: nextStateTag) } - mutating func appendSections(_ identifiers: [String], nextStateTag: UUID) { + mutating func appendSections(_ identifiers: [String]) { - self.structure.append(sectionIDs: identifiers, nextStateTag: nextStateTag) + self.structure.append(sectionIDs: identifiers) } - mutating func insertSections(_ identifiers: [String], beforeSection toIdentifier: String, nextStateTag: UUID) { + mutating func insertSections(_ identifiers: [String], beforeSection toIdentifier: String) { - self.structure.insert(sectionIDs: identifiers, before: toIdentifier, nextStateTag: nextStateTag) + self.structure.insert(sectionIDs: identifiers, before: toIdentifier) } - mutating func insertSections(_ identifiers: [String], afterSection toIdentifier: String, nextStateTag: UUID) { + mutating func insertSections(_ identifiers: [String], afterSection toIdentifier: String) { - self.structure.insert(sectionIDs: identifiers, after: toIdentifier, nextStateTag: nextStateTag) + self.structure.insert(sectionIDs: identifiers, after: toIdentifier) } mutating func deleteSections(_ identifiers: [String]) { @@ -271,7 +242,7 @@ extension Internals { self.sections = [] } - init(sections: [NSFetchedResultsSectionInfo], previousStateTag: UUID) { + init(sections: [NSFetchedResultsSectionInfo]) { self.sections = sections.map { @@ -279,8 +250,7 @@ extension Internals { id: $0.name, items: $0.objects? .compactMap({ ($0 as? NSManagedObject)?.objectID }) - .map({ Item(id: $0, stateTag: previousStateTag) }) ?? [], - stateTag: previousStateTag + .map({ Item(id: $0) }) ?? [] ) } } @@ -290,21 +260,11 @@ extension Internals { return self.sections.map({ $0.id }) } - var allSectionStateIDs: [SectionStateID] { - - return self.sections.map({ $0.stateID }) - } - var allItemIDs: [NSManagedObjectID] { return self.sections.lazy.flatMap({ $0.elements }).map({ $0.id }) } - var allItemStateIDs: [ItemStateID] { - - return self.sections.lazy.flatMap({ $0.elements }).map({ $0.stateID }) - } - func items(in sectionID: String) -> [NSManagedObjectID] { guard let sectionIndex = self.sectionIndex(of: sectionID) else { @@ -314,33 +274,12 @@ extension Internals { return self.sections[sectionIndex].elements.map({ $0.id }) } - func itemsIDs(where stateCondition: @escaping (UUID) -> Bool) -> [NSManagedObjectID] { - - return self.sections.lazy - .flatMap({ $0.elements.filter({ stateCondition($0.stateTag) }) }) - .map({ $0.id }) - } - - func itemStateIDs(in sectionID: String) -> [ItemStateID] { - - guard let sectionIndex = self.sectionIndex(of: sectionID) else { - - Internals.abort("Section \"\(sectionID)\" does not exist") - } - return self.sections[sectionIndex].elements.map({ $0.stateID }) - } - func section(containing itemID: NSManagedObjectID) -> String? { return self.itemPositionMap()[itemID]?.section.id } - func sectionStateID(containing itemID: NSManagedObjectID) -> SectionStateID? { - - return self.itemPositionMap()[itemID]?.section.stateID - } - - mutating func append(itemIDs: [NSManagedObjectID], to sectionID: String?, nextStateTag: UUID) { + mutating func append(itemIDs: [NSManagedObjectID], to sectionID: String?) { let index: Array
.Index if let sectionID = sectionID { @@ -360,22 +299,22 @@ extension Internals { } index = section.index(before: section.endIndex) } - let items = itemIDs.lazy.map({ Item(id: $0, stateTag: nextStateTag) }) + let items = itemIDs.lazy.map({ Item(id: $0) }) self.sections[index].elements.append(contentsOf: items) } - mutating func insert(itemIDs: [NSManagedObjectID], before beforeItemID: NSManagedObjectID, nextStateTag: UUID) { + mutating func insert(itemIDs: [NSManagedObjectID], before beforeItemID: NSManagedObjectID) { guard let itemPosition = self.itemPositionMap()[beforeItemID] else { Internals.abort("Item \(beforeItemID) does not exist") } - let items = itemIDs.lazy.map({ Item(id: $0, stateTag: nextStateTag) }) + let items = itemIDs.lazy.map({ Item(id: $0) }) self.sections[itemPosition.sectionIndex].elements .insert(contentsOf: items, at: itemPosition.itemRelativeIndex) } - mutating func insert(itemIDs: [NSManagedObjectID], after afterItemID: NSManagedObjectID, nextStateTag: UUID) { + mutating func insert(itemIDs: [NSManagedObjectID], after afterItemID: NSManagedObjectID) { guard let itemPosition = self.itemPositionMap()[afterItemID] else { @@ -383,7 +322,7 @@ extension Internals { } let itemIndex = self.sections[itemPosition.sectionIndex].elements .index(after: itemPosition.itemRelativeIndex) - let items = itemIDs.lazy.map({ Item(id: $0, stateTag: nextStateTag) }) + let items = itemIDs.lazy.map({ Item(id: $0) }) self.sections[itemPosition.sectionIndex].elements .insert(contentsOf: items, at: itemIndex) } @@ -459,34 +398,34 @@ extension Internals { continue } self.sections[itemPosition.sectionIndex] - .elements[itemPosition.itemRelativeIndex].stateTag = nextStateTag + .elements[itemPosition.itemRelativeIndex].isReloaded = true } } - mutating func append(sectionIDs: [String], nextStateTag: UUID) { + mutating func append(sectionIDs: [String]) { - let newSections = sectionIDs.lazy.map({ Section(id: $0, stateTag: nextStateTag) }) + let newSections = sectionIDs.lazy.map({ Section(id: $0) }) self.sections.append(contentsOf: newSections) } - mutating func insert(sectionIDs: [String], before beforeSectionID: String, nextStateTag: UUID) { + mutating func insert(sectionIDs: [String], before beforeSectionID: String) { guard let sectionIndex = self.sectionIndex(of: beforeSectionID) else { Internals.abort("Section \"\(beforeSectionID)\" does not exist") } - let newSections = sectionIDs.lazy.map({ Section(id: $0, stateTag: nextStateTag) }) + let newSections = sectionIDs.lazy.map({ Section(id: $0) }) self.sections.insert(contentsOf: newSections, at: sectionIndex) } - mutating func insert(sectionIDs: [String], after afterSectionID: String, nextStateTag: UUID) { + mutating func insert(sectionIDs: [String], after afterSectionID: String) { guard let beforeIndex = self.sectionIndex(of: afterSectionID) else { Internals.abort("Section \"\(afterSectionID)\" does not exist") } let sectionIndex = self.sections.index(after: beforeIndex) - let newSections = sectionIDs.lazy.map({ Section(id: $0, stateTag: nextStateTag) }) + let newSections = sectionIDs.lazy.map({ Section(id: $0) }) self.sections.insert(contentsOf: newSections, at: sectionIndex) } @@ -533,7 +472,7 @@ extension Internals { continue } - self.sections[sectionIndex].stateTag = nextStateTag + self.sections[sectionIndex].isReloaded = true } } @@ -589,22 +528,17 @@ extension Internals { fileprivate struct Item: Identifiable, Equatable { - var stateTag: UUID + var isReloaded: Bool - init(id: NSManagedObjectID, stateTag: UUID) { + init(id: NSManagedObjectID, isReloaded: Bool = false) { self.id = id - self.stateTag = stateTag - } - - var stateID: ItemStateID { - - return .init(id: self.id, stateTag: self.stateTag) + self.isReloaded = isReloaded } func isContentEqual(to source: Item) -> Bool { - return self.id == source.id && self.stateTag == source.stateTag + return !self.isReloaded && self.id == source.id } // MARK: Identifiable @@ -618,27 +552,22 @@ extension Internals { fileprivate struct Section: Identifiable, Equatable { var elements: [Item] = [] - var stateTag: UUID + var isReloaded: Bool - init(id: String, items: [Item] = [], stateTag: UUID) { + init(id: String, items: [Item] = [], isReloaded: Bool = false) { self.id = id self.elements = items - self.stateTag = stateTag + self.isReloaded = isReloaded } init(source: Section, elements: S) where S.Element == Item { - self.init(id: source.id, items: Array(elements), stateTag: source.stateTag) - } - - var stateID: SectionStateID { - - return .init(id: self.id, stateTag: self.stateTag) + self.init(id: source.id, items: Array(elements), isReloaded: source.isReloaded) } func isContentEqual(to source: Section) -> Bool { - return self.id == source.id && self.stateTag == source.stateTag + return !self.isReloaded && self.id == source.id } // MARK: Identifiable diff --git a/Sources/Internals.FetchedDiffableDataSourceSnapshotDelegate.swift b/Sources/Internals.FetchedDiffableDataSourceSnapshotDelegate.swift index a9185f8..a49e902 100644 --- a/Sources/Internals.FetchedDiffableDataSourceSnapshotDelegate.swift +++ b/Sources/Internals.FetchedDiffableDataSourceSnapshotDelegate.swift @@ -77,7 +77,7 @@ extension Internals { // #if canImport(UIKit) || canImport(AppKit) // -// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) { +// if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) { // // return // } @@ -96,7 +96,7 @@ extension Internals { // #if canImport(UIKit) || canImport(AppKit) // -// @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) +// @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) // @objc // dynamic func controller(_ controller: NSFetchedResultsController, didChangeContentWith snapshot: NSDiffableDataSourceSnapshotReference) { // @@ -111,48 +111,12 @@ extension Internals { @objc dynamic func controllerDidChangeContent(_ controller: NSFetchedResultsController) { - let nextStateTag: UUID = .init() - var dataSourceSnapshot = Internals.DiffableDataSourceSnapshot( - sections: controller.sections ?? [], - previousStateTag: self.previousStateTag, - nextStateTag: nextStateTag - ) - dataSourceSnapshot.reloadItems(self.reloadedItemIDs, nextStateTag: nextStateTag) - dataSourceSnapshot.reloadSections(self.reloadedSectionIDs, nextStateTag: nextStateTag) - self.handler?.controller( controller, - didChangContentWith: dataSourceSnapshot + didChangContentWith: Internals.DiffableDataSourceSnapshot( + sections: controller.sections ?? [] + ) ) - self.previousStateTag = nextStateTag } - - @objc - dynamic func controller(_ controller: NSFetchedResultsController, didChange anObject: Any, at indexPath: IndexPath?, for type: NSFetchedResultsChangeType, newIndexPath: IndexPath?) { - - let managedObject = anObject as! NSManagedObject - self.reloadedItemIDs.insert(managedObject.objectID) - } - - @objc - dynamic func controller(_ controller: NSFetchedResultsController, didChange sectionInfo: NSFetchedResultsSectionInfo, atSectionIndex sectionIndex: Int, for type: NSFetchedResultsChangeType) { - - self.reloadedSectionIDs.insert(sectionInfo.name) - } - - - // MARK: Private - - private var previousStateTag: UUID = .init() { - - didSet { - - self.reloadedItemIDs = [] - self.reloadedSectionIDs = [] - } - } - - private var reloadedItemIDs: Set = [] - private var reloadedSectionIDs: Set = [] } } diff --git a/Sources/ListSnapshot.swift b/Sources/ListSnapshot.swift index 5509a4b..51b985d 100644 --- a/Sources/ListSnapshot.swift +++ b/Sources/ListSnapshot.swift @@ -128,17 +128,17 @@ public struct ListSnapshot: SnapshotResult, RandomAccessCollec public mutating func appendItems(_ identifiers: [ItemID], toSection sectionIdentifier: SectionID? = nil) { - self.diffableSnapshot.appendItems(identifiers, toSection: sectionIdentifier, nextStateTag: .init()) + self.diffableSnapshot.appendItems(identifiers, toSection: sectionIdentifier) } public mutating func insertItems(_ identifiers: [ItemID], beforeItem beforeIdentifier: ItemID) { - self.diffableSnapshot.insertItems(identifiers, beforeItem: beforeIdentifier, nextStateTag: .init()) + self.diffableSnapshot.insertItems(identifiers, beforeItem: beforeIdentifier) } public mutating func insertItems(_ identifiers: [ItemID], afterItem afterIdentifier: ItemID) { - self.diffableSnapshot.insertItems(identifiers, afterItem: afterIdentifier, nextStateTag: .init()) + self.diffableSnapshot.insertItems(identifiers, afterItem: afterIdentifier) } public mutating func deleteItems(_ identifiers: [ItemID]) { @@ -168,17 +168,17 @@ public struct ListSnapshot: SnapshotResult, RandomAccessCollec public mutating func appendSections(_ identifiers: [SectionID]) { - self.diffableSnapshot.appendSections(identifiers, nextStateTag: .init()) + self.diffableSnapshot.appendSections(identifiers) } public mutating func insertSections(_ identifiers: [SectionID], beforeSection toIdentifier: SectionID) { - self.diffableSnapshot.insertSections(identifiers, beforeSection: toIdentifier, nextStateTag: .init()) + self.diffableSnapshot.insertSections(identifiers, beforeSection: toIdentifier) } public mutating func insertSections(_ identifiers: [SectionID], afterSection toIdentifier: SectionID) { - self.diffableSnapshot.insertSections(identifiers, afterSection: toIdentifier, nextStateTag: .init()) + self.diffableSnapshot.insertSections(identifiers, afterSection: toIdentifier) } public mutating func deleteSections(_ identifiers: [SectionID]) { @@ -263,16 +263,6 @@ public struct ListSnapshot: SnapshotResult, RandomAccessCollec self.diffableSnapshot = diffableSnapshot self.context = context } - - internal var nextStateTag: UUID { - - return self.diffableSnapshot.nextStateTag - } - - internal func itemIDs(where stateCondition: @escaping (UUID) -> Bool) -> [ItemID] { - - return self.diffableSnapshot.itemIDs(where: stateCondition) - } // MARK: Private diff --git a/Sources/LiveList.swift b/Sources/LiveList.swift index 1029a64..da455aa 100644 --- a/Sources/LiveList.swift +++ b/Sources/LiveList.swift @@ -49,10 +49,7 @@ public final class LiveList: Hashable { willSet { - if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) { - - self.willChange() - } + self.willChange() } } @@ -267,7 +264,7 @@ public final class LiveList: Hashable { applyFetchClauses: applyFetchClauses ) - if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) { + if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) { #if canImport(Combine) self.rawObjectWillChange = ObservableObjectPublisher() @@ -318,20 +315,33 @@ extension LiveList: FetchedDiffableDataSourceSnapshotHandler { #if canImport(Combine) import Combine +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) +extension LiveList: ObservableObject {} + +#endif + // MARK: - LiveList: LiveResult -@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) extension LiveList: LiveResult { // MARK: ObservableObject + #if canImport(Combine) + + @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) public var objectWillChange: ObservableObjectPublisher { return self.rawObjectWillChange! as! ObservableObjectPublisher } + #endif + public func willChange() { + guard #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) else { + + return + } #if canImport(Combine) #if canImport(SwiftUI) @@ -340,11 +350,10 @@ extension LiveList: LiveResult { self.objectWillChange.send() } - #else - self.objectWillChange.send() - #endif + self.objectWillChange.send() + #endif } @@ -353,5 +362,3 @@ extension LiveList: LiveResult { // TODO: } } - -#endif diff --git a/Sources/LiveObject.swift b/Sources/LiveObject.swift index f11be0d..2e6c937 100644 --- a/Sources/LiveObject.swift +++ b/Sources/LiveObject.swift @@ -100,7 +100,7 @@ public final class LiveObject: Identifiable, Hashable { self.id = id self.context = context - if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) { + if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) { #if canImport(Combine) self.rawObjectWillChange = ObservableObjectPublisher() @@ -132,10 +132,7 @@ public final class LiveObject: Identifiable, Hashable { return } self.$lazySnapshot.reset({ initializer(id, context) }) - if #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) { - - self.willChange() - } + self.willChange() } ) @@ -156,20 +153,33 @@ public final class LiveObject: Identifiable, Hashable { #if canImport(Combine) import Combine +@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) +extension LiveObject: ObservableObject {} + +#endif + // MARK: - LiveObject: LiveResult -@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) extension LiveObject: LiveResult { // MARK: ObservableObject + #if canImport(Combine) + + @available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) public var objectWillChange: ObservableObjectPublisher { return self.rawObjectWillChange! as! ObservableObjectPublisher } + + #endif public func willChange() { + guard #available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) else { + + return + } #if canImport(Combine) #if canImport(SwiftUI) @@ -178,11 +188,10 @@ extension LiveObject: LiveResult { self.objectWillChange.send() } - #else - self.objectWillChange.send() - #endif + self.objectWillChange.send() + #endif } @@ -192,8 +201,6 @@ extension LiveObject: LiveResult { } } -#endif - // MARK: - LiveObject where O: NSManagedObject diff --git a/Sources/LiveQuery.swift b/Sources/LiveQuery.swift index c270229..aefa1ce 100644 --- a/Sources/LiveQuery.swift +++ b/Sources/LiveQuery.swift @@ -31,7 +31,7 @@ // // //#warning("TODO: autoupdating doesn't work yet") -//@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) +//@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) //@propertyWrapper //public struct LiveQuery: DynamicProperty { // @@ -143,7 +143,7 @@ // //#if canImport(UIKit) || canImport(AppKit) // -//@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) +//@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 10.15, *) //extension LiveQuery { // // public init(liveList: LiveList) where Result == LiveList { diff --git a/Sources/LiveResult.swift b/Sources/LiveResult.swift index e246116..e2c012f 100644 --- a/Sources/LiveResult.swift +++ b/Sources/LiveResult.swift @@ -36,8 +36,7 @@ import AppKit // MARK: - LiveResult -@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) -public protocol LiveResult: ObservableObject { +public protocol LiveResult: AnyObject { associatedtype ObjectType associatedtype SnapshotType: SnapshotResult where SnapshotType.ObjectType == Self.ObjectType diff --git a/Sources/ObjectRepresentation.swift b/Sources/ObjectRepresentation.swift new file mode 100644 index 0000000..d68b8e2 --- /dev/null +++ b/Sources/ObjectRepresentation.swift @@ -0,0 +1,9 @@ +// +// ObjectRepresentation.swift +// CoreStore +// +// Created by John Estropia on 2019/10/11. +// Copyright © 2019 John Rommel Estropia. All rights reserved. +// + +import Foundation diff --git a/Sources/PropertyProtocol.swift b/Sources/PropertyProtocol.swift new file mode 100644 index 0000000..1c6292a --- /dev/null +++ b/Sources/PropertyProtocol.swift @@ -0,0 +1,35 @@ +// +// PropertyProtocol.swift +// CoreStore +// +// Copyright © 2018 John Rommel Estropia +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to deal +// in the Software without restriction, including without limitation the rights +// to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +// copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in all +// copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +// OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +// SOFTWARE. +// + +import Foundation +import CoreData + + +// MARK: - PropertyProtocol + +internal protocol PropertyProtocol: AnyObject { + + var keyPath: KeyPathString { get } +} diff --git a/Sources/Relationship.swift b/Sources/Relationship.swift index 6fe5419..4243db4 100644 --- a/Sources/Relationship.swift +++ b/Sources/Relationship.swift @@ -249,11 +249,15 @@ public enum RelationshipContainer { // MARK: RelationshipKeyPathStringConvertible public typealias ReturnValueType = DestinationValueType? + + + // MARK: PropertyProtocol + + internal let keyPath: KeyPathString // MARK: RelationshipProtocol - - internal let keyPath: KeyPathString + internal let isToMany = false internal let isOrdered = false internal let deleteRule: NSDeleteRule @@ -540,11 +544,15 @@ public enum RelationshipContainer { // MARK: RelationshipKeyPathStringConvertible public typealias ReturnValueType = [DestinationValueType] + + + // MARK: PropertyProtocol + + internal let keyPath: KeyPathString // MARK: RelationshipProtocol - - internal let keyPath: KeyPathString + internal let isToMany = true internal let isOptional = true internal let isOrdered = true @@ -837,11 +845,15 @@ public enum RelationshipContainer { // MARK: RelationshipKeyPathStringConvertible public typealias ReturnValueType = Set + + + // MARK: PropertyProtocol + + internal let keyPath: KeyPathString // MARK: RelationshipProtocol - internal let keyPath: KeyPathString internal let isToMany = true internal let isOptional = true internal let isOrdered = false diff --git a/Sources/RelationshipProtocol.swift b/Sources/RelationshipProtocol.swift index 31e4293..b2269a5 100644 --- a/Sources/RelationshipProtocol.swift +++ b/Sources/RelationshipProtocol.swift @@ -29,9 +29,8 @@ import CoreData // MARK: - RelationshipProtocol -internal protocol RelationshipProtocol: AnyObject { - - var keyPath: KeyPathString { get } +internal protocol RelationshipProtocol: PropertyProtocol { + var isToMany: Bool { get } var isOrdered: Bool { get } var deleteRule: NSDeleteRule { get } diff --git a/Sources/SnapshotResult.swift b/Sources/SnapshotResult.swift index e22bfb6..a58422c 100644 --- a/Sources/SnapshotResult.swift +++ b/Sources/SnapshotResult.swift @@ -36,7 +36,6 @@ import AppKit // MARK: - SnapshotResult -@available(iOS 13.0, tvOS 13.0, watchOS 6.0, macOS 15.0, *) public protocol SnapshotResult { associatedtype ObjectType: DynamicObject diff --git a/Sources/Transformable.swift b/Sources/Transformable.swift index 52ac645..f26ed6c 100644 --- a/Sources/Transformable.swift +++ b/Sources/Transformable.swift @@ -209,6 +209,11 @@ public enum TransformableContainer { public typealias ReturnValueType = DestinationValueType + // MARK: PropertyProtocol + + internal let keyPath: KeyPathString + + // MARK: AttributeProtocol internal static var attributeType: NSAttributeType { @@ -216,7 +221,6 @@ public enum TransformableContainer { return .transformableAttributeType } - internal let keyPath: KeyPathString internal let isOptional = false internal let isTransient: Bool internal let allowsExternalBinaryDataStorage: Bool @@ -427,6 +431,11 @@ public enum TransformableContainer { public typealias ReturnValueType = DestinationValueType? + // MARK: PropertyProtocol + + internal let keyPath: KeyPathString + + // MARK: AttributeProtocol internal static var attributeType: NSAttributeType { @@ -434,7 +443,6 @@ public enum TransformableContainer { return .transformableAttributeType } - internal let keyPath: KeyPathString internal let isOptional = true internal let isTransient: Bool internal let allowsExternalBinaryDataStorage: Bool diff --git a/Sources/Value.swift b/Sources/Value.swift index f4c2d28..fec2e9a 100644 --- a/Sources/Value.swift +++ b/Sources/Value.swift @@ -202,6 +202,11 @@ public enum ValueContainer { // MARK: AttributeKeyPathStringConvertible public typealias ReturnValueType = DestinationValueType + + + // MARK: PropertyProtocol + + internal let keyPath: KeyPathString // MARK: AttributeProtocol @@ -210,8 +215,7 @@ public enum ValueContainer { return V.cs_rawAttributeType } - - internal let keyPath: KeyPathString + internal let isOptional = false internal let isTransient: Bool internal let allowsExternalBinaryDataStorage = false @@ -420,6 +424,11 @@ public enum ValueContainer { // MARK: AttributeKeyPathStringConvertible public typealias ReturnValueType = DestinationValueType? + + + // MARK: PropertyProtocol + + internal let keyPath: KeyPathString // MARK: AttributeProtocol @@ -429,7 +438,6 @@ public enum ValueContainer { return V.cs_rawAttributeType } - internal let keyPath: KeyPathString internal let isOptional = true internal let isTransient: Bool internal let allowsExternalBinaryDataStorage = false