Compare commits

..

6 Commits
2.1.0 ... 2.1.2

Author SHA1 Message Date
John Estropia
65ac069a0b skip observer testing on macOS 2016-09-30 13:32:31 +09:00
John Estropia
862ef27374 version bump, cleanup, unit test 2016-09-30 13:28:19 +09:00
John Estropia
243b6a76d5 Merge pull request #104 from colinmorelli/feature/sync-transaction-async-merge
Fixes #98 (deadlock when merge happens while main queue is querying)
2016-09-30 12:38:28 +09:00
Colin Morelli
8be20370d5 Fixes #98 (deadlock when merge happens while main queue is querying) 2016-09-29 14:47:51 -04:00
John Estropia
ed8c7b35e8 Reduce leaking (a little) on the workaround for NSFetchRequest.affectedStores ARC bug 2016-09-27 17:31:08 +09:00
John Rommel Estropia
4d2ebe4ea8 workaround #100 (NSFetchRequest.affectedStores ARC bug) 2016-09-27 02:39:28 +09:00
22 changed files with 279 additions and 145 deletions

View File

@@ -12,10 +12,11 @@ env:
matrix:
- DESTINATION="OS=10.0,name=iPhone 7" SCHEME="CoreStore iOS" SDK=iphonesimulator10.0 RUN_TESTS="YES" POD_LINT="NO"
- DESTINATION="OS=9.0,name=iPhone 6 Plus" SCHEME="CoreStore iOS" SDK=iphonesimulator10.0 RUN_TESTS="YES" POD_LINT="NO"
- DESTINATION="OS=8.4,name=iPhone 6" SCHEME="CoreStore iOS" SDK=iphonesimulator10.0 RUN_TESTS="YES" POD_LINT="NO"
- DESTINATION="OS=8.3,name=iPhone 5S" SCHEME="CoreStore iOS" SDK=iphonesimulator10.0 RUN_TESTS="YES" POD_LINT="NO"
- DESTINATION="OS=8.2,name=iPhone 5" SCHEME="CoreStore iOS" SDK=iphonesimulator10.0 RUN_TESTS="YES" POD_LINT="NO"
- DESTINATION="OS=8.1,name=iPhone 4S" SCHEME="CoreStore iOS" SDK=iphonesimulator10.0 RUN_TESTS="YES" POD_LINT="YES"
# iOS 8 testing currently broken on Xcode 8
# - DESTINATION="OS=8.4,name=iPhone 6" SCHEME="CoreStore iOS" SDK=iphonesimulator10.0 RUN_TESTS="YES" POD_LINT="NO"
# - DESTINATION="OS=8.3,name=iPhone 5s" SCHEME="CoreStore iOS" SDK=iphonesimulator10.0 RUN_TESTS="YES" POD_LINT="NO"
# - DESTINATION="OS=8.2,name=iPhone 5" SCHEME="CoreStore iOS" SDK=iphonesimulator10.0 RUN_TESTS="YES" POD_LINT="NO"
# - DESTINATION="OS=8.1,name=iPhone 4s" SCHEME="CoreStore iOS" SDK=iphonesimulator10.0 RUN_TESTS="YES" POD_LINT="YES"
- DESTINATION="arch=x86_64" SCHEME="CoreStore OSX" SDK=macosx10.12 RUN_TESTS="YES" POD_LINT="NO"
- DESTINATION="OS=3.0,name=Apple Watch - 42mm" SCHEME="CoreStore watchOS" SDK=watchsimulator3.0 RUN_TESTS="NO" POD_LINT="NO"
- DESTINATION="OS=2.2,name=Apple Watch - 42mm" SCHEME="CoreStore watchOS" SDK=watchsimulator3.0 RUN_TESTS="NO" POD_LINT="NO"

View File

@@ -1,6 +1,6 @@
Pod::Spec.new do |s|
s.name = "CoreStore"
s.version = "2.1.0"
s.version = "2.1.2"
s.license = "MIT"
s.summary = "Unleashing the real power of Core Data with the elegance and safety of Swift"
s.homepage = "https://github.com/JohnEstropia/CoreStore"

View File

@@ -145,10 +145,6 @@
B52557881D02DE8100E51965 /* FetchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52557871D02DE8100E51965 /* FetchTests.swift */; };
B52557891D02DE8100E51965 /* FetchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52557871D02DE8100E51965 /* FetchTests.swift */; };
B525578A1D02DE8100E51965 /* FetchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = B52557871D02DE8100E51965 /* FetchTests.swift */; };
B52661401CADD585007B85D9 /* CoreStoreFetchRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526613F1CADD585007B85D9 /* CoreStoreFetchRequest.swift */; };
B52661421CADD585007B85D9 /* CoreStoreFetchRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526613F1CADD585007B85D9 /* CoreStoreFetchRequest.swift */; };
B52661431CADD585007B85D9 /* CoreStoreFetchRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526613F1CADD585007B85D9 /* CoreStoreFetchRequest.swift */; };
B52661441CADD585007B85D9 /* CoreStoreFetchRequest.swift in Sources */ = {isa = PBXBuildFile; fileRef = B526613F1CADD585007B85D9 /* CoreStoreFetchRequest.swift */; };
B529C2041CA4A2DB007E7EBD /* CSSaveResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B529C2031CA4A2DB007E7EBD /* CSSaveResult.swift */; };
B529C2061CA4A2DB007E7EBD /* CSSaveResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B529C2031CA4A2DB007E7EBD /* CSSaveResult.swift */; };
B529C2071CA4A2DC007E7EBD /* CSSaveResult.swift in Sources */ = {isa = PBXBuildFile; fileRef = B529C2031CA4A2DB007E7EBD /* CSSaveResult.swift */; };
@@ -600,7 +596,6 @@
B525577F1D029D2500E51965 /* TweakTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = TweakTests.swift; sourceTree = "<group>"; };
B52557831D02A07400E51965 /* SectionByTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SectionByTests.swift; sourceTree = "<group>"; };
B52557871D02DE8100E51965 /* FetchTests.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FetchTests.swift; sourceTree = "<group>"; };
B526613F1CADD585007B85D9 /* CoreStoreFetchRequest.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CoreStoreFetchRequest.swift; sourceTree = "<group>"; };
B529C2031CA4A2DB007E7EBD /* CSSaveResult.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CSSaveResult.swift; sourceTree = "<group>"; };
B52DD1741BE1F8CC00949AFE /* CoreStore.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = CoreStore.framework; sourceTree = BUILT_PRODUCTS_DIR; };
B52DD17D1BE1F8CC00949AFE /* CoreStoreTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CoreStoreTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; };
@@ -1178,7 +1173,6 @@
B5E834BA1B7691F3001D3D50 /* Functions.swift */,
B5FAD6AB1B51285300714891 /* MigrationManager.swift */,
B5E84F2B1AFF849C0064E85B /* NotificationObserver.swift */,
B526613F1CADD585007B85D9 /* CoreStoreFetchRequest.swift */,
B5E84F2C1AFF849C0064E85B /* NSManagedObjectContext+CoreStore.swift */,
B5E84F351AFF85470064E85B /* NSManagedObjectContext+Querying.swift */,
B5E84F321AFF85470064E85B /* NSManagedObjectContext+Setup.swift */,
@@ -1400,7 +1394,7 @@
isa = PBXProject;
attributes = {
LastSwiftUpdateCheck = 0730;
LastUpgradeCheck = 0700;
LastUpgradeCheck = 0800;
ORGANIZATIONNAME = "John Rommel Estropia";
TargetAttributes = {
2F03A52F19C5C6DA005002A5 = {
@@ -1558,7 +1552,6 @@
B53FB9FE1CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */,
B5DBE2D21C991B3E00B5CEFA /* CSDataStack.swift in Sources */,
B50392F91C478FF3009900CA /* NSManagedObject+Transaction.swift in Sources */,
B52661401CADD585007B85D9 /* CoreStoreFetchRequest.swift in Sources */,
B53FBA181CAB63E200F0D40A /* NSManagedObject+ObjectiveC.swift in Sources */,
B5202CFA1C04688100DED140 /* NSFetchedResultsController+Convenience.swift in Sources */,
B5519A591CA2008C002BEF78 /* CSBaseDataTransaction.swift in Sources */,
@@ -1708,7 +1701,6 @@
82BA18B41C4BBD3900A0916E /* BaseDataTransaction+Importing.swift in Sources */,
B53FBA1A1CAB63E200F0D40A /* NSManagedObject+ObjectiveC.swift in Sources */,
82BA18CA1C4BBD5900A0916E /* MigrationResult.swift in Sources */,
B52661421CADD585007B85D9 /* CoreStoreFetchRequest.swift in Sources */,
B5519A5A1CA2008C002BEF78 /* CSBaseDataTransaction.swift in Sources */,
B5ECDBE11CA6BB2B00C7F112 /* CSBaseDataTransaction+Querying.swift in Sources */,
82BA18C11C4BBD5300A0916E /* CoreStore+Observing.swift in Sources */,
@@ -1857,7 +1849,6 @@
B52DD1C71BE1F94600949AFE /* NSManagedObjectContext+Querying.swift in Sources */,
B52DD1C81BE1F94600949AFE /* NSManagedObjectContext+Setup.swift in Sources */,
B52DD1C31BE1F94600949AFE /* NotificationObserver.swift in Sources */,
B52661441CADD585007B85D9 /* CoreStoreFetchRequest.swift in Sources */,
B52DD1A81BE1F93200949AFE /* DataStack+Querying.swift in Sources */,
B5220E221D130818009BC71E /* CSSectionBy.swift in Sources */,
B52DD1BC1BE1F94000949AFE /* MigrationResult.swift in Sources */,
@@ -2001,7 +1992,6 @@
B53FBA011CAB2D2F00F0D40A /* CSMigrationResult.swift in Sources */,
B5DBE2D41C991B3E00B5CEFA /* CSDataStack.swift in Sources */,
B50392FA1C47963F009900CA /* NSManagedObject+Transaction.swift in Sources */,
B52661431CADD585007B85D9 /* CoreStoreFetchRequest.swift in Sources */,
B53FBA1B1CAB63E200F0D40A /* NSManagedObject+ObjectiveC.swift in Sources */,
B5519A5B1CA2008C002BEF78 /* CSBaseDataTransaction.swift in Sources */,
B5ECDBE21CA6BB2B00C7F112 /* CSBaseDataTransaction+Querying.swift in Sources */,
@@ -2106,8 +2096,10 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@@ -2117,6 +2109,7 @@
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_DYNAMIC_NO_PIC = NO;
GCC_NO_COMMON_BLOCKS = YES;
GCC_OPTIMIZATION_LEVEL = 0;
GCC_PREPROCESSOR_DEFINITIONS = (
"DEBUG=1",
@@ -2162,8 +2155,10 @@
CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
CLANG_WARN_EMPTY_BODY = YES;
CLANG_WARN_ENUM_CONVERSION = YES;
CLANG_WARN_INFINITE_RECURSION = YES;
CLANG_WARN_INT_CONVERSION = YES;
CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
CLANG_WARN_SUSPICIOUS_MOVE = YES;
CLANG_WARN_UNREACHABLE_CODE = YES;
CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer";
@@ -2173,6 +2168,7 @@
ENABLE_STRICT_OBJC_MSGSEND = YES;
ENABLE_TESTABILITY = YES;
GCC_C_LANGUAGE_STANDARD = gnu99;
GCC_NO_COMMON_BLOCKS = YES;
GCC_PREPROCESSOR_DEFINITIONS = "USE_FRAMEWORKS=1";
GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
@@ -2188,7 +2184,7 @@
PRODUCT_BUNDLE_IDENTIFIER = com.johnestropia.CoreStore;
PRODUCT_NAME = CoreStore;
SDKROOT = iphoneos;
SWIFT_OPTIMIZATION_LEVEL = "-O";
SWIFT_OPTIMIZATION_LEVEL = "-Owholemodule";
TARGETED_DEVICE_FAMILY = "1,2";
TVOS_DEPLOYMENT_TARGET = 9.0;
VALIDATE_PRODUCT = YES;
@@ -2202,6 +2198,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
@@ -2219,6 +2216,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
"CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
@@ -2268,6 +2266,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
@@ -2287,6 +2286,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
"CODE_SIGN_IDENTITY[sdk=appletvos*]" = "";
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
DYLIB_CURRENT_VERSION = 1;
@@ -2339,7 +2339,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_IDENTITY = "";
COMBINE_HIDPI_IMAGES = YES;
DEBUG_INFORMATION_FORMAT = dwarf;
DEFINES_MODULE = YES;
@@ -2361,7 +2361,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
CODE_SIGN_IDENTITY = "-";
CODE_SIGN_IDENTITY = "";
COMBINE_HIDPI_IMAGES = YES;
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
@@ -2421,6 +2421,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
DEBUG_INFORMATION_FORMAT = dwarf;
DEFINES_MODULE = YES;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -2441,6 +2442,7 @@
isa = XCBuildConfiguration;
buildSettings = {
CLANG_ENABLE_MODULES = YES;
"CODE_SIGN_IDENTITY[sdk=watchos*]" = "";
COPY_PHASE_STRIP = NO;
DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
DEFINES_MODULE = YES;

View File

@@ -10,10 +10,10 @@
"DVTSourceControlWorkspaceBlueprintIdentifierKey" : "F347F55F-7F5C-4476-9148-6E902F06E4AD",
"DVTSourceControlWorkspaceBlueprintWorkingCopyPathsKey" : {
"8B2E522D57154DFA93A06982C36315ECBEA4FA97" : "CoreStoreLibraries\/GCDKit",
"4B60F1BCB491FF717C56441AE7783C74F417BE48" : "CoreStore"
"4B60F1BCB491FF717C56441AE7783C74F417BE48" : "CoreStore\/"
},
"DVTSourceControlWorkspaceBlueprintNameKey" : "CoreStore",
"DVTSourceControlWorkspaceBlueprintVersion" : 203,
"DVTSourceControlWorkspaceBlueprintVersion" : 204,
"DVTSourceControlWorkspaceBlueprintRelativePathToProjectKey" : "CoreStore.xcodeproj",
"DVTSourceControlWorkspaceBlueprintRemoteRepositoriesKey" : [
{

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0710"
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0700"
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"
@@ -11,8 +11,7 @@
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES"
hideIssues = "NO">
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2F03A52F19C5C6DA005002A5"
@@ -26,8 +25,7 @@
buildForRunning = "NO"
buildForProfiling = "NO"
buildForArchiving = "NO"
buildForAnalyzing = "NO"
hideIssues = "NO">
buildForAnalyzing = "NO">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "2F03A53A19C5C6DA005002A5"

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0720"
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "0700"
LastUpgradeVersion = "0800"
version = "1.3">
<BuildAction
parallelizeBuildables = "YES"

View File

@@ -74,33 +74,33 @@ final class FromTests: BaseTestCase {
let from = From<TestEntity1>()
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
XCTAssertTrue(storesFound)
XCTAssertNotNil(request.entity)
XCTAssertNotNil(request.affectedStores)
XCTAssertNotNil(request.safeAffectedStores)
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
XCTAssertEqual(affectedConfigurations, ["PF_DEFAULT_CONFIGURATION_NAME"])
}
do {
let from = From<TestEntity1>("Config1")
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
let storesFound = self.expectLogger([.LogWarning]) {
from.applyToFetchRequest(request, context: dataStack.mainContext)
}
XCTAssertFalse(storesFound)
XCTAssertNotNil(request.entity)
XCTAssertNotNil(request.affectedStores)
XCTAssertNotNil(request.safeAffectedStores)
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
XCTAssertTrue(affectedConfigurations.isEmpty)
}
}
@@ -115,102 +115,102 @@ final class FromTests: BaseTestCase {
let from = From<TestEntity1>()
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
XCTAssertTrue(storesFound)
XCTAssertNotNil(request.entity)
XCTAssertNotNil(request.affectedStores)
XCTAssertNotNil(request.safeAffectedStores)
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
XCTAssertEqual(affectedConfigurations, ["Config1"])
}
do {
let from = From<TestEntity1>("Config1")
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
XCTAssertTrue(storesFound)
XCTAssertNotNil(request.entity)
XCTAssertNotNil(request.affectedStores)
XCTAssertNotNil(request.safeAffectedStores)
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
XCTAssertEqual(affectedConfigurations, ["Config1"])
}
do {
let from = From<TestEntity1>("Config2")
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
let storesFound = self.expectLogger([.LogWarning]) {
from.applyToFetchRequest(request, context: dataStack.mainContext)
}
XCTAssertFalse(storesFound)
XCTAssertNotNil(request.entity)
XCTAssertNotNil(request.affectedStores)
XCTAssertNotNil(request.safeAffectedStores)
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
XCTAssertTrue(affectedConfigurations.isEmpty)
}
do {
let from = From<TestEntity2>()
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
let storesFound = self.expectLogger([.LogWarning]) {
from.applyToFetchRequest(request, context: dataStack.mainContext)
}
XCTAssertFalse(storesFound)
XCTAssertNotNil(request.entity)
XCTAssertNotNil(request.affectedStores)
XCTAssertNotNil(request.safeAffectedStores)
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
XCTAssertTrue(affectedConfigurations.isEmpty)
}
do {
let from = From<TestEntity2>("Config1")
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
let storesFound = self.expectLogger([.LogWarning]) {
from.applyToFetchRequest(request, context: dataStack.mainContext)
}
XCTAssertFalse(storesFound)
XCTAssertNotNil(request.entity)
XCTAssertNotNil(request.affectedStores)
XCTAssertNotNil(request.safeAffectedStores)
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
XCTAssertTrue(affectedConfigurations.isEmpty)
}
do {
let from = From<TestEntity2>("Config2")
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
let storesFound = self.expectLogger([.LogWarning]) {
from.applyToFetchRequest(request, context: dataStack.mainContext)
}
XCTAssertFalse(storesFound)
XCTAssertNotNil(request.entity)
XCTAssertNotNil(request.affectedStores)
XCTAssertNotNil(request.safeAffectedStores)
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
XCTAssertTrue(affectedConfigurations.isEmpty)
}
}
@@ -225,99 +225,99 @@ final class FromTests: BaseTestCase {
let from = From<TestEntity1>()
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
XCTAssertTrue(storesFound)
XCTAssertNotNil(request.entity)
XCTAssertNotNil(request.affectedStores)
XCTAssertNotNil(request.safeAffectedStores)
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
XCTAssertEqual(Set(affectedConfigurations), ["PF_DEFAULT_CONFIGURATION_NAME", "Config1"] as Set)
}
do {
let from = From<TestEntity1>("Config1")
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
XCTAssertTrue(storesFound)
XCTAssertNotNil(request.entity)
XCTAssertNotNil(request.affectedStores)
XCTAssertNotNil(request.safeAffectedStores)
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
XCTAssertEqual(affectedConfigurations, ["Config1"])
}
do {
let from = From<TestEntity1>("Config2")
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
let storesFound = self.expectLogger([.LogWarning]) {
from.applyToFetchRequest(request, context: dataStack.mainContext)
}
XCTAssertFalse(storesFound)
XCTAssertNotNil(request.entity)
XCTAssertNotNil(request.affectedStores)
XCTAssertNotNil(request.safeAffectedStores)
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
XCTAssertTrue(affectedConfigurations.isEmpty)
}
do {
let from = From<TestEntity2>()
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
XCTAssertTrue(storesFound)
XCTAssertNotNil(request.entity)
XCTAssertNotNil(request.affectedStores)
XCTAssertNotNil(request.safeAffectedStores)
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
XCTAssertEqual(affectedConfigurations, ["PF_DEFAULT_CONFIGURATION_NAME"])
}
do {
let from = From<TestEntity2>("Config1")
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
let storesFound = self.expectLogger([.LogWarning]) {
from.applyToFetchRequest(request, context: dataStack.mainContext)
}
XCTAssertFalse(storesFound)
XCTAssertNotNil(request.entity)
XCTAssertNotNil(request.affectedStores)
XCTAssertNotNil(request.safeAffectedStores)
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
XCTAssertTrue(affectedConfigurations.isEmpty)
}
do {
let from = From<TestEntity2>("Config2")
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
let storesFound = self.expectLogger([.LogWarning]) {
from.applyToFetchRequest(request, context: dataStack.mainContext)
}
XCTAssertFalse(storesFound)
XCTAssertNotNil(request.entity)
XCTAssertNotNil(request.affectedStores)
XCTAssertNotNil(request.safeAffectedStores)
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
XCTAssertTrue(affectedConfigurations.isEmpty)
}
}
@@ -332,96 +332,96 @@ final class FromTests: BaseTestCase {
let from = From<TestEntity1>()
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
XCTAssertTrue(storesFound)
XCTAssertNotNil(request.entity)
XCTAssertNotNil(request.affectedStores)
XCTAssertNotNil(request.safeAffectedStores)
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
XCTAssertEqual(affectedConfigurations, ["Config1"])
}
do {
let from = From<TestEntity1>("Config1")
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
XCTAssertTrue(storesFound)
XCTAssertNotNil(request.entity)
XCTAssertNotNil(request.affectedStores)
XCTAssertNotNil(request.safeAffectedStores)
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
XCTAssertEqual(affectedConfigurations, ["Config1"])
}
do {
let from = From<TestEntity1>("Config2")
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
let storesFound = self.expectLogger([.LogWarning]) {
from.applyToFetchRequest(request, context: dataStack.mainContext)
}
XCTAssertFalse(storesFound)
XCTAssertNotNil(request.entity)
XCTAssertNotNil(request.affectedStores)
XCTAssertNotNil(request.safeAffectedStores)
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
XCTAssertTrue(affectedConfigurations.isEmpty)
}
do {
let from = From<TestEntity2>()
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
XCTAssertTrue(storesFound)
XCTAssertNotNil(request.entity)
XCTAssertNotNil(request.affectedStores)
XCTAssertNotNil(request.safeAffectedStores)
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
XCTAssertEqual(affectedConfigurations, ["Config2"])
}
do {
let from = From<TestEntity2>("Config1")
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
let storesFound = self.expectLogger([.LogWarning]) {
from.applyToFetchRequest(request, context: dataStack.mainContext)
}
XCTAssertFalse(storesFound)
XCTAssertNotNil(request.entity)
XCTAssertNotNil(request.affectedStores)
XCTAssertNotNil(request.safeAffectedStores)
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
XCTAssertTrue(affectedConfigurations.isEmpty)
}
do {
let from = From<TestEntity2>("Config2")
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
let storesFound = from.applyToFetchRequest(request, context: dataStack.mainContext)
XCTAssertTrue(storesFound)
XCTAssertNotNil(request.entity)
XCTAssertNotNil(request.affectedStores)
XCTAssertNotNil(request.safeAffectedStores)
XCTAssert(from.entityClass == NSClassFromString(request.entity!.managedObjectClassName))
let affectedConfigurations = request.affectedStores!.map { $0.configurationName }
let affectedConfigurations = request.safeAffectedStores!.map { $0.configurationName }
XCTAssertEqual(affectedConfigurations, ["Config2"])
}
}

View File

@@ -68,7 +68,7 @@ final class GroupByTests: BaseTestCase {
let groupBy = GroupBy("testString")
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
_ = From(TestEntity1).applyToFetchRequest(request, context: dataStack.mainContext)
groupBy.applyToFetchRequest(request)

View File

@@ -179,7 +179,7 @@ final class OrderByTests: XCTestCase {
dynamic func test_ThatOrderByClauses_ApplyToFetchRequestsCorrectly() {
let orderBy = OrderBy(.Ascending("key"))
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
orderBy.applyToFetchRequest(request)
XCTAssertNotNil(request.sortDescriptors)
XCTAssertEqual(request.sortDescriptors ?? [], orderBy.sortDescriptors)

View File

@@ -345,6 +345,107 @@ final class TransactionTests: BaseTestCase {
}
}
#if os(iOS) || os(watchOS) || os(tvOS)
@objc
dynamic func test_ThatSynchronousTransactions_CanCommitWithoutWaitingForMerges() {
self.prepareStack { (stack) in
let observer = TestListObserver()
let monitor = stack.monitorList(
From(TestEntity1),
OrderBy(.Ascending("testEntityID"))
)
monitor.addObserver(observer)
XCTAssertFalse(monitor.hasObjects())
var events = 0
let willChangeExpectation = self.expectationForNotification(
"listMonitorWillChange:",
object: observer,
handler: { (note) -> Bool in
XCTAssertEqual(events, 0)
XCTAssertEqual((note.userInfo ?? [:]), NSDictionary())
defer {
events += 1
}
return events == 0
}
)
let didInsertObjectExpectation = self.expectationForNotification(
"listMonitor:didInsertObject:toIndexPath:",
object: observer,
handler: { (note) -> Bool in
XCTAssertEqual(events, 1)
let userInfo = note.userInfo
XCTAssertNotNil(userInfo)
XCTAssertEqual(
Set(((userInfo as? [String: AnyObject]) ?? [:]).keys),
["indexPath", "object"]
)
let indexPath = userInfo?["indexPath"] as? NSIndexPath
XCTAssertEqual(indexPath?.section, 0)
XCTAssertEqual(indexPath?.row, 0)
let object = userInfo?["object"] as? TestEntity1
XCTAssertEqual(object?.testBoolean, NSNumber(bool: true))
XCTAssertEqual(object?.testNumber, NSNumber(integer: 1))
XCTAssertEqual(object?.testDecimal, NSDecimalNumber(string: "1"))
XCTAssertEqual(object?.testString, "nil:TestEntity1:1")
defer {
events += 1
}
return events == 1
}
)
let didChangeExpectation = self.expectationForNotification(
"listMonitorDidChange:",
object: observer,
handler: { (note) -> Bool in
XCTAssertEqual((note.userInfo ?? [:]), NSDictionary())
defer {
events += 1
}
return events == 2
}
)
let saveExpectation = self.expectationWithDescription("save")
stack.beginSynchronous { (transaction) in
let object = transaction.create(Into(TestEntity1))
object.testBoolean = NSNumber(bool: true)
object.testNumber = NSNumber(integer: 1)
object.testDecimal = NSDecimalNumber(string: "1")
object.testString = "nil:TestEntity1:1"
switch transaction.commit() {
case .Success(let hasChanges):
XCTAssertTrue(hasChanges)
saveExpectation.fulfill()
default:
XCTFail()
}
}
XCTAssertEqual(events, 0)
XCTAssertEqual(monitor.numberOfObjects(), 0)
self.waitAndCheckExpectations()
}
}
#endif
@objc
dynamic func test_ThatAsynchronousTransactions_CanPerformCRUDs() {

View File

@@ -43,7 +43,7 @@ final class TweakTests: XCTestCase {
$0.fetchLimit = 200
$0.predicate = predicate
}
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
tweak.applyToFetchRequest(request)
XCTAssertEqual(request.fetchOffset, 100)
XCTAssertEqual(request.fetchLimit, 200)

View File

@@ -142,7 +142,7 @@ final class WhereTests: XCTestCase {
dynamic func test_ThatWhereClauses_ApplyToFetchRequestsCorrectly() {
let whereClause = Where("key", isEqualTo: "value")
let request = NSFetchRequest()
let request = CoreStoreFetchRequest()
whereClause.applyToFetchRequest(request)
XCTAssertNotNil(request.predicate)
XCTAssertEqual(request.predicate, whereClause.predicate)

View File

@@ -15,7 +15,7 @@
<key>CFBundlePackageType</key>
<string>FMWK</string>
<key>CFBundleShortVersionString</key>
<string>2.0.6</string>
<string>2.1.2</string>
<key>CFBundleSignature</key>
<string>????</string>
<key>CFBundleVersion</key>

View File

@@ -1,42 +0,0 @@
//
// CoreStoreFetchRequest.swift
// CoreStore
//
// Copyright © 2016 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: - CoreStoreFetchRequest
// Bugfix for NSFetchRequest messing up memory management for `affectedStores`
// http://stackoverflow.com/questions/14396375/nsfetchedresultscontroller-crashes-in-ios-6-if-affectedstores-is-specified
internal final class CoreStoreFetchRequest: NSFetchRequest {
@objc
override var affectedStores: [NSPersistentStore]? {
get { return super.affectedStores }
set { super.affectedStores = newValue }
}
}

View File

@@ -101,8 +101,8 @@ internal extension NSManagedObjectContext {
}
@nonobjc
internal func saveSynchronously() -> SaveResult {
internal func saveSynchronously(waitForMerge waitForMerge: Bool) -> SaveResult {
var result = SaveResult(hasChanges: false)
self.performBlockAndWait {
@@ -114,7 +114,7 @@ internal extension NSManagedObjectContext {
do {
self.isSavingSynchronously = true
self.isSavingSynchronously = waitForMerge
try self.save()
self.isSavingSynchronously = nil
}
@@ -131,7 +131,7 @@ internal extension NSManagedObjectContext {
if let parentContext = self.parentContext where self.shouldCascadeSavesToParent {
switch parentContext.saveSynchronously() {
switch parentContext.saveSynchronously(waitForMerge: waitForMerge) {
case .Success:
result = SaveResult(hasChanges: true)

View File

@@ -570,4 +570,16 @@ CORESTORE_EXTERN
CSWhere *_Nonnull CSWherePredicate(NSPredicate *_Nonnull predicate) CORESTORE_RETURNS_RETAINED;
#pragma mark CoreStoreFetchRequest
// Bugfix for NSFetchRequest messing up memory management for `affectedStores`
// http://stackoverflow.com/questions/14396375/nsfetchedresultscontroller-crashes-in-ios-6-if-affectedstores-is-specified
NS_SWIFT_NAME(CoreStoreFetchRequest)
@interface _CSFetchRequest: NSFetchRequest
@property (nullable, nonatomic, copy, readonly) NSArray<NSPersistentStore *> *safeAffectedStores;
@end
#endif /* CoreStoreBridge_h */

View File

@@ -218,3 +218,40 @@ CSWhere *_Nonnull CSWherePredicate(NSPredicate *_Nonnull predicate) CORESTORE_RE
return [[CSWhere alloc] initWithPredicate:predicate];
}
#pragma mark CoreStoreFetchRequest
@interface _CSFetchRequest ()
@property (nullable, nonatomic, copy) NSArray<NSPersistentStore *> *safeAffectedStores;
@property (nullable, nonatomic, assign) CFArrayRef releaseArray;
@end
@implementation _CSFetchRequest
// MARK: NSFetchRequest
- (void)setAffectedStores:(NSArray<NSPersistentStore *> *_Nullable)affectedStores {
// Bugfix for NSFetchRequest messing up memory management for `affectedStores`
// http://stackoverflow.com/questions/14396375/nsfetchedresultscontroller-crashes-in-ios-6-if-affectedstores-is-specified
if (NSFoundationVersionNumber < NSFoundationVersionNumber10_0) {
self.safeAffectedStores = affectedStores;
[super setAffectedStores:affectedStores];
return;
}
if (self.releaseArray != NULL) {
CFRelease(self.releaseArray);
self.releaseArray = NULL;
}
self.safeAffectedStores = affectedStores;
[super setAffectedStores:affectedStores];
self.releaseArray = CFBridgingRetain([super affectedStores]);
}
@end

View File

@@ -1123,7 +1123,7 @@ public final class ListMonitor<T: NSManagedObject>: Hashable {
self.isPersistentStoreChanging = true
guard let removedStores = (note.userInfo?[NSRemovedPersistentStoresKey] as? [NSPersistentStore]).flatMap(Set.init)
where !Set(self.fetchedResultsController.fetchRequest.affectedStores ?? []).intersect(removedStores).isEmpty else {
where !Set((self.fetchedResultsController.fetchRequest as! CoreStoreFetchRequest).safeAffectedStores ?? []).intersect(removedStores).isEmpty else {
return
}
@@ -1144,7 +1144,7 @@ public final class ListMonitor<T: NSManagedObject>: Hashable {
if !self.isPendingRefetch {
let previousStores = Set(self.fetchedResultsController.fetchRequest.affectedStores ?? [])
let previousStores = Set((self.fetchedResultsController.fetchRequest as! CoreStoreFetchRequest).safeAffectedStores ?? [])
let currentStores = previousStores
.subtract(note.userInfo?[NSRemovedPersistentStoresKey] as? [NSPersistentStore] ?? [])
.union(note.userInfo?[NSAddedPersistentStoresKey] as? [NSPersistentStore] ?? [])

View File

@@ -38,7 +38,8 @@ import CoreData
public final class SynchronousDataTransaction: BaseDataTransaction {
/**
Saves the transaction changes and waits for completion synchronously. This method should not be used after the `commit()` method was already called once.
Saves the transaction changes and waits for completion synchronously. This method should not be used after the `commit()` or `commitAndWait()` method was already called once.
- Important: Unlike `SynchronousDataTransaction.commit()`, this method waits for all observers to be notified of the changes before returning. This results in more predictable data update order, but may risk triggering deadlocks.
- returns: a `SaveResult` containing the success or failure information
*/
@@ -55,11 +56,35 @@ public final class SynchronousDataTransaction: BaseDataTransaction {
self.isCommitted = true
let result = self.context.saveSynchronously()
let result = self.context.saveSynchronously(waitForMerge: true)
self.result = result
return result
}
/**
Saves the transaction changes and waits for completion synchronously. This method should not be used after the `commit()` or `commitAndWait()` method was already called once.
- Important: Unlike `SynchronousDataTransaction.commitAndWait()`, this method does not wait for observers to be notified of the changes before returning. This results in lower risk for deadlocks, but the updated data may not have been propagated to the `DataStack` after returning.
- returns: a `SaveResult` containing the success or failure information
*/
public func commit() -> SaveResult {
CoreStore.assert(
self.transactionQueue.isCurrentExecutionContext(),
"Attempted to commit a \(cs_typeName(self)) outside its designated queue."
)
CoreStore.assert(
!self.isCommitted,
"Attempted to commit a \(cs_typeName(self)) more than once."
)
self.isCommitted = true
let result = self.context.saveSynchronously(waitForMerge: false)
self.result = result
return result
}
/**
Begins a child transaction synchronously where `NSManagedObject` creates, updates, and deletes can be made. This method should not be used after the `commit()` method was already called once.

View File

@@ -58,7 +58,7 @@ public final class UnsafeDataTransaction: BaseDataTransaction {
*/
public func commitAndWait() -> SaveResult {
let result = self.context.saveSynchronously()
let result = self.context.saveSynchronously(waitForMerge: true)
self.result = result
return result
}