Compare commits

..

10 Commits

Author SHA1 Message Date
John Estropia
2559375491 Raise watchOS min version to 7.4 as supported by Xcode 14 unit tests 2022-09-13 09:26:21 +09:00
John Estropia
c923dfc12c fix unit testing for multi-platform module 2022-09-12 16:51:20 +09:00
John Estropia
a5936c1120 added watchOS unit test lane to make cocoapods linter happy 2022-09-12 13:56:47 +09:00
John Estropia
159448b36d update README 2022-09-12 11:57:32 +09:00
John Estropia
7ee91834ab update README 2022-09-12 11:53:06 +09:00
John Estropia
8faf44c166 version bump 2022-09-12 10:04:33 +09:00
John Estropia
7f4cfaf5a0 Allow unit-testing in SPM builds 2022-08-16 11:55:44 +08:00
John Estropia
da4ac192db async API prototypes 2022-07-10 10:39:45 +09:00
John Estropia
e9219682b5 Fix builds for Swift 5.6 and below 2022-07-08 16:49:43 +09:00
John Estropia
6264022ccf make CoreStoreError dynamicc initializer public 2022-07-08 14:07:32 +09:00
22 changed files with 889 additions and 189 deletions

View File

@@ -11,12 +11,25 @@ Pod::Spec.new do |s|
s.ios.deployment_target = "13.0"
s.osx.deployment_target = "10.15"
s.watchos.deployment_target = "6.0"
s.tvos.deployment_target = "13.0"
s.watchos.deployment_target = "7.4"
s.source_files = "Sources", "Sources/**/*.{swift,h,m}"
s.source_files = "Sources", "Sources/**/*.swift"
s.public_header_files = "Sources/**/*.h"
s.frameworks = "Foundation", "CoreData"
s.requires_arc = true
s.pod_target_xcconfig = { 'OTHER_SWIFT_FLAGS[config=Debug]' => '-D DEBUG', 'OTHER_LDFLAGS' => '-weak_framework Combine -weak_framework SwiftUI' }
s.test_spec "CoreStoreTests" do |ts|
ts.source_files = "CoreStoreTests", "CoreStoreTests/**/*.swift", "CoreStoreTests/**/*.xcdatamodeld", "CoreStoreTests/**/*.xcdatamodel"
ts.public_header_files = "CoreStoreTests/**/*.h"
ts.resources = [ "CoreStoreTests/**/*.xcdatamodeld", "CoreStoreTests/**/*.xcdatamodel" ]
ts.preserve_paths = "CoreStoreTests/**/*.xcdatamodeld"
ts.frameworks = "Foundation", "CoreData"
ts.requires_arc = true
ts.ios.deployment_target = "13.0"
ts.osx.deployment_target = "10.15"
ts.tvos.deployment_target = "13.0"
ts.watchos.deployment_target = "7.4"
end
end

View File

@@ -180,6 +180,35 @@
B50EE14323473C96009B8C47 /* CoreStoreObject+DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */; };
B50EE14423473C97009B8C47 /* CoreStoreObject+DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */; };
B50EE14523473C97009B8C47 /* CoreStoreObject+DataSources.swift in Sources */ = {isa = PBXBuildFile; fileRef = B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */; };
B5114DA728CEEE5400EEAE78 /* SectionByTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52557831D02A07400E51965 /* SectionByTests.swift */; };
B5114DA828CEEE5400EEAE78 /* ConvenienceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B596BBAD1DD59FDB001DCDD9 /* ConvenienceTests.swift */; };
B5114DA928CEEE5400EEAE78 /* ListObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5220E0F1D0DA6AB009BC71E /* ListObserverTests.swift */; };
B5114DAA28CEEE5400EEAE78 /* ErrorTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5519A3F1CA1B17B002BEF78 /* ErrorTests.swift */; };
B5114DAB28CEEE5400EEAE78 /* GroupByTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B525577B1D0291FE00E51965 /* GroupByTests.swift */; };
B5114DAC28CEEE5400EEAE78 /* ListPublisherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D8CA7A2346EC550055D7D1 /* ListPublisherTests.swift */; };
B5114DAD28CEEE5400EEAE78 /* WhereTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52557731D02791400E51965 /* WhereTests.swift */; };
B5114DAE28CEEE5400EEAE78 /* MigrationChainTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5DC47C51C93D22900FA3BF3 /* MigrationChainTests.swift */; };
B5114DAF28CEEE5400EEAE78 /* ImportTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5220E0B1D0D0D19009BC71E /* ImportTests.swift */; };
B5114DB028CEEE5400EEAE78 /* DynamicModelTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5D339B31E925C2B00C880DE /* DynamicModelTests.swift */; };
B5114DB128CEEE5400EEAE78 /* IntoTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B525576B1CFAF18F00E51965 /* IntoTests.swift */; };
B5114DB228CEEE5400EEAE78 /* ObjectPublisherTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B581B9312362BB8C002BDB2B /* ObjectPublisherTests.swift */; };
B5114DB328CEEE5400EEAE78 /* SetupTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B58085741CDF7F00004C2EEB /* SetupTests.swift */; };
B5114DB428CEEE5400EEAE78 /* FetchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52557871D02DE8100E51965 /* FetchTests.swift */; };
B5114DB528CEEE5400EEAE78 /* FromTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5489F4F1CF603D5008B4978 /* FromTests.swift */; };
B5114DB628CEEE5400EEAE78 /* ObjectObserverTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5220E071D0C5F8D009BC71E /* ObjectObserverTests.swift */; };
B5114DB728CEEE5400EEAE78 /* OrderByTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52557771D02826E00E51965 /* OrderByTests.swift */; };
B5114DB828CEEE5400EEAE78 /* TestEntity2.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5489F3E1CF5EEBC008B4978 /* TestEntity2.swift */; };
B5114DB928CEEE5400EEAE78 /* SelectTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B525576F1D02561A00E51965 /* SelectTests.swift */; };
B5114DBA28CEEE5400EEAE78 /* TransactionTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5489F451CF5F017008B4978 /* TransactionTests.swift */; };
B5114DBB28CEEE5400EEAE78 /* TweakTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B525577F1D029D2500E51965 /* TweakTests.swift */; };
B5114DBC28CEEE5400EEAE78 /* VersionLockTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B59A51822256C85E00CEF3C5 /* VersionLockTests.swift */; };
B5114DBD28CEEE5400EEAE78 /* BaseTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5489F4B1CF5F743008B4978 /* BaseTestCase.swift */; };
B5114DBE28CEEE5400EEAE78 /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = B5D372821A39CD6900F583D9 /* Model.xcdatamodeld */; };
B5114DBF28CEEE5400EEAE78 /* BaseTestDataTestCase.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57D27BD1D0BBE8200539C58 /* BaseTestDataTestCase.swift */; };
B5114DC028CEEE5400EEAE78 /* TestEntity1.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5489F3D1CF5EEBC008B4978 /* TestEntity1.swift */; };
B5114DC128CEEE5400EEAE78 /* QueryTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B57D27C11D0BC20100539C58 /* QueryTests.swift */; };
B5114DC228CEEE5400EEAE78 /* StorageInterfaceTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5DC47C91C93D9C800FA3BF3 /* StorageInterfaceTests.swift */; };
B5114DC428CEEE5400EEAE78 /* CoreStore.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 82BA18891C4BBCBA00A0916E /* CoreStore.framework */; };
B512607F1E97A18000402229 /* CoreStoreObject+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B512607E1E97A18000402229 /* CoreStoreObject+Convenience.swift */; };
B51260801E97A18000402229 /* CoreStoreObject+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B512607E1E97A18000402229 /* CoreStoreObject+Convenience.swift */; };
B51260811E97A18000402229 /* CoreStoreObject+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B512607E1E97A18000402229 /* CoreStoreObject+Convenience.swift */; };
@@ -783,6 +812,14 @@
B5F8496D234898240029D57B /* ListSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F8496B234898240029D57B /* ListSnapshot.swift */; };
B5F8496E234898240029D57B /* ListSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F8496B234898240029D57B /* ListSnapshot.swift */; };
B5F8496F234898240029D57B /* ListSnapshot.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F8496B234898240029D57B /* ListSnapshot.swift */; };
B5F9C093287849E0007AAD2E /* DataStack+Concurrency.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F9C092287849E0007AAD2E /* DataStack+Concurrency.swift */; };
B5F9C094287849E0007AAD2E /* DataStack+Concurrency.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F9C092287849E0007AAD2E /* DataStack+Concurrency.swift */; };
B5F9C095287849E0007AAD2E /* DataStack+Concurrency.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F9C092287849E0007AAD2E /* DataStack+Concurrency.swift */; };
B5F9C096287849E0007AAD2E /* DataStack+Concurrency.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F9C092287849E0007AAD2E /* DataStack+Concurrency.swift */; };
B5F9C098287850D6007AAD2E /* MigrationProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F9C097287850D6007AAD2E /* MigrationProgress.swift */; };
B5F9C099287850D6007AAD2E /* MigrationProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F9C097287850D6007AAD2E /* MigrationProgress.swift */; };
B5F9C09A287850D6007AAD2E /* MigrationProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F9C097287850D6007AAD2E /* MigrationProgress.swift */; };
B5F9C09B287850D6007AAD2E /* MigrationProgress.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5F9C097287850D6007AAD2E /* MigrationProgress.swift */; };
B5FAD6A91B50A4B400714891 /* Progress+Convenience.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FAD6A81B50A4B300714891 /* Progress+Convenience.swift */; };
B5FAD6AC1B51285300714891 /* Internals.MigrationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FAD6AB1B51285300714891 /* Internals.MigrationManager.swift */; };
B5FE4DA21C8481E100FA6A91 /* StorageInterface.swift in Sources */ = {isa = PBXBuildFile; fileRef = B5FE4DA11C8481E100FA6A91 /* StorageInterface.swift */; };
@@ -811,6 +848,13 @@
remoteGlobalIDString = 82BA18881C4BBCBA00A0916E;
remoteInfo = "CoreStore tvOS";
};
B5114DCB28CF0C2B00EEAE78 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 2F03A52719C5C6DA005002A5 /* Project object */;
proxyType = 1;
remoteGlobalIDString = B563216E1BD65082006C9394;
remoteInfo = "CoreStore watchOS";
};
B52DD17F1BE1F8CD00949AFE /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 2F03A52719C5C6DA005002A5 /* Project object */;
@@ -871,6 +915,7 @@
B50E17602351FA66004F033C /* Internals.Closure.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Internals.Closure.swift; sourceTree = "<group>"; };
B50E42F623FBB91800ED476E /* ObjectProxy.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ObjectProxy.swift; sourceTree = "<group>"; };
B50EE14123473C92009B8C47 /* CoreStoreObject+DataSources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "CoreStoreObject+DataSources.swift"; sourceTree = "<group>"; };
B5114DC928CEEE5400EEAE78 /* CoreStoreTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CoreStoreTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
B512607E1E97A18000402229 /* CoreStoreObject+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "CoreStoreObject+Convenience.swift"; sourceTree = "<group>"; };
B51260881E9B252B00402229 /* NSEntityDescription+DynamicModel.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "NSEntityDescription+DynamicModel.swift"; sourceTree = "<group>"; };
B51260921E9B28F100402229 /* Internals.EntityIdentifier.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Internals.EntityIdentifier.swift; sourceTree = "<group>"; };
@@ -1061,6 +1106,8 @@
B5F5848628633741001F57ED /* Module.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Module.swift; sourceTree = "<group>"; };
B5F8496B234898240029D57B /* ListSnapshot.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ListSnapshot.swift; sourceTree = "<group>"; };
B5F849702348A6690029D57B /* EnvironmentValues+DataSources.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "EnvironmentValues+DataSources.swift"; sourceTree = "<group>"; };
B5F9C092287849E0007AAD2E /* DataStack+Concurrency.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "DataStack+Concurrency.swift"; sourceTree = "<group>"; };
B5F9C097287850D6007AAD2E /* MigrationProgress.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrationProgress.swift; sourceTree = "<group>"; };
B5FAD6A81B50A4B300714891 /* Progress+Convenience.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = "Progress+Convenience.swift"; sourceTree = "<group>"; };
B5FAD6AB1B51285300714891 /* Internals.MigrationManager.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Internals.MigrationManager.swift; sourceTree = "<group>"; };
B5FE4DA11C8481E100FA6A91 /* StorageInterface.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = StorageInterface.swift; sourceTree = "<group>"; };
@@ -1108,6 +1155,14 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
B5114DC328CEEE5400EEAE78 /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
B5114DC428CEEE5400EEAE78 /* CoreStore.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
B52DD1701BE1F8CC00949AFE /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
@@ -1150,7 +1205,9 @@
2F03A53C19C5C6DA005002A5 /* CoreStoreTests */,
2F03A53119C5C6DA005002A5 /* Products */,
);
indentWidth = 4;
sourceTree = "<group>";
tabWidth = 4;
};
2F03A53119C5C6DA005002A5 /* Products */ = {
isa = PBXGroup;
@@ -1162,6 +1219,7 @@
B52DD17D1BE1F8CC00949AFE /* CoreStoreTests.xctest */,
82BA18891C4BBCBA00A0916E /* CoreStore.framework */,
82BA18921C4BBCBA00A0916E /* CoreStoreTests.xctest */,
B5114DC928CEEE5400EEAE78 /* CoreStoreTests.xctest */,
);
name = Products;
sourceTree = "<group>";
@@ -1430,6 +1488,7 @@
B5D1E22B19FA9FBC003B2874 /* CoreStoreError.swift */,
B549F6721E56A92800FBAB2D /* CoreDataNativeType.swift */,
B5D339F01E94AF5800C880DE /* CoreStoreStrings.swift */,
B5F9C091287849CB007AAD2E /* Swift Concurrency */,
B5C795BE25D933C200BDACC1 /* Reactive Programming */,
B52FEC722596DB6400368BFB /* SwiftUI */,
B5E84EDA1AFF84500064E85B /* Setup */,
@@ -1500,6 +1559,7 @@
B5B866EC25F4800800335476 /* DataStack.AddStoragePublisher.swift */,
B5944EFA25E8E8DA001D1D81 /* ListPublisher.SnapshotPublisher.swift */,
B5B866DF25E9048000335476 /* ObjectPublisher.SnapshotPublisher.swift */,
B5F9C097287850D6007AAD2E /* MigrationProgress.swift */,
);
name = "Reactive Programming";
sourceTree = "<group>";
@@ -1679,6 +1739,14 @@
name = Internals;
sourceTree = "<group>";
};
B5F9C091287849CB007AAD2E /* Swift Concurrency */ = {
isa = PBXGroup;
children = (
B5F9C092287849E0007AAD2E /* DataStack+Concurrency.swift */,
);
name = "Swift Concurrency";
sourceTree = "<group>";
};
B5FE4DA01C84818B00FA6A91 /* StorageInterfaces */ = {
isa = PBXGroup;
children = (
@@ -1799,6 +1867,24 @@
productReference = 82BA18921C4BBCBA00A0916E /* CoreStoreTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
B5114DA328CEEE5400EEAE78 /* CoreStoreTests watchOS */ = {
isa = PBXNativeTarget;
buildConfigurationList = B5114DC628CEEE5400EEAE78 /* Build configuration list for PBXNativeTarget "CoreStoreTests watchOS" */;
buildPhases = (
B5114DA628CEEE5400EEAE78 /* Sources */,
B5114DC328CEEE5400EEAE78 /* Frameworks */,
B5114DC528CEEE5400EEAE78 /* Resources */,
);
buildRules = (
);
dependencies = (
B5114DCC28CF0C2B00EEAE78 /* PBXTargetDependency */,
);
name = "CoreStoreTests watchOS";
productName = "CoreStore tvOSTests";
productReference = B5114DC928CEEE5400EEAE78 /* CoreStoreTests.xctest */;
productType = "com.apple.product-type.bundle.unit-test";
};
B52DD1731BE1F8CC00949AFE /* CoreStore OSX */ = {
isa = PBXNativeTarget;
buildConfigurationList = B52DD18C1BE1F8CD00949AFE /* Build configuration list for PBXNativeTarget "CoreStore OSX" */;
@@ -1910,6 +1996,7 @@
82BA18881C4BBCBA00A0916E /* CoreStore tvOS */,
82BA18911C4BBCBA00A0916E /* CoreStoreTests tvOS */,
B563216E1BD65082006C9394 /* CoreStore watchOS */,
B5114DA328CEEE5400EEAE78 /* CoreStoreTests watchOS */,
B52DD1731BE1F8CC00949AFE /* CoreStore OSX */,
B52DD17C1BE1F8CC00949AFE /* CoreStoreTests OSX */,
);
@@ -1945,6 +2032,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
B5114DC528CEEE5400EEAE78 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
B52DD1721BE1F8CC00949AFE /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
@@ -2052,6 +2146,7 @@
B5F1DA8D1B9AA97D007C5CBB /* ImportableObject.swift in Sources */,
B56965241B356B820075EE4A /* MigrationResult.swift in Sources */,
B5C7958F25D7D18000BDACC1 /* ListState.swift in Sources */,
B5F9C098287850D6007AAD2E /* MigrationProgress.swift in Sources */,
B5FE4DAC1C85D44E00FA6A91 /* SQLiteStore.swift in Sources */,
B5E41EC01EA9BB37006240F0 /* DynamicSchema+Convenience.swift in Sources */,
B5A1DAC81F111BFA003CF369 /* KeyPath+Querying.swift in Sources */,
@@ -2073,6 +2168,7 @@
B50E175723517DE4004F033C /* Differentiable.swift in Sources */,
B59AFF411C6593E400C0ABE2 /* NSPersistentStoreCoordinator+Setup.swift in Sources */,
B5E84F231AFF84860064E85B /* ListMonitor.swift in Sources */,
B5F9C093287849E0007AAD2E /* DataStack+Concurrency.swift in Sources */,
B5BF7FC6234D7E460070E741 /* ObjectSnapshot.swift in Sources */,
B5E84EF71AFF846E0064E85B /* UnsafeDataTransaction.swift in Sources */,
B56964D41B22FFAD0075EE4A /* DataStack+Migration.swift in Sources */,
@@ -2266,6 +2362,7 @@
B56923C51EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */,
82BA18C91C4BBD5900A0916E /* MigrationType.swift in Sources */,
B5D8CA772346EAEE0055D7D1 /* DataStack+DataSources.swift in Sources */,
B5F9C099287850D6007AAD2E /* MigrationProgress.swift in Sources */,
82BA18D01C4BBD7100A0916E /* Internals.MigrationManager.swift in Sources */,
B5DE5231230BDA1300A22534 /* CoreStoreDefaults.swift in Sources */,
B56E4ED523CDB54A00E1708C /* FieldProtocol.swift in Sources */,
@@ -2287,6 +2384,7 @@
82BA18BF1C4BBD5300A0916E /* SectionBy.swift in Sources */,
B5D339ED1E9495E500C880DE /* CoreStoreObject+Querying.swift in Sources */,
B509D7D423C84E1900F42824 /* Transformable.Required.swift in Sources */,
B5F9C094287849E0007AAD2E /* DataStack+Concurrency.swift in Sources */,
82BA18AC1C4BBD3100A0916E /* SynchronousDataTransaction.swift in Sources */,
B50C3EE623D153EA00B29880 /* Field.Coded.swift in Sources */,
82BA18C41C4BBD5300A0916E /* ListMonitor.swift in Sources */,
@@ -2397,6 +2495,41 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
B5114DA628CEEE5400EEAE78 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
B5114DA728CEEE5400EEAE78 /* SectionByTests.swift in Sources */,
B5114DA828CEEE5400EEAE78 /* ConvenienceTests.swift in Sources */,
B5114DA928CEEE5400EEAE78 /* ListObserverTests.swift in Sources */,
B5114DAA28CEEE5400EEAE78 /* ErrorTests.swift in Sources */,
B5114DAB28CEEE5400EEAE78 /* GroupByTests.swift in Sources */,
B5114DAC28CEEE5400EEAE78 /* ListPublisherTests.swift in Sources */,
B5114DAD28CEEE5400EEAE78 /* WhereTests.swift in Sources */,
B5114DAE28CEEE5400EEAE78 /* MigrationChainTests.swift in Sources */,
B5114DAF28CEEE5400EEAE78 /* ImportTests.swift in Sources */,
B5114DB028CEEE5400EEAE78 /* DynamicModelTests.swift in Sources */,
B5114DB128CEEE5400EEAE78 /* IntoTests.swift in Sources */,
B5114DB228CEEE5400EEAE78 /* ObjectPublisherTests.swift in Sources */,
B5114DB328CEEE5400EEAE78 /* SetupTests.swift in Sources */,
B5114DB428CEEE5400EEAE78 /* FetchTests.swift in Sources */,
B5114DB528CEEE5400EEAE78 /* FromTests.swift in Sources */,
B5114DB628CEEE5400EEAE78 /* ObjectObserverTests.swift in Sources */,
B5114DB728CEEE5400EEAE78 /* OrderByTests.swift in Sources */,
B5114DB828CEEE5400EEAE78 /* TestEntity2.swift in Sources */,
B5114DB928CEEE5400EEAE78 /* SelectTests.swift in Sources */,
B5114DBA28CEEE5400EEAE78 /* TransactionTests.swift in Sources */,
B5114DBB28CEEE5400EEAE78 /* TweakTests.swift in Sources */,
B5114DBC28CEEE5400EEAE78 /* VersionLockTests.swift in Sources */,
B5114DBD28CEEE5400EEAE78 /* BaseTestCase.swift in Sources */,
B5114DBE28CEEE5400EEAE78 /* Model.xcdatamodeld in Sources */,
B5114DBF28CEEE5400EEAE78 /* BaseTestDataTestCase.swift in Sources */,
B5114DC028CEEE5400EEAE78 /* TestEntity1.swift in Sources */,
B5114DC128CEEE5400EEAE78 /* QueryTests.swift in Sources */,
B5114DC228CEEE5400EEAE78 /* StorageInterfaceTests.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
B52DD16F1BE1F8CC00949AFE /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
@@ -2480,6 +2613,7 @@
B5D8CA792346EAEF0055D7D1 /* DataStack+DataSources.swift in Sources */,
B56E4ED723CDB54A00E1708C /* FieldProtocol.swift in Sources */,
B56923C71EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */,
B5F9C09B287850D6007AAD2E /* MigrationProgress.swift in Sources */,
B5944EFE25E8E8DA001D1D81 /* ListPublisher.SnapshotPublisher.swift in Sources */,
B5DE5233230BDA1300A22534 /* CoreStoreDefaults.swift in Sources */,
B52DD1A51BE1F92F00949AFE /* ImportableUniqueObject.swift in Sources */,
@@ -2501,6 +2635,7 @@
B52F74321E9B50D0005F3DAC /* SchemaHistory.swift in Sources */,
B509D7D623C84E1900F42824 /* Transformable.Required.swift in Sources */,
B50C3EE823D153EA00B29880 /* Field.Coded.swift in Sources */,
B5F9C096287849E0007AAD2E /* DataStack+Concurrency.swift in Sources */,
B5D339EF1E9495E500C880DE /* CoreStoreObject+Querying.swift in Sources */,
B52DD19F1BE1F92C00949AFE /* SynchronousDataTransaction.swift in Sources */,
B52DD1CB1BE1F94600949AFE /* Internals.WeakObject.swift in Sources */,
@@ -2694,6 +2829,7 @@
B56321931BD65216006C9394 /* DataStack+Querying.swift in Sources */,
B5D8CA782346EAEF0055D7D1 /* DataStack+DataSources.swift in Sources */,
B56923C61EB823B4007C4DC9 /* NSEntityDescription+Migration.swift in Sources */,
B5F9C09A287850D6007AAD2E /* MigrationProgress.swift in Sources */,
B56321A71BD65216006C9394 /* MigrationResult.swift in Sources */,
B56E4ED623CDB54A00E1708C /* FieldProtocol.swift in Sources */,
B5DE5232230BDA1300A22534 /* CoreStoreDefaults.swift in Sources */,
@@ -2715,6 +2851,7 @@
B52F74311E9B50D0005F3DAC /* SchemaHistory.swift in Sources */,
B563218F1BD65216006C9394 /* ImportableObject.swift in Sources */,
B509D7D523C84E1900F42824 /* Transformable.Required.swift in Sources */,
B5F9C095287849E0007AAD2E /* DataStack+Concurrency.swift in Sources */,
B56321991BD65216006C9394 /* OrderBy.swift in Sources */,
B50C3EE723D153EA00B29880 /* Field.Coded.swift in Sources */,
B5D339EE1E9495E500C880DE /* CoreStoreObject+Querying.swift in Sources */,
@@ -2798,6 +2935,11 @@
target = 82BA18881C4BBCBA00A0916E /* CoreStore tvOS */;
targetProxy = 82BA18941C4BBCBA00A0916E /* PBXContainerItemProxy */;
};
B5114DCC28CF0C2B00EEAE78 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = B563216E1BD65082006C9394 /* CoreStore watchOS */;
targetProxy = B5114DCB28CF0C2B00EEAE78 /* PBXContainerItemProxy */;
};
B52DD1801BE1F8CD00949AFE /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = B52DD1731BE1F8CC00949AFE /* CoreStore OSX */;
@@ -2879,7 +3021,7 @@
TVOS_DEPLOYMENT_TARGET = 13.0;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
WATCHOS_DEPLOYMENT_TARGET = 6.0;
WATCHOS_DEPLOYMENT_TARGET = 7.4;
};
name = Debug;
};
@@ -2944,7 +3086,7 @@
VALIDATE_PRODUCT = YES;
VERSIONING_SYSTEM = "apple-generic";
VERSION_INFO_PREFIX = "";
WATCHOS_DEPLOYMENT_TARGET = 6.0;
WATCHOS_DEPLOYMENT_TARGET = 7.4;
};
name = Release;
};
@@ -3117,6 +3259,46 @@
};
name = Release;
};
B5114DC728CEEE5400EEAE78 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = NO;
CLANG_ENABLE_MODULES = YES;
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = CoreStoreTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.johnestropia.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = CoreStoreTests;
SDKROOT = watchos;
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
TARGETED_DEVICE_FAMILY = 4;
WATCHOS_DEPLOYMENT_TARGET = 7.4;
};
name = Debug;
};
B5114DC828CEEE5400EEAE78 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
APPLICATION_EXTENSION_API_ONLY = NO;
CLANG_ENABLE_MODULES = YES;
COPY_PHASE_STRIP = NO;
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = CoreStoreTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/Frameworks @loader_path/Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = "com.johnestropia.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = CoreStoreTests;
SDKROOT = watchos;
SUPPORTS_MACCATALYST = NO;
SUPPORTS_MAC_DESIGNED_FOR_IPHONE_IPAD = NO;
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
TARGETED_DEVICE_FAMILY = 4;
WATCHOS_DEPLOYMENT_TARGET = 7.4;
};
name = Release;
};
B52DD1851BE1F8CD00949AFE /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
@@ -3190,7 +3372,7 @@
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = CoreStoreTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.johnestropia.CoreStore;
PRODUCT_BUNDLE_IDENTIFIER = "com.johnestropia.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = CoreStoreTests;
SDKROOT = macosx;
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
@@ -3211,7 +3393,7 @@
GCC_NO_COMMON_BLOCKS = YES;
INFOPLIST_FILE = CoreStoreTests/Info.plist;
LD_RUNPATH_SEARCH_PATHS = "$(inherited) @executable_path/../Frameworks @loader_path/../Frameworks";
PRODUCT_BUNDLE_IDENTIFIER = com.johnestropia.CoreStore;
PRODUCT_BUNDLE_IDENTIFIER = "com.johnestropia.$(PRODUCT_NAME:rfc1034identifier)";
PRODUCT_NAME = CoreStoreTests;
SDKROOT = macosx;
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
@@ -3242,7 +3424,7 @@
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
TARGETED_DEVICE_FAMILY = 4;
WATCHOS_DEPLOYMENT_TARGET = 6.0;
WATCHOS_DEPLOYMENT_TARGET = 7.4;
};
name = Debug;
};
@@ -3271,7 +3453,7 @@
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_SWIFT3_OBJC_INFERENCE = Off;
TARGETED_DEVICE_FAMILY = 4;
WATCHOS_DEPLOYMENT_TARGET = 6.0;
WATCHOS_DEPLOYMENT_TARGET = 7.4;
};
name = Release;
};
@@ -3323,6 +3505,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B5114DC628CEEE5400EEAE78 /* Build configuration list for PBXNativeTarget "CoreStoreTests watchOS" */ = {
isa = XCConfigurationList;
buildConfigurations = (
B5114DC728CEEE5400EEAE78 /* Debug */,
B5114DC828CEEE5400EEAE78 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
B52DD18C1BE1F8CD00949AFE /* Build configuration list for PBXNativeTarget "CoreStore OSX" */ = {
isa = XCConfigurationList;
buildConfigurations = (

View File

@@ -20,6 +20,20 @@
ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "NO"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B5114DA328CEEE5400EEAE78"
BuildableName = "CoreStoreTests.xctest"
BlueprintName = "CoreStoreTests watchOS"
ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
@@ -28,6 +42,16 @@
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "B5114DA328CEEE5400EEAE78"
BuildableName = "CoreStoreTests.xctest"
BlueprintName = "CoreStoreTests watchOS"
ReferencedContainer = "container:CoreStore.xcodeproj">
</BuildableReference>
</TestableReference>
</Testables>
</TestAction>
<LaunchAction

View File

@@ -1,5 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict/>
<dict>
<key>PreviewsEnabled</key>
<false/>
</dict>
</plist>

View File

@@ -28,6 +28,17 @@ import XCTest
@testable
import CoreStore
#if !SWIFT_PACKAGE
extension Bundle {
static var module: Bundle {
return Bundle(for: BaseTestCase.self)
}
}
#endif
// MARK: - BaseTestCase
@@ -40,7 +51,7 @@ class BaseTestCase: XCTestCase {
let stack = DataStack(
xcodeModelName: "Model",
bundle: Bundle(for: Self.self)
bundle: Bundle.module
)
do {

View File

@@ -84,7 +84,7 @@ final class ErrorTests: XCTestCase {
let schemaHistory = SchemaHistory(
XcodeDataModelSchema.from(
modelName: "Model",
bundle: Bundle(for: Self.self)
bundle: Bundle.module
)
)
let version = "1.0.0"

View File

@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>BNDL</string>
<key>CFBundleShortVersionString</key>
<string>6.2.1</string>
<string>9.0.0</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>

View File

@@ -101,9 +101,9 @@ class ListObserverTests: BaseTestDataTestCase {
["indexPath", "object"]
)
let indexPath = userInfo?["indexPath"] as? NSIndexPath
XCTAssertEqual(indexPath?.index(atPosition: 0), 0)
XCTAssertEqual(indexPath?.index(atPosition: 1), 0)
let indexPath = userInfo?["indexPath"] as? IndexPath
XCTAssertEqual(indexPath?.section, 0)
XCTAssertEqual(indexPath?.item, 0)
let object = userInfo?["object"] as? TestEntity1
XCTAssertEqual(object?.testBoolean, NSNumber(value: true))
@@ -213,14 +213,14 @@ class ListObserverTests: BaseTestDataTestCase {
["indexPath", "object"]
)
let indexPath = userInfo?["indexPath"] as? NSIndexPath
let indexPath = userInfo?["indexPath"] as? IndexPath
let object = userInfo?["object"] as? TestEntity1
switch object?.testEntityID {
case NSNumber(value: 101)?:
XCTAssertEqual(indexPath?.index(atPosition: 0), 1)
XCTAssertEqual(indexPath?.index(atPosition: 1), 0)
XCTAssertEqual(indexPath?.section, 1)
XCTAssertEqual(indexPath?.item, 0)
XCTAssertEqual(object?.testBoolean, NSNumber(value: true))
XCTAssertEqual(object?.testNumber, NSNumber(value: 11))
@@ -230,8 +230,8 @@ class ListObserverTests: BaseTestDataTestCase {
XCTAssertEqual(object?.testDate, self.dateFormatter.date(from: "2000-01-11T00:00:00Z")!)
case NSNumber(value: 102)?:
XCTAssertEqual(indexPath?.index(atPosition: 0), 0)
XCTAssertEqual(indexPath?.index(atPosition: 1), 0)
XCTAssertEqual(indexPath?.section, 0)
XCTAssertEqual(indexPath?.item, 0)
XCTAssertEqual(object?.testBoolean, NSNumber(value: false))
XCTAssertEqual(object?.testNumber, NSNumber(value: 22))
@@ -357,13 +357,13 @@ class ListObserverTests: BaseTestDataTestCase {
["fromIndexPath", "toIndexPath", "object"]
)
let fromIndexPath = userInfo?["fromIndexPath"] as? NSIndexPath
XCTAssertEqual(fromIndexPath?.index(atPosition: 0), 0)
XCTAssertEqual(fromIndexPath?.index(atPosition: 1), 0)
let fromIndexPath = userInfo?["fromIndexPath"] as? IndexPath
XCTAssertEqual(fromIndexPath?.section, 0)
XCTAssertEqual(fromIndexPath?.item, 0)
let toIndexPath = userInfo?["toIndexPath"] as? NSIndexPath
XCTAssertEqual(toIndexPath?.index(atPosition: 0), 1)
XCTAssertEqual(toIndexPath?.index(atPosition: 1), 1)
let toIndexPath = userInfo?["toIndexPath"] as? IndexPath
XCTAssertEqual(toIndexPath?.section, 1)
XCTAssertEqual(toIndexPath?.item, 1)
let object = userInfo?["object"] as? TestEntity1
XCTAssertEqual(object?.testEntityID, NSNumber(value: 102))
@@ -465,10 +465,10 @@ class ListObserverTests: BaseTestDataTestCase {
["indexPath", "object"]
)
let indexPath = userInfo?["indexPath"] as? NSIndexPath
let indexPath = userInfo?["indexPath"] as? IndexPath
XCTAssertEqual(indexPath?.section, 0)
XCTAssert(indexPath?.index(atPosition: 1) == 0 || indexPath?.index(atPosition: 1) == 1)
XCTAssert(indexPath?.item == 0 || indexPath?.item == 1)
let object = userInfo?["object"] as? TestEntity1
XCTAssertEqual(object?.isDeleted, true)

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="14460.32" systemVersion="17G2307" minimumToolsVersion="Xcode 4.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
<entity name="TestEntity1AAA" representedClassName="CoreStoreTests.TestEntity1" syncable="YES">
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="21279" systemVersion="21G83" minimumToolsVersion="Xcode 4.3" sourceLanguage="Objective-C" userDefinedModelVersionIdentifier="">
<entity name="TestEntity1AAA" representedClassName=".TestEntity1" syncable="YES">
<attribute name="testBoolean" optional="YES" attributeType="Boolean" usesScalarValueType="NO" syncable="YES"/>
<attribute name="testData" optional="YES" attributeType="Binary" syncable="YES"/>
<attribute name="testDate" optional="YES" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
@@ -12,7 +12,7 @@
<relationship name="testToManyUnordered" optional="YES" toMany="YES" deletionRule="Nullify" destinationEntity="TestEntity1AAA" inverseName="testToOne" inverseEntity="TestEntity1AAA" syncable="YES"/>
<relationship name="testToOne" optional="YES" maxCount="1" deletionRule="Nullify" destinationEntity="TestEntity1AAA" inverseName="testToManyUnordered" inverseEntity="TestEntity1AAA" syncable="YES"/>
</entity>
<entity name="TestEntity2" representedClassName="CoreStoreTests.TestEntity2" syncable="YES">
<entity name="TestEntity2" representedClassName=".TestEntity2" syncable="YES">
<attribute name="testBoolean" optional="YES" attributeType="Boolean" usesScalarValueType="NO" syncable="YES"/>
<attribute name="testData" optional="YES" attributeType="Binary" syncable="YES"/>
<attribute name="testDate" optional="YES" attributeType="Date" usesScalarValueType="NO" syncable="YES"/>
@@ -30,8 +30,4 @@
<configuration name="Config2">
<memberEntity name="TestEntity2"/>
</configuration>
<elements>
<element name="TestEntity1AAA" positionX="-63" positionY="-18" width="128" height="195"/>
<element name="TestEntity2" positionX="-63" positionY="9" width="128" height="195"/>
</elements>
</model>

View File

@@ -42,7 +42,7 @@ class SetupTests: BaseTestDataTestCase {
let schemaHistory = SchemaHistory(
XcodeDataModelSchema.from(
modelName: "Model",
bundle: Bundle(for: Self.self)
bundle: Bundle.module
)
)
let stack = DataStack(schemaHistory: schemaHistory)
@@ -68,7 +68,7 @@ class SetupTests: BaseTestDataTestCase {
DataStack(
xcodeModelName: "Model",
bundle: Bundle(for: Self.self),
bundle: Bundle.module,
migrationChain: migrationChain
)
}
@@ -82,7 +82,7 @@ class SetupTests: BaseTestDataTestCase {
let stack = DataStack(
xcodeModelName: "Model",
bundle: Bundle(for: Self.self)
bundle: Bundle.module
)
do {
@@ -137,7 +137,7 @@ class SetupTests: BaseTestDataTestCase {
let stack = DataStack(
xcodeModelName: "Model",
bundle: Bundle(for: Self.self)
bundle: Bundle.module
)
do {
@@ -205,7 +205,7 @@ class SetupTests: BaseTestDataTestCase {
let stack = DataStack(
xcodeModelName: "Model",
bundle: Bundle(for: Self.self)
bundle: Bundle.module
)
try! stack.addStorageAndWait(sqliteStore)
self.prepareTestDataForStack(stack)
@@ -224,7 +224,7 @@ class SetupTests: BaseTestDataTestCase {
let metadata = try createStore()
let stack = DataStack(
xcodeModelName: "Model",
bundle: Bundle(for: Self.self)
bundle: Bundle.module
)
try sqliteStore.cs_eraseStorageAndWait(
metadata: metadata,
@@ -257,7 +257,7 @@ class SetupTests: BaseTestDataTestCase {
let stack = DataStack(
xcodeModelName: "Model",
bundle: Bundle(for: Self.self)
bundle: Bundle.module
)
do {
@@ -325,7 +325,7 @@ class SetupTests: BaseTestDataTestCase {
let stack = DataStack(
xcodeModelName: "Model",
bundle: Bundle(for: Self.self)
bundle: Bundle.module
)
try! stack.addStorageAndWait(
SQLiteStore.legacy(
@@ -351,7 +351,7 @@ class SetupTests: BaseTestDataTestCase {
let metadata = try createStore()
let stack = DataStack(
xcodeModelName: "Model",
bundle: Bundle(for: Self.self)
bundle: Bundle.module
)
try sqliteStore.cs_eraseStorageAndWait(
metadata: metadata,

View File

@@ -103,7 +103,7 @@ final class StorageInterfaceTests: XCTestCase {
.appendingPathExtension("db")
let mappingProvider = XcodeSchemaMappingProvider(
from: "V1", to: "V2",
mappingModelBundle: Bundle(for: Self.self)
mappingModelBundle: Bundle.module
)
let store = SQLiteStore(
@@ -131,7 +131,7 @@ final class StorageInterfaceTests: XCTestCase {
let fileName = UUID().uuidString + ".db"
let mappingProvider = XcodeSchemaMappingProvider(
from: "V1", to: "V2",
mappingModelBundle: Bundle(for: Self.self)
mappingModelBundle: Bundle.module
)
let store = SQLiteStore(
fileName: fileName,
@@ -197,7 +197,7 @@ final class StorageInterfaceTests: XCTestCase {
let fileName = UUID().uuidString + ".db"
let mappingProvider = XcodeSchemaMappingProvider(
from: "V1", to: "V2",
mappingModelBundle: Bundle(for: Self.self)
mappingModelBundle: Bundle.module
)
let store = SQLiteStore.legacy(
fileName: fileName,

View File

@@ -23,6 +23,7 @@
// SOFTWARE.
//
import Foundation
import XCTest
@testable
@@ -426,9 +427,9 @@ final class TransactionTests: BaseTestCase {
["indexPath", "object"]
)
let indexPath = userInfo?["indexPath"] as? NSIndexPath
XCTAssertEqual(indexPath?.index(atPosition: 0), 0)
XCTAssertEqual(indexPath?.index(atPosition: 1), 0)
let indexPath = userInfo?["indexPath"] as? IndexPath
XCTAssertEqual(indexPath?.section, 0)
XCTAssertEqual(indexPath?.item, 0)
let object = userInfo?["object"] as? TestEntity1
XCTAssertEqual(object?.testBoolean, NSNumber(value: true))

View File

@@ -242,7 +242,9 @@
B5A3911A24E5429200E7E8BD /* Products */,
B5A3916624E698F900E7E8BD /* Frameworks */,
);
indentWidth = 4;
sourceTree = "<group>";
tabWidth = 4;
};
B5A3911A24E5429200E7E8BD /* Products */ = {
isa = PBXGroup;

View File

@@ -1,4 +1,4 @@
// swift-tools-version:5.5
// swift-tools-version:5.7
//
// Package.swift
// CoreStore
@@ -29,7 +29,7 @@ import PackageDescription
let package = Package(
name: "CoreStore",
platforms: [
.macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v6)
.macOS(.v10_15), .iOS(.v13), .tvOS(.v13), .watchOS(.v7)
],
products: [
.library(name: "CoreStore", targets: ["CoreStore"])
@@ -45,7 +45,10 @@ let package = Package(
.testTarget(
name: "CoreStoreTests",
dependencies: ["CoreStore"],
path: "CoreStoreTests"
path: "CoreStoreTests",
resources: [
.process("Model.xcdatamodeld")
]
)
],
swiftLanguageVersions: [.v5]

View File

@@ -20,8 +20,8 @@ Unleashing the real power of Core Data with the elegance and safety of Swift
<br />
</p>
* **Swift 5.5:** iOS 11+ / macOS 10.13+ / watchOS 4.0+ / tvOS 11.0+
* Previously supported Swift versions: [Swift 5.4](https://github.com/JohnEstropia/CoreStore/tree/8.0.1), [Swift 5.3](https://github.com/JohnEstropia/CoreStore/tree/7.3.1), [Swift 5.1](https://github.com/JohnEstropia/CoreStore/tree/7.0.4), [Swift 5.0](https://github.com/JohnEstropia/CoreStore/tree/6.3.2), [Swift 4.2](https://github.com/JohnEstropia/CoreStore/tree/6.2.1), [Swift 3.2](https://github.com/JohnEstropia/CoreStore/tree/4.2.3)
* **Swift 5.7:** iOS 13+ / macOS 10.15+ / watchOS 7.4+ / tvOS 13.0+
* Previously supported Swift versions: [Swift 5.5](https://github.com/JohnEstropia/CoreStore/tree/8.1.0), [Swift 5.4](https://github.com/JohnEstropia/CoreStore/tree/8.0.1), [Swift 5.3](https://github.com/JohnEstropia/CoreStore/tree/7.3.1)
Upgrading from previous CoreStore versions? Check out the [🆕 features](#features) and make sure to read the [Change logs](https://github.com/JohnEstropia/CoreStore/releases).
@@ -2438,9 +2438,9 @@ var body: some View {
# Installation
- Requires:
- iOS 10 SDK and above
- Swift 5.2 (Xcode 11.4+)
- For previous Swift versions: [Swift 3.2](https://github.com/JohnEstropia/CoreStore/tree/4.2.3), [Swift 4.2](https://github.com/JohnEstropia/CoreStore/tree/6.2.1), [Swift 5.0](https://github.com/JohnEstropia/CoreStore/tree/6.3.2), [Swift 5.1](https://github.com/JohnEstropia/CoreStore/tree/7.0.4)
- iOS 13 SDK and above
- Swift 5.7 (Xcode 14.0+)
- For previous Swift versions: [Swift 5.5](https://github.com/JohnEstropia/CoreStore/tree/8.1.0), [Swift 5.4](https://github.com/JohnEstropia/CoreStore/tree/8.0.1), [Swift 5.3](https://github.com/JohnEstropia/CoreStore/tree/7.3.1)
- Dependencies:
- *None*
- Other notes:
@@ -2449,7 +2449,7 @@ var body: some View {
### Install with CocoaPods
In your `Podfile`, add
```
pod 'CoreStore', '~> 8.0'
pod 'CoreStore', '~> 9.0'
```
and run
```
@@ -2460,7 +2460,7 @@ This installs CoreStore as a framework. Declare `import CoreStore` in your swift
### Install with Carthage
In your `Cartfile`, add
```
github "JohnEstropia/CoreStore" >= 8.0.0
github "JohnEstropia/CoreStore" >= 9.0.0
```
and run
```
@@ -2471,7 +2471,7 @@ This installs CoreStore as a framework. Declare `import CoreStore` in your swift
#### Install with Swift Package Manager:
```swift
dependencies: [
.package(url: "https://github.com/JohnEstropia/CoreStore.git", from: "8.0.1"))
.package(url: "https://github.com/JohnEstropia/CoreStore.git", from: "9.0.0"))
]
```
Declare `import CoreStore` in your swift file to use the library.

View File

@@ -78,6 +78,29 @@ public enum CoreStoreError: Error, CustomNSError, Hashable {
Attempted to perform a fetch but could not find any related persistent store.
*/
case persistentStoreNotFound(entity: DynamicObject.Type)
/**
Casts any `Error` to a known `CoreStoreError`, or wraps it in `CoreStoreError.internalError(NSError:)`.
*/
public init(_ error: Error?) {
guard let error = error else {
self = .unknown
return
}
switch error {
case let error as CoreStoreError:
self = error
case let error as NSError:
self = .internalError(NSError: error)
default:
self = .unknown
}
}
// MARK: CustomNSError
@@ -253,29 +276,6 @@ public enum CoreStoreError: Error, CustomNSError, Hashable {
break
}
}
// MARK: Internal
internal init(_ error: Error?) {
guard let error = error else {
self = .unknown
return
}
switch error {
case let error as CoreStoreError:
self = error
case let error as NSError:
self = .internalError(NSError: error)
default:
self = .unknown
}
}
}

View File

@@ -0,0 +1,367 @@
//
// DataStack+Concurrency.swift
// CoreStore
//
// Copyright © 2021 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: - DataStack
extension DataStack {
// MARK: Public
/**
Swift concurrency utilities for the `DataStack` are exposed through this namespace
*/
public var async: DataStack.AsyncNamespace {
return .init(self)
}
// MARK: - ReactiveNamespace
/**
Swift concurrency for the `DataStack` are exposed through this namespace. Extend this type if you need to add other `async` utilities for `DataStack`.
*/
public struct AsyncNamespace {
// MARK: Public
/**
The `DataStack` instance
*/
public let base: DataStack
// MARK: Internal
internal init(_ base: DataStack) {
self.base = base
}
}
}
// MARK: - DataStack.AsyncNamespace
extension DataStack.AsyncNamespace {
// MARK: Public
/**
Swift concurrency extension for `CoreStore.DataStack`'s `addStorage(...)` API. Asynchronously adds a `StorageInterface` to the stack.
```
let storage = try await dataStack.async.addStorage(
InMemoryStore(configuration: "Config1")
)
```
- parameter storage: the storage
- returns: The `StorageInterface` instance added to the `DataStack`. Note that the `StorageInterface` event value may not always be the same instance as the parameter argument if a previous `StorageInterface` was already added at the same URL and with the same configuration.
- throws: A `CoreStoreError` value indicating the failure reason
*/
public func addStorage<T: StorageInterface>(
_ storage: T
) async throws -> T {
return try await withCheckedThrowingContinuation { continuation in
self.base.addStorage(
storage,
completion: continuation.resume(with:)
)
}
}
/**
Swift concurrency extension for `CoreStore.DataStack`'s `addStorage(...)` API. Asynchronously adds a `LocalStorage` to the stack. Migrations are also initiated by default. The event emits `MigrationProgress` `enum` values.
```
for try await migrationProgress in dataStack.async.addStorage(
SQLiteStore(
fileName: "core_data.sqlite",
configuration: "Config1"
)
) {
print("\(round(migrationProgress.fractionCompleted * 100)) %") // 0.0 ~ 1.0
}
```
- parameter storage: the local storage
- returns: An `AsyncThrowingStream` that emits a `MigrationProgress` value with metadata for migration progress. Note that the `LocalStorage` event value may not always be the same instance as the parameter argument if a previous `LocalStorage` was already added at the same URL and with the same configuration.
- throws: A `CoreStoreError` value indicating the failure reason
*/
public func addStorage<T>(
_ storage: T
) -> AsyncThrowingStream<MigrationProgress<T>, Swift.Error> {
return .init(
bufferingPolicy: .unbounded,
{ continuation in
var progress: Progress? = nil
progress = self.base.addStorage(
storage,
completion: { result in
progress?.setProgressHandler(nil)
switch result {
case .success(let storage):
continuation.yield(
.finished(
storage: storage,
migrationRequired: progress != nil
)
)
continuation.finish()
case .failure(let error):
continuation.finish(
throwing: error
)
}
}
)
if let progress = progress {
progress.setProgressHandler { progress in
continuation.yield(
.migrating(
storage: storage,
progressObject: progress
)
)
}
}
}
)
}
/**
Swift concurrency extension for `CoreStore.DataStack`'s `importObject(...)` API. Creates an `ImportableObject` by importing from the specified import source. The event value will be the object instance correctly associated for the `DataStack`.
```
let object = try await dataStack.async.importObject(
Into<Person>(),
source: ["name": "John"]
)
```
- parameter into: an `Into` clause specifying the entity type
- parameter source: the object to import values from
- returns: The object instance correctly associated for the `DataStack` if the object was imported successfully, or `nil` if the `ImportableObject` ignored the `source`.
- throws: A `CoreStoreError` value indicating the failure reason
*/
public func importObject<O: DynamicObject & ImportableObject>(
_ into: Into<O>,
source: O.ImportSource
) async throws -> O? {
return try await withCheckedThrowingContinuation { continuation in
self.base.perform(
asynchronous: { (transaction) -> O? in
return try transaction.importObject(
into,
source: source
)
},
success: {
continuation.resume(
with: .success($0.flatMap(self.base.fetchExisting))
)
},
failure: continuation.resume(throwing:)
)
}
}
/**
Swift concurrency extension for `CoreStore.DataStack`'s `importObject(...)` API. Updates an existing `ImportableObject` by importing values from the specified import source. The event value will be the object instance correctly associated for the `DataStack`.
```
let importedPerson = try await dataStack.async.importObject(
existingPerson,
source: ["name": "John", "age": 30]
)
```
- parameter object: the object to update
- parameter source: the object to import values from
- returns: The object instance correctly associated for the `DataStack` if the object was imported successfully, or `nil` if the `ImportableObject` ignored the `source`.
- throws: A `CoreStoreError` value indicating the failure reason
*/
public func importObject<O: DynamicObject & ImportableObject>(
_ object: O,
source: O.ImportSource
) async throws -> O? {
return try await withCheckedThrowingContinuation { continuation in
self.base.perform(
asynchronous: { (transaction) -> O? in
guard let object = transaction.edit(object) else {
try transaction.cancel()
}
try transaction.importObject(
object,
source: source
)
return object
},
success: {
continuation.resume(
with: .success($0.flatMap(self.base.fetchExisting))
)
},
failure: continuation.resume(throwing:)
)
}
}
/**
Swift concurrency extension for `CoreStore.DataStack`'s `importUniqueObject(...)` API. Updates an existing `ImportableUniqueObject` or creates a new instance by importing from the specified import source. The event value will be the object instance correctly associated for the `DataStack`.
```
let person = try await dataStack.async.importUniqueObject(
Into<Person>(),
source: ["name": "John", "age": 30]
)
```
- parameter into: an `Into` clause specifying the entity type
- parameter source: the object to import values from
- returns: The object instance correctly associated for the `DataStack` if the object was imported successfully, or `nil` if the `ImportableUniqueObject` ignored the `source`.
- throws: A `CoreStoreError` value indicating the failure reason
*/
public func importUniqueObject<O: DynamicObject & ImportableUniqueObject>(
_ into: Into<O>,
source: O.ImportSource
) async throws -> O? {
return try await withCheckedThrowingContinuation { continuation in
self.base.perform(
asynchronous: { (transaction) -> O? in
return try transaction.importUniqueObject(
into,
source: source
)
},
success: {
continuation.resume(
with: .success($0.flatMap(self.base.fetchExisting))
)
},
failure: continuation.resume(throwing:)
)
}
}
/**
Swift concurrency extension for `CoreStore.DataStack`'s `importUniqueObjects(...)` API. Updates existing `ImportableUniqueObject`s or creates them by importing from the specified array of import sources. `ImportableUniqueObject` methods are called on the objects in the same order as they are in the `sourceArray`, and are returned in an array with that same order. The event values will be object instances correctly associated for the `DataStack`.
```
let people = try await dataStack.async.importUniqueObjects(
Into<Person>(),
sourceArray: [
["name": "John"],
["name": "Bob"],
["name": "Joe"]
]
)
```
- Warning: If `sourceArray` contains multiple import sources with same ID, no merging will occur and ONLY THE LAST duplicate will be imported.
- parameter into: an `Into` clause specifying the entity type
- parameter sourceArray: the array of objects to import values from
- parameter preProcess: a closure that lets the caller tweak the internal `UniqueIDType`-to-`ImportSource` mapping to be used for importing. Callers can remove from/add to/update `mapping` and return the updated array from the closure.
- returns: The imported objects correctly associated for the `DataStack`.
- throws: A `CoreStoreError` value indicating the failure reason
*/
public func importUniqueObjects<O: DynamicObject & ImportableUniqueObject, S: Sequence>(
_ into: Into<O>,
sourceArray: S,
preProcess: @escaping (_ mapping: [O.UniqueIDType: O.ImportSource]) throws -> [O.UniqueIDType: O.ImportSource] = { $0 }
) async throws -> [O]
where S.Iterator.Element == O.ImportSource {
return try await withCheckedThrowingContinuation { continuation in
self.base.perform(
asynchronous: { (transaction) -> [O] in
return try transaction.importUniqueObjects(
into,
sourceArray: sourceArray,
preProcess: preProcess
)
},
success: {
continuation.resume(
with: .success(self.base.fetchExisting($0))
)
},
failure: continuation.resume(throwing:)
)
}
}
/**
Swift concurrency extension for `CoreStore.DataStack`'s `perform(asynchronous:...)` API. Performs a transaction asynchronously where `NSManagedObject` creates, updates, and deletes can be made. The changes are commited automatically after the `task` closure returns. The event value will be the value returned from the `task` closure. Any errors thrown from inside the `task` will be wrapped in a `CoreStoreError` before being thrown from the `async` method. To cancel/rollback changes, call `transaction.cancel()`, which throws a `CoreStoreError.userCancelled`.
```
let result = try await dataStack.async.perform(
asynchronous: { (transaction) -> (inserted: Set<NSManagedObject>, deleted: Set<NSManagedObject>) in
// ...
return (
transaction.insertedObjects(),
transaction.deletedObjects()
)
}
)
let inserted = dataStack.fetchExisting(result.inserted)
let deleted = dataStack.fetchExisting(result.deleted)
```
- parameter task: the asynchronous closure where creates, updates, and deletes can be made to the transaction. Transaction blocks are executed serially in a background queue, and all changes are made from a concurrent `NSManagedObjectContext`.
- returns: The value returned from the `task` closure.
- throws: A `CoreStoreError` value indicating the failure reason
*/
public func perform<Output>(
_ asynchronous: @escaping (AsynchronousDataTransaction) throws -> Output
) async throws -> Output {
return try await withCheckedThrowingContinuation { continuation in
self.base.perform(
asynchronous: asynchronous,
completion: continuation.resume(with:)
)
}
}
}

View File

@@ -93,7 +93,9 @@ extension DataStack.ReactiveNamespace {
- parameter storage: the storage
- returns: A `Future` that emits a `StorageInterface` instance added to the `DataStack`. Note that the `StorageInterface` event value may not always be the same instance as the parameter argument if a previous `StorageInterface` was already added at the same URL and with the same configuration.
*/
public func addStorage<T: StorageInterface>(_ storage: T) -> Future<T, CoreStoreError> {
public func addStorage<T: StorageInterface>(
_ storage: T
) -> Future<T, CoreStoreError> {
return .init { (promise) in
@@ -115,7 +117,7 @@ extension DataStack.ReactiveNamespace {
}
/**
Reactive extension for `CoreStore.DataStack`'s `addStorage(...)` API. Asynchronously adds a `LocalStorage` to the stack. Migrations are also initiated by default. The event emits `DataStack.AddStoragePublisher.MigrationProgress` `enum` values.
Reactive extension for `CoreStore.DataStack`'s `addStorage(...)` API. Asynchronously adds a `LocalStorage` to the stack. Migrations are also initiated by default. The event emits `MigrationProgress` `enum` values.
```
dataStack.reactive
.addStorage(
@@ -135,7 +137,7 @@ extension DataStack.ReactiveNamespace {
.store(in: &cancellables)
```
- parameter storage: the local storage
- returns: A `DataStack.AddStoragePublisher` that emits a `DataStack.AddStoragePublisher.MigrationProgress` value with metadata for migration progress. Note that the `LocalStorage` event value may not always be the same instance as the parameter argument if a previous `LocalStorage` was already added at the same URL and with the same configuration.
- returns: A `DataStack.AddStoragePublisher` that emits a `MigrationProgress` value with metadata for migration progress. Note that the `LocalStorage` event value may not always be the same instance as the parameter argument if a previous `LocalStorage` was already added at the same URL and with the same configuration.
*/
public func addStorage<T: LocalStorage>(_ storage: T) -> DataStack.AddStoragePublisher<T> {

View File

@@ -49,7 +49,7 @@ extension DataStack {
// MARK: Publisher
public typealias Output = MigrationProgress
public typealias Output = CoreStore.MigrationProgress<Storage>
public typealias Failure = CoreStoreError
public func receive<S: Subscriber>(subscriber: S) where S.Input == Output, S.Failure == Failure {
@@ -63,56 +63,6 @@ extension DataStack {
)
}
// MARK: - MigrationProgress
/**
A `MigrationProgress` contains info on a `LocalStorage`'s setup progress.
- SeeAlso: DataStack.reactive.addStorage(_:)
*/
public enum MigrationProgress {
/**
The `LocalStorage` is currently being migrated
*/
case migrating(storage: Storage, progressObject: Progress)
/**
The `LocalStorage` has been added to the `DataStack` and is ready for reading and writing
*/
case finished(storage: Storage, migrationRequired: Bool)
/**
The fraction of the overall work completed by the migration. Returns a value between 0.0 and 1.0, inclusive.
*/
public var fractionCompleted: Double {
switch self {
case .migrating(_, let progressObject):
return progressObject.fractionCompleted
case .finished:
return 1
}
}
/**
Returns `true` if the storage was successfully added to the stack, `false` otherwise.
*/
public var isCompleted: Bool {
switch self {
case .migrating:
return false
case .finished:
return true
}
}
}
// MARK: - AddStorageSubscriber
@@ -232,6 +182,12 @@ extension DataStack {
private let storage: Storage
private var subscriber: S?
}
// MARK: Deprecated
@available(*, deprecated, renamed: "MigrationProgress")
public typealias MigrationProgress = CoreStore.MigrationProgress<Storage>
}
}

View File

@@ -46,7 +46,11 @@ public final class DataStack: Equatable {
- parameter bundle: an optional bundle to load .xcdatamodeld models from. If not specified, the main bundle will be used.
- parameter migrationChain: the `MigrationChain` that indicates the sequence of model versions to be used as the order for progressive migrations. If not specified, will default to a non-migrating data stack.
*/
public convenience init(xcodeModelName: XcodeDataModelFileName = DataStack.applicationName, bundle: Bundle = Bundle.main, migrationChain: MigrationChain = nil) {
public convenience init(
xcodeModelName: XcodeDataModelFileName = DataStack.applicationName,
bundle: Bundle = Bundle.main,
migrationChain: MigrationChain = nil
) {
self.init(
schemaHistory: SchemaHistory(
@@ -79,7 +83,11 @@ public final class DataStack: Equatable {
- parameter otherSchema: a list of other `DynamicSchema` instances that represent present/previous/future model versions, in any order
- parameter migrationChain: the `MigrationChain` that indicates the sequence of model versions to be used as the order for progressive migrations. If not specified, will default to a non-migrating data stack.
*/
public convenience init(_ schema: DynamicSchema, _ otherSchema: DynamicSchema..., migrationChain: MigrationChain = nil) {
public convenience init(
_ schema: DynamicSchema,
_ otherSchema: DynamicSchema...,
migrationChain: MigrationChain = nil
) {
self.init(
schemaHistory: SchemaHistory(
@@ -108,7 +116,9 @@ public final class DataStack: Equatable {
```
- parameter schemaHistory: the `SchemaHistory` for the stack
*/
public required init(schemaHistory: SchemaHistory) {
public required init(
schemaHistory: SchemaHistory
) {
self.coordinator = NSPersistentStoreCoordinator(managedObjectModel: schemaHistory.rawModel)
self.rootSavingContext = NSManagedObjectContext.rootSavingContextForCoordinator(self.coordinator)
@@ -139,7 +149,9 @@ public final class DataStack: Equatable {
/**
Returns the entity name-to-class type mapping from the `DataStack`'s model.
*/
public func entityTypesByName(for type: NSManagedObject.Type) -> [EntityName: NSManagedObject.Type] {
public func entityTypesByName(
for type: NSManagedObject.Type
) -> [EntityName: NSManagedObject.Type] {
var entityTypesByName: [EntityName: NSManagedObject.Type] = [:]
for (entityIdentifier, entityDescription) in self.schemaHistory.entityDescriptionsByEntityIdentifier {
@@ -163,7 +175,9 @@ public final class DataStack: Equatable {
/**
Returns the entity name-to-class type mapping from the `DataStack`'s model.
*/
public func entityTypesByName(for type: CoreStoreObject.Type) -> [EntityName: CoreStoreObject.Type] {
public func entityTypesByName(
for type: CoreStoreObject.Type
) -> [EntityName: CoreStoreObject.Type] {
var entityTypesByName: [EntityName: CoreStoreObject.Type] = [:]
for (entityIdentifier, entityDescription) in self.schemaHistory.entityDescriptionsByEntityIdentifier {
@@ -191,7 +205,9 @@ public final class DataStack: Equatable {
/**
Returns the `NSEntityDescription` for the specified `NSManagedObject` subclass.
*/
public func entityDescription(for type: NSManagedObject.Type) -> NSEntityDescription? {
public func entityDescription(
for type: NSManagedObject.Type
) -> NSEntityDescription? {
return self.entityDescription(for: Internals.EntityIdentifier(type))
}
@@ -199,7 +215,9 @@ public final class DataStack: Equatable {
/**
Returns the `NSEntityDescription` for the specified `CoreStoreObject` subclass.
*/
public func entityDescription(for type: CoreStoreObject.Type) -> NSEntityDescription? {
public func entityDescription(
for type: CoreStoreObject.Type
) -> NSEntityDescription? {
return self.entityDescription(for: Internals.EntityIdentifier(type))
}
@@ -207,7 +225,9 @@ public final class DataStack: Equatable {
/**
Returns the `NSManagedObjectID` for the specified object URI if it exists in the persistent store.
*/
public func objectID(forURIRepresentation url: URL) -> NSManagedObjectID? {
public func objectID(
forURIRepresentation url: URL
) -> NSManagedObjectID? {
return self.coordinator.managedObjectID(forURIRepresentation: url)
}
@@ -236,7 +256,9 @@ public final class DataStack: Equatable {
- returns: the `StorageInterface` added to the stack
*/
@discardableResult
public func addStorageAndWait<T: StorageInterface>(_ storage: T) throws -> T {
public func addStorageAndWait<T: StorageInterface>(
_ storage: T
) throws -> T {
do {
@@ -275,7 +297,9 @@ public final class DataStack: Equatable {
- returns: the local storage added to the stack. Note that this may not always be the same instance as the parameter argument if a previous `LocalStorage` was already added at the same URL and with the same configuration.
*/
@discardableResult
public func addStorageAndWait<T: LocalStorage>(_ storage: T) throws -> T {
public func addStorageAndWait<T: LocalStorage>(
_ storage: T
) throws -> T {
return try self.coordinator.performSynchronously {
@@ -374,7 +398,9 @@ public final class DataStack: Equatable {
Prepares deinitializing the `DataStack` by removing all persistent stores. This is not necessary, but can help silence SQLite warnings when actively releasing and recreating `DataStack`s.
- parameter completion: the closure to execute after all persistent stores are removed
*/
public func unsafeRemoveAllPersistentStores(completion: @escaping () -> Void = {}) {
public func unsafeRemoveAllPersistentStores(
completion: @escaping () -> Void = {}
) {
let coordinator = self.coordinator
coordinator.performAsynchronously {
@@ -441,7 +467,7 @@ public final class DataStack: Equatable {
internal let mainContext: NSManagedObjectContext
internal let schemaHistory: SchemaHistory
internal let childTransactionQueue = DispatchQueue.serial("com.coreStore.dataStack.childTransactionQueue", qos: .utility)
internal let storeMetadataUpdateQueue = DispatchQueue.concurrent("com.coreStore.persistentStoreBarrierQueue", qos: .userInteractive)
internal let storeMetadataLock: NSRecursiveLock = .init()
internal let migrationQueue: OperationQueue = Internals.with {
let migrationQueue = OperationQueue()
@@ -452,56 +478,68 @@ public final class DataStack: Equatable {
return migrationQueue
}
internal func persistentStoreForStorage(_ storage: StorageInterface) -> NSPersistentStore? {
internal func persistentStoreForStorage(
_ storage: StorageInterface
) -> NSPersistentStore? {
return self.coordinator.persistentStores
.filter { $0.storageInterface === storage }
.first
}
internal func persistentStores(for entityIdentifier: Internals.EntityIdentifier) -> [NSPersistentStore]? {
var returnValue: [NSPersistentStore]? = nil
self.storeMetadataUpdateQueue.sync(flags: .barrier) {
returnValue = self.finalConfigurationsByEntityIdentifier[entityIdentifier]?
.map({ self.persistentStoresByFinalConfiguration[$0]! }) ?? []
internal func persistentStores(
for entityIdentifier: Internals.EntityIdentifier
) -> [NSPersistentStore]? {
self.storeMetadataLock.lock()
defer {
self.storeMetadataLock.unlock()
}
return returnValue
return self.finalConfigurationsByEntityIdentifier[entityIdentifier]?
.map({ self.persistentStoresByFinalConfiguration[$0]! }) ?? []
}
internal func persistentStore(for entityIdentifier: Internals.EntityIdentifier, configuration: ModelConfiguration, inferStoreIfPossible: Bool) -> (store: NSPersistentStore?, isAmbiguous: Bool) {
return self.storeMetadataUpdateQueue.sync(flags: .barrier) { () -> (store: NSPersistentStore?, isAmbiguous: Bool) in
let configurationsForEntity = self.finalConfigurationsByEntityIdentifier[entityIdentifier] ?? []
if let configuration = configuration {
if configurationsForEntity.contains(configuration) {
return (store: self.persistentStoresByFinalConfiguration[configuration], isAmbiguous: false)
}
else if !inferStoreIfPossible {
return (store: nil, isAmbiguous: false)
}
internal func persistentStore(
for entityIdentifier: Internals.EntityIdentifier,
configuration: ModelConfiguration,
inferStoreIfPossible: Bool
) -> (store: NSPersistentStore?, isAmbiguous: Bool) {
self.storeMetadataLock.lock()
defer {
self.storeMetadataLock.unlock()
}
let configurationsForEntity = self.finalConfigurationsByEntityIdentifier[entityIdentifier] ?? []
if let configuration = configuration {
if configurationsForEntity.contains(configuration) {
return (store: self.persistentStoresByFinalConfiguration[configuration], isAmbiguous: false)
}
switch configurationsForEntity.count {
case 0:
else if !inferStoreIfPossible {
return (store: nil, isAmbiguous: false)
case 1 where inferStoreIfPossible:
return (store: self.persistentStoresByFinalConfiguration[configurationsForEntity.first!], isAmbiguous: false)
default:
return (store: nil, isAmbiguous: true)
}
}
switch configurationsForEntity.count {
case 0:
return (store: nil, isAmbiguous: false)
case 1 where inferStoreIfPossible:
return (store: self.persistentStoresByFinalConfiguration[configurationsForEntity.first!], isAmbiguous: false)
default:
return (store: nil, isAmbiguous: true)
}
}
internal func createPersistentStoreFromStorage(_ storage: StorageInterface, finalURL: URL?, finalStoreOptions: [AnyHashable: Any]?) throws -> NSPersistentStore {
internal func createPersistentStoreFromStorage(
_ storage: StorageInterface,
finalURL: URL?,
finalStoreOptions: [AnyHashable: Any]?
) throws -> NSPersistentStore {
let persistentStore = try self.coordinator.addPersistentStore(
ofType: type(of: storage).storeType,
@@ -510,8 +548,13 @@ public final class DataStack: Equatable {
options: finalStoreOptions
)
persistentStore.storageInterface = storage
self.storeMetadataUpdateQueue.async(flags: .barrier) {
do {
self.storeMetadataLock.lock()
defer {
self.storeMetadataLock.unlock()
}
let configurationName = persistentStore.configurationName
self.persistentStoresByFinalConfiguration[configurationName] = persistentStore
@@ -534,7 +577,9 @@ public final class DataStack: Equatable {
return persistentStore
}
internal func entityDescription(for entityIdentifier: Internals.EntityIdentifier) -> NSEntityDescription? {
internal func entityDescription(
for entityIdentifier: Internals.EntityIdentifier
) -> NSEntityDescription? {
return self.schemaHistory.entityDescriptionsByEntityIdentifier[entityIdentifier]
}

View File

@@ -0,0 +1,79 @@
//
// MigrationProgress.swift
// CoreStore
//
// Copyright © 2021 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: - MigrationProgress
/**
A `MigrationProgress` contains info on a `LocalStorage`'s setup progress.
- SeeAlso: DataStack.reactive.addStorage(_:)
- SeeAlso: DataStack.async.addStorage(_:)
*/
public enum MigrationProgress<T: LocalStorage> {
/**
The `LocalStorage` is currently being migrated
*/
case migrating(storage: T, progressObject: Progress)
/**
The `LocalStorage` has been added to the `DataStack` and is ready for reading and writing
*/
case finished(storage: T, migrationRequired: Bool)
/**
The fraction of the overall work completed by the migration. Returns a value between 0.0 and 1.0, inclusive.
*/
public var fractionCompleted: Double {
switch self {
case .migrating(_, let progressObject):
return progressObject.fractionCompleted
case .finished:
return 1
}
}
/**
Returns `true` if the storage was successfully added to the stack, `false` otherwise.
*/
public var isCompleted: Bool {
switch self {
case .migrating:
return false
case .finished:
return true
}
}
}

View File

@@ -197,7 +197,8 @@ public struct Where<O: DynamicObject>: WhereClauseType, FetchClause, QueryClause
)
}
switch value {
#if swift(>=5.7)
case let optionalValue as any FieldOptionalType:
switch optionalValue.cs_wrappedValue {
@@ -209,6 +210,12 @@ public struct Where<O: DynamicObject>: WhereClauseType, FetchClause, QueryClause
self.init(valuePredicate)
}
#else
case nil:
self.init(nilPredicate)
#endif
case is NSNull:
self.init(nilPredicate)