├── CoreDataExample.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ ├── xcshareddata │ │ └── IDEWorkspaceChecks.plist │ └── xcuserdata │ │ └── deda.xcuserdatad │ │ └── UserInterfaceState.xcuserstate └── xcuserdata │ └── deda.xcuserdatad │ ├── xcdebugger │ └── Breakpoints_v2.xcbkptlist │ └── xcschemes │ └── xcschememanagement.plist ├── CoreDataExample ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── ContentView.swift ├── CoreDataExample.xcdatamodeld │ ├── .xccurrentversion │ └── CoreDataExample.xcdatamodel │ │ └── contents ├── CoreDataExampleApp.swift ├── Info.plist └── Source │ ├── CoreDataDeleteModelPublisher.swift │ ├── CoreDataFetchResultsPublisher.swift │ ├── CoreDataSaveModelPublisher.swift │ └── CoreDataStore.swift ├── CoreDataExampleTests ├── CoreDataExampleTests.swift └── Info.plist ├── CoreDataExampleUITests ├── CoreDataExampleUITests.swift └── Info.plist ├── README.md └── Simulator Screen Shot - iPhone 11 Pro - 2020-11-19 at 21.00.11.png /CoreDataExample.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 8FC08561256595E9000FBF9E /* CoreDataStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FC08560256595E9000FBF9E /* CoreDataStore.swift */; }; 11 | 8FEE5C9725658CF300472077 /* CoreDataExampleApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FEE5C9625658CF300472077 /* CoreDataExampleApp.swift */; }; 12 | 8FEE5C9925658CF300472077 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FEE5C9825658CF300472077 /* ContentView.swift */; }; 13 | 8FEE5C9B25658CF400472077 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 8FEE5C9A25658CF400472077 /* Assets.xcassets */; }; 14 | 8FEE5CA325658CF400472077 /* CoreDataExample.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 8FEE5CA125658CF400472077 /* CoreDataExample.xcdatamodeld */; }; 15 | 8FEE5CAE25658CF400472077 /* CoreDataExampleTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FEE5CAD25658CF400472077 /* CoreDataExampleTests.swift */; }; 16 | 8FEE5CB925658CF400472077 /* CoreDataExampleUITests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FEE5CB825658CF400472077 /* CoreDataExampleUITests.swift */; }; 17 | 8FFD6D472566F030005F41C3 /* CoreDataFetchResultsPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FFD6D462566F030005F41C3 /* CoreDataFetchResultsPublisher.swift */; }; 18 | 8FFD6D4C2566F052005F41C3 /* CoreDataDeleteModelPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FFD6D4B2566F052005F41C3 /* CoreDataDeleteModelPublisher.swift */; }; 19 | 8FFD6D512566F70A005F41C3 /* CoreDataSaveModelPublisher.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8FFD6D502566F70A005F41C3 /* CoreDataSaveModelPublisher.swift */; }; 20 | /* End PBXBuildFile section */ 21 | 22 | /* Begin PBXContainerItemProxy section */ 23 | 8FEE5CAA25658CF400472077 /* PBXContainerItemProxy */ = { 24 | isa = PBXContainerItemProxy; 25 | containerPortal = 8FEE5C8B25658CF300472077 /* Project object */; 26 | proxyType = 1; 27 | remoteGlobalIDString = 8FEE5C9225658CF300472077; 28 | remoteInfo = CoreDataExample; 29 | }; 30 | 8FEE5CB525658CF400472077 /* PBXContainerItemProxy */ = { 31 | isa = PBXContainerItemProxy; 32 | containerPortal = 8FEE5C8B25658CF300472077 /* Project object */; 33 | proxyType = 1; 34 | remoteGlobalIDString = 8FEE5C9225658CF300472077; 35 | remoteInfo = CoreDataExample; 36 | }; 37 | /* End PBXContainerItemProxy section */ 38 | 39 | /* Begin PBXFileReference section */ 40 | 8FC08560256595E9000FBF9E /* CoreDataStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataStore.swift; sourceTree = ""; }; 41 | 8FEE5C9325658CF300472077 /* CoreDataExample.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = CoreDataExample.app; sourceTree = BUILT_PRODUCTS_DIR; }; 42 | 8FEE5C9625658CF300472077 /* CoreDataExampleApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataExampleApp.swift; sourceTree = ""; }; 43 | 8FEE5C9825658CF300472077 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 44 | 8FEE5C9A25658CF400472077 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 45 | 8FEE5CA225658CF400472077 /* CoreDataExample.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = CoreDataExample.xcdatamodel; sourceTree = ""; }; 46 | 8FEE5CA425658CF400472077 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 47 | 8FEE5CA925658CF400472077 /* CoreDataExampleTests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CoreDataExampleTests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 48 | 8FEE5CAD25658CF400472077 /* CoreDataExampleTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataExampleTests.swift; sourceTree = ""; }; 49 | 8FEE5CAF25658CF400472077 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 50 | 8FEE5CB425658CF400472077 /* CoreDataExampleUITests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = CoreDataExampleUITests.xctest; sourceTree = BUILT_PRODUCTS_DIR; }; 51 | 8FEE5CB825658CF400472077 /* CoreDataExampleUITests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataExampleUITests.swift; sourceTree = ""; }; 52 | 8FEE5CBA25658CF400472077 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 53 | 8FFD6D462566F030005F41C3 /* CoreDataFetchResultsPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataFetchResultsPublisher.swift; sourceTree = ""; }; 54 | 8FFD6D4B2566F052005F41C3 /* CoreDataDeleteModelPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataDeleteModelPublisher.swift; sourceTree = ""; }; 55 | 8FFD6D502566F70A005F41C3 /* CoreDataSaveModelPublisher.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataSaveModelPublisher.swift; sourceTree = ""; }; 56 | /* End PBXFileReference section */ 57 | 58 | /* Begin PBXFrameworksBuildPhase section */ 59 | 8FEE5C9025658CF300472077 /* Frameworks */ = { 60 | isa = PBXFrameworksBuildPhase; 61 | buildActionMask = 2147483647; 62 | files = ( 63 | ); 64 | runOnlyForDeploymentPostprocessing = 0; 65 | }; 66 | 8FEE5CA625658CF400472077 /* Frameworks */ = { 67 | isa = PBXFrameworksBuildPhase; 68 | buildActionMask = 2147483647; 69 | files = ( 70 | ); 71 | runOnlyForDeploymentPostprocessing = 0; 72 | }; 73 | 8FEE5CB125658CF400472077 /* Frameworks */ = { 74 | isa = PBXFrameworksBuildPhase; 75 | buildActionMask = 2147483647; 76 | files = ( 77 | ); 78 | runOnlyForDeploymentPostprocessing = 0; 79 | }; 80 | /* End PBXFrameworksBuildPhase section */ 81 | 82 | /* Begin PBXGroup section */ 83 | 8F8770192567018400636B0F /* Source */ = { 84 | isa = PBXGroup; 85 | children = ( 86 | 8FC08560256595E9000FBF9E /* CoreDataStore.swift */, 87 | 8FFD6D462566F030005F41C3 /* CoreDataFetchResultsPublisher.swift */, 88 | 8FFD6D4B2566F052005F41C3 /* CoreDataDeleteModelPublisher.swift */, 89 | 8FFD6D502566F70A005F41C3 /* CoreDataSaveModelPublisher.swift */, 90 | ); 91 | path = Source; 92 | sourceTree = ""; 93 | }; 94 | 8FEE5C8A25658CF300472077 = { 95 | isa = PBXGroup; 96 | children = ( 97 | 8FEE5C9525658CF300472077 /* CoreDataExample */, 98 | 8FEE5CAC25658CF400472077 /* CoreDataExampleTests */, 99 | 8FEE5CB725658CF400472077 /* CoreDataExampleUITests */, 100 | 8FEE5C9425658CF300472077 /* Products */, 101 | ); 102 | sourceTree = ""; 103 | }; 104 | 8FEE5C9425658CF300472077 /* Products */ = { 105 | isa = PBXGroup; 106 | children = ( 107 | 8FEE5C9325658CF300472077 /* CoreDataExample.app */, 108 | 8FEE5CA925658CF400472077 /* CoreDataExampleTests.xctest */, 109 | 8FEE5CB425658CF400472077 /* CoreDataExampleUITests.xctest */, 110 | ); 111 | name = Products; 112 | sourceTree = ""; 113 | }; 114 | 8FEE5C9525658CF300472077 /* CoreDataExample */ = { 115 | isa = PBXGroup; 116 | children = ( 117 | 8F8770192567018400636B0F /* Source */, 118 | 8FEE5C9625658CF300472077 /* CoreDataExampleApp.swift */, 119 | 8FEE5C9825658CF300472077 /* ContentView.swift */, 120 | 8FEE5C9A25658CF400472077 /* Assets.xcassets */, 121 | 8FEE5CA425658CF400472077 /* Info.plist */, 122 | 8FEE5CA125658CF400472077 /* CoreDataExample.xcdatamodeld */, 123 | ); 124 | path = CoreDataExample; 125 | sourceTree = ""; 126 | }; 127 | 8FEE5CAC25658CF400472077 /* CoreDataExampleTests */ = { 128 | isa = PBXGroup; 129 | children = ( 130 | 8FEE5CAD25658CF400472077 /* CoreDataExampleTests.swift */, 131 | 8FEE5CAF25658CF400472077 /* Info.plist */, 132 | ); 133 | path = CoreDataExampleTests; 134 | sourceTree = ""; 135 | }; 136 | 8FEE5CB725658CF400472077 /* CoreDataExampleUITests */ = { 137 | isa = PBXGroup; 138 | children = ( 139 | 8FEE5CB825658CF400472077 /* CoreDataExampleUITests.swift */, 140 | 8FEE5CBA25658CF400472077 /* Info.plist */, 141 | ); 142 | path = CoreDataExampleUITests; 143 | sourceTree = ""; 144 | }; 145 | /* End PBXGroup section */ 146 | 147 | /* Begin PBXNativeTarget section */ 148 | 8FEE5C9225658CF300472077 /* CoreDataExample */ = { 149 | isa = PBXNativeTarget; 150 | buildConfigurationList = 8FEE5CBD25658CF400472077 /* Build configuration list for PBXNativeTarget "CoreDataExample" */; 151 | buildPhases = ( 152 | 8FEE5C8F25658CF300472077 /* Sources */, 153 | 8FEE5C9025658CF300472077 /* Frameworks */, 154 | 8FEE5C9125658CF300472077 /* Resources */, 155 | ); 156 | buildRules = ( 157 | ); 158 | dependencies = ( 159 | ); 160 | name = CoreDataExample; 161 | productName = CoreDataExample; 162 | productReference = 8FEE5C9325658CF300472077 /* CoreDataExample.app */; 163 | productType = "com.apple.product-type.application"; 164 | }; 165 | 8FEE5CA825658CF400472077 /* CoreDataExampleTests */ = { 166 | isa = PBXNativeTarget; 167 | buildConfigurationList = 8FEE5CC025658CF400472077 /* Build configuration list for PBXNativeTarget "CoreDataExampleTests" */; 168 | buildPhases = ( 169 | 8FEE5CA525658CF400472077 /* Sources */, 170 | 8FEE5CA625658CF400472077 /* Frameworks */, 171 | 8FEE5CA725658CF400472077 /* Resources */, 172 | ); 173 | buildRules = ( 174 | ); 175 | dependencies = ( 176 | 8FEE5CAB25658CF400472077 /* PBXTargetDependency */, 177 | ); 178 | name = CoreDataExampleTests; 179 | productName = CoreDataExampleTests; 180 | productReference = 8FEE5CA925658CF400472077 /* CoreDataExampleTests.xctest */; 181 | productType = "com.apple.product-type.bundle.unit-test"; 182 | }; 183 | 8FEE5CB325658CF400472077 /* CoreDataExampleUITests */ = { 184 | isa = PBXNativeTarget; 185 | buildConfigurationList = 8FEE5CC325658CF400472077 /* Build configuration list for PBXNativeTarget "CoreDataExampleUITests" */; 186 | buildPhases = ( 187 | 8FEE5CB025658CF400472077 /* Sources */, 188 | 8FEE5CB125658CF400472077 /* Frameworks */, 189 | 8FEE5CB225658CF400472077 /* Resources */, 190 | ); 191 | buildRules = ( 192 | ); 193 | dependencies = ( 194 | 8FEE5CB625658CF400472077 /* PBXTargetDependency */, 195 | ); 196 | name = CoreDataExampleUITests; 197 | productName = CoreDataExampleUITests; 198 | productReference = 8FEE5CB425658CF400472077 /* CoreDataExampleUITests.xctest */; 199 | productType = "com.apple.product-type.bundle.ui-testing"; 200 | }; 201 | /* End PBXNativeTarget section */ 202 | 203 | /* Begin PBXProject section */ 204 | 8FEE5C8B25658CF300472077 /* Project object */ = { 205 | isa = PBXProject; 206 | attributes = { 207 | LastSwiftUpdateCheck = 1200; 208 | LastUpgradeCheck = 1200; 209 | TargetAttributes = { 210 | 8FEE5C9225658CF300472077 = { 211 | CreatedOnToolsVersion = 12.0.1; 212 | }; 213 | 8FEE5CA825658CF400472077 = { 214 | CreatedOnToolsVersion = 12.0.1; 215 | TestTargetID = 8FEE5C9225658CF300472077; 216 | }; 217 | 8FEE5CB325658CF400472077 = { 218 | CreatedOnToolsVersion = 12.0.1; 219 | TestTargetID = 8FEE5C9225658CF300472077; 220 | }; 221 | }; 222 | }; 223 | buildConfigurationList = 8FEE5C8E25658CF300472077 /* Build configuration list for PBXProject "CoreDataExample" */; 224 | compatibilityVersion = "Xcode 9.3"; 225 | developmentRegion = en; 226 | hasScannedForEncodings = 0; 227 | knownRegions = ( 228 | en, 229 | Base, 230 | ); 231 | mainGroup = 8FEE5C8A25658CF300472077; 232 | productRefGroup = 8FEE5C9425658CF300472077 /* Products */; 233 | projectDirPath = ""; 234 | projectRoot = ""; 235 | targets = ( 236 | 8FEE5C9225658CF300472077 /* CoreDataExample */, 237 | 8FEE5CA825658CF400472077 /* CoreDataExampleTests */, 238 | 8FEE5CB325658CF400472077 /* CoreDataExampleUITests */, 239 | ); 240 | }; 241 | /* End PBXProject section */ 242 | 243 | /* Begin PBXResourcesBuildPhase section */ 244 | 8FEE5C9125658CF300472077 /* Resources */ = { 245 | isa = PBXResourcesBuildPhase; 246 | buildActionMask = 2147483647; 247 | files = ( 248 | 8FEE5C9B25658CF400472077 /* Assets.xcassets in Resources */, 249 | ); 250 | runOnlyForDeploymentPostprocessing = 0; 251 | }; 252 | 8FEE5CA725658CF400472077 /* Resources */ = { 253 | isa = PBXResourcesBuildPhase; 254 | buildActionMask = 2147483647; 255 | files = ( 256 | ); 257 | runOnlyForDeploymentPostprocessing = 0; 258 | }; 259 | 8FEE5CB225658CF400472077 /* Resources */ = { 260 | isa = PBXResourcesBuildPhase; 261 | buildActionMask = 2147483647; 262 | files = ( 263 | ); 264 | runOnlyForDeploymentPostprocessing = 0; 265 | }; 266 | /* End PBXResourcesBuildPhase section */ 267 | 268 | /* Begin PBXSourcesBuildPhase section */ 269 | 8FEE5C8F25658CF300472077 /* Sources */ = { 270 | isa = PBXSourcesBuildPhase; 271 | buildActionMask = 2147483647; 272 | files = ( 273 | 8FFD6D4C2566F052005F41C3 /* CoreDataDeleteModelPublisher.swift in Sources */, 274 | 8FEE5C9925658CF300472077 /* ContentView.swift in Sources */, 275 | 8FEE5C9725658CF300472077 /* CoreDataExampleApp.swift in Sources */, 276 | 8FC08561256595E9000FBF9E /* CoreDataStore.swift in Sources */, 277 | 8FFD6D472566F030005F41C3 /* CoreDataFetchResultsPublisher.swift in Sources */, 278 | 8FFD6D512566F70A005F41C3 /* CoreDataSaveModelPublisher.swift in Sources */, 279 | 8FEE5CA325658CF400472077 /* CoreDataExample.xcdatamodeld in Sources */, 280 | ); 281 | runOnlyForDeploymentPostprocessing = 0; 282 | }; 283 | 8FEE5CA525658CF400472077 /* Sources */ = { 284 | isa = PBXSourcesBuildPhase; 285 | buildActionMask = 2147483647; 286 | files = ( 287 | 8FEE5CAE25658CF400472077 /* CoreDataExampleTests.swift in Sources */, 288 | ); 289 | runOnlyForDeploymentPostprocessing = 0; 290 | }; 291 | 8FEE5CB025658CF400472077 /* Sources */ = { 292 | isa = PBXSourcesBuildPhase; 293 | buildActionMask = 2147483647; 294 | files = ( 295 | 8FEE5CB925658CF400472077 /* CoreDataExampleUITests.swift in Sources */, 296 | ); 297 | runOnlyForDeploymentPostprocessing = 0; 298 | }; 299 | /* End PBXSourcesBuildPhase section */ 300 | 301 | /* Begin PBXTargetDependency section */ 302 | 8FEE5CAB25658CF400472077 /* PBXTargetDependency */ = { 303 | isa = PBXTargetDependency; 304 | target = 8FEE5C9225658CF300472077 /* CoreDataExample */; 305 | targetProxy = 8FEE5CAA25658CF400472077 /* PBXContainerItemProxy */; 306 | }; 307 | 8FEE5CB625658CF400472077 /* PBXTargetDependency */ = { 308 | isa = PBXTargetDependency; 309 | target = 8FEE5C9225658CF300472077 /* CoreDataExample */; 310 | targetProxy = 8FEE5CB525658CF400472077 /* PBXContainerItemProxy */; 311 | }; 312 | /* End PBXTargetDependency section */ 313 | 314 | /* Begin XCBuildConfiguration section */ 315 | 8FEE5CBB25658CF400472077 /* Debug */ = { 316 | isa = XCBuildConfiguration; 317 | buildSettings = { 318 | ALWAYS_SEARCH_USER_PATHS = NO; 319 | CLANG_ANALYZER_NONNULL = YES; 320 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 321 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 322 | CLANG_CXX_LIBRARY = "libc++"; 323 | CLANG_ENABLE_MODULES = YES; 324 | CLANG_ENABLE_OBJC_ARC = YES; 325 | CLANG_ENABLE_OBJC_WEAK = YES; 326 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 327 | CLANG_WARN_BOOL_CONVERSION = YES; 328 | CLANG_WARN_COMMA = YES; 329 | CLANG_WARN_CONSTANT_CONVERSION = YES; 330 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 331 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 332 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 333 | CLANG_WARN_EMPTY_BODY = YES; 334 | CLANG_WARN_ENUM_CONVERSION = YES; 335 | CLANG_WARN_INFINITE_RECURSION = YES; 336 | CLANG_WARN_INT_CONVERSION = YES; 337 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 338 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 339 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 340 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 341 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 342 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 343 | CLANG_WARN_STRICT_PROTOTYPES = YES; 344 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 345 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 346 | CLANG_WARN_UNREACHABLE_CODE = YES; 347 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 348 | COPY_PHASE_STRIP = NO; 349 | DEBUG_INFORMATION_FORMAT = dwarf; 350 | ENABLE_STRICT_OBJC_MSGSEND = YES; 351 | ENABLE_TESTABILITY = YES; 352 | GCC_C_LANGUAGE_STANDARD = gnu11; 353 | GCC_DYNAMIC_NO_PIC = NO; 354 | GCC_NO_COMMON_BLOCKS = YES; 355 | GCC_OPTIMIZATION_LEVEL = 0; 356 | GCC_PREPROCESSOR_DEFINITIONS = ( 357 | "DEBUG=1", 358 | "$(inherited)", 359 | ); 360 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 361 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 362 | GCC_WARN_UNDECLARED_SELECTOR = YES; 363 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 364 | GCC_WARN_UNUSED_FUNCTION = YES; 365 | GCC_WARN_UNUSED_VARIABLE = YES; 366 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 367 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 368 | MTL_FAST_MATH = YES; 369 | ONLY_ACTIVE_ARCH = YES; 370 | SDKROOT = iphoneos; 371 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 372 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 373 | }; 374 | name = Debug; 375 | }; 376 | 8FEE5CBC25658CF400472077 /* Release */ = { 377 | isa = XCBuildConfiguration; 378 | buildSettings = { 379 | ALWAYS_SEARCH_USER_PATHS = NO; 380 | CLANG_ANALYZER_NONNULL = YES; 381 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 382 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 383 | CLANG_CXX_LIBRARY = "libc++"; 384 | CLANG_ENABLE_MODULES = YES; 385 | CLANG_ENABLE_OBJC_ARC = YES; 386 | CLANG_ENABLE_OBJC_WEAK = YES; 387 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 388 | CLANG_WARN_BOOL_CONVERSION = YES; 389 | CLANG_WARN_COMMA = YES; 390 | CLANG_WARN_CONSTANT_CONVERSION = YES; 391 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 392 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 393 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 394 | CLANG_WARN_EMPTY_BODY = YES; 395 | CLANG_WARN_ENUM_CONVERSION = YES; 396 | CLANG_WARN_INFINITE_RECURSION = YES; 397 | CLANG_WARN_INT_CONVERSION = YES; 398 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 399 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 400 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 401 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 402 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 403 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 404 | CLANG_WARN_STRICT_PROTOTYPES = YES; 405 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 406 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 407 | CLANG_WARN_UNREACHABLE_CODE = YES; 408 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 409 | COPY_PHASE_STRIP = NO; 410 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 411 | ENABLE_NS_ASSERTIONS = NO; 412 | ENABLE_STRICT_OBJC_MSGSEND = YES; 413 | GCC_C_LANGUAGE_STANDARD = gnu11; 414 | GCC_NO_COMMON_BLOCKS = YES; 415 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 416 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 417 | GCC_WARN_UNDECLARED_SELECTOR = YES; 418 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 419 | GCC_WARN_UNUSED_FUNCTION = YES; 420 | GCC_WARN_UNUSED_VARIABLE = YES; 421 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 422 | MTL_ENABLE_DEBUG_INFO = NO; 423 | MTL_FAST_MATH = YES; 424 | SDKROOT = iphoneos; 425 | SWIFT_COMPILATION_MODE = wholemodule; 426 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 427 | VALIDATE_PRODUCT = YES; 428 | }; 429 | name = Release; 430 | }; 431 | 8FEE5CBE25658CF400472077 /* Debug */ = { 432 | isa = XCBuildConfiguration; 433 | buildSettings = { 434 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 435 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 436 | CODE_SIGN_STYLE = Automatic; 437 | ENABLE_PREVIEWS = YES; 438 | INFOPLIST_FILE = CoreDataExample/Info.plist; 439 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 440 | LD_RUNPATH_SEARCH_PATHS = ( 441 | "$(inherited)", 442 | "@executable_path/Frameworks", 443 | ); 444 | PRODUCT_BUNDLE_IDENTIFIER = Bassem.Qoulta.CoreDataExample; 445 | PRODUCT_NAME = "$(TARGET_NAME)"; 446 | SWIFT_VERSION = 5.0; 447 | TARGETED_DEVICE_FAMILY = "1,2"; 448 | }; 449 | name = Debug; 450 | }; 451 | 8FEE5CBF25658CF400472077 /* Release */ = { 452 | isa = XCBuildConfiguration; 453 | buildSettings = { 454 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 455 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 456 | CODE_SIGN_STYLE = Automatic; 457 | ENABLE_PREVIEWS = YES; 458 | INFOPLIST_FILE = CoreDataExample/Info.plist; 459 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 460 | LD_RUNPATH_SEARCH_PATHS = ( 461 | "$(inherited)", 462 | "@executable_path/Frameworks", 463 | ); 464 | PRODUCT_BUNDLE_IDENTIFIER = Bassem.Qoulta.CoreDataExample; 465 | PRODUCT_NAME = "$(TARGET_NAME)"; 466 | SWIFT_VERSION = 5.0; 467 | TARGETED_DEVICE_FAMILY = "1,2"; 468 | }; 469 | name = Release; 470 | }; 471 | 8FEE5CC125658CF400472077 /* Debug */ = { 472 | isa = XCBuildConfiguration; 473 | buildSettings = { 474 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 475 | BUNDLE_LOADER = "$(TEST_HOST)"; 476 | CODE_SIGN_STYLE = Automatic; 477 | INFOPLIST_FILE = CoreDataExampleTests/Info.plist; 478 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 479 | LD_RUNPATH_SEARCH_PATHS = ( 480 | "$(inherited)", 481 | "@executable_path/Frameworks", 482 | "@loader_path/Frameworks", 483 | ); 484 | PRODUCT_BUNDLE_IDENTIFIER = Bassem.Qoulta.CoreDataExampleTests; 485 | PRODUCT_NAME = "$(TARGET_NAME)"; 486 | SWIFT_VERSION = 5.0; 487 | TARGETED_DEVICE_FAMILY = "1,2"; 488 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CoreDataExample.app/CoreDataExample"; 489 | }; 490 | name = Debug; 491 | }; 492 | 8FEE5CC225658CF400472077 /* Release */ = { 493 | isa = XCBuildConfiguration; 494 | buildSettings = { 495 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 496 | BUNDLE_LOADER = "$(TEST_HOST)"; 497 | CODE_SIGN_STYLE = Automatic; 498 | INFOPLIST_FILE = CoreDataExampleTests/Info.plist; 499 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 500 | LD_RUNPATH_SEARCH_PATHS = ( 501 | "$(inherited)", 502 | "@executable_path/Frameworks", 503 | "@loader_path/Frameworks", 504 | ); 505 | PRODUCT_BUNDLE_IDENTIFIER = Bassem.Qoulta.CoreDataExampleTests; 506 | PRODUCT_NAME = "$(TARGET_NAME)"; 507 | SWIFT_VERSION = 5.0; 508 | TARGETED_DEVICE_FAMILY = "1,2"; 509 | TEST_HOST = "$(BUILT_PRODUCTS_DIR)/CoreDataExample.app/CoreDataExample"; 510 | }; 511 | name = Release; 512 | }; 513 | 8FEE5CC425658CF400472077 /* Debug */ = { 514 | isa = XCBuildConfiguration; 515 | buildSettings = { 516 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 517 | CODE_SIGN_STYLE = Automatic; 518 | INFOPLIST_FILE = CoreDataExampleUITests/Info.plist; 519 | LD_RUNPATH_SEARCH_PATHS = ( 520 | "$(inherited)", 521 | "@executable_path/Frameworks", 522 | "@loader_path/Frameworks", 523 | ); 524 | PRODUCT_BUNDLE_IDENTIFIER = Bassem.Qoulta.CoreDataExampleUITests; 525 | PRODUCT_NAME = "$(TARGET_NAME)"; 526 | SWIFT_VERSION = 5.0; 527 | TARGETED_DEVICE_FAMILY = "1,2"; 528 | TEST_TARGET_NAME = CoreDataExample; 529 | }; 530 | name = Debug; 531 | }; 532 | 8FEE5CC525658CF400472077 /* Release */ = { 533 | isa = XCBuildConfiguration; 534 | buildSettings = { 535 | ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; 536 | CODE_SIGN_STYLE = Automatic; 537 | INFOPLIST_FILE = CoreDataExampleUITests/Info.plist; 538 | LD_RUNPATH_SEARCH_PATHS = ( 539 | "$(inherited)", 540 | "@executable_path/Frameworks", 541 | "@loader_path/Frameworks", 542 | ); 543 | PRODUCT_BUNDLE_IDENTIFIER = Bassem.Qoulta.CoreDataExampleUITests; 544 | PRODUCT_NAME = "$(TARGET_NAME)"; 545 | SWIFT_VERSION = 5.0; 546 | TARGETED_DEVICE_FAMILY = "1,2"; 547 | TEST_TARGET_NAME = CoreDataExample; 548 | }; 549 | name = Release; 550 | }; 551 | /* End XCBuildConfiguration section */ 552 | 553 | /* Begin XCConfigurationList section */ 554 | 8FEE5C8E25658CF300472077 /* Build configuration list for PBXProject "CoreDataExample" */ = { 555 | isa = XCConfigurationList; 556 | buildConfigurations = ( 557 | 8FEE5CBB25658CF400472077 /* Debug */, 558 | 8FEE5CBC25658CF400472077 /* Release */, 559 | ); 560 | defaultConfigurationIsVisible = 0; 561 | defaultConfigurationName = Release; 562 | }; 563 | 8FEE5CBD25658CF400472077 /* Build configuration list for PBXNativeTarget "CoreDataExample" */ = { 564 | isa = XCConfigurationList; 565 | buildConfigurations = ( 566 | 8FEE5CBE25658CF400472077 /* Debug */, 567 | 8FEE5CBF25658CF400472077 /* Release */, 568 | ); 569 | defaultConfigurationIsVisible = 0; 570 | defaultConfigurationName = Release; 571 | }; 572 | 8FEE5CC025658CF400472077 /* Build configuration list for PBXNativeTarget "CoreDataExampleTests" */ = { 573 | isa = XCConfigurationList; 574 | buildConfigurations = ( 575 | 8FEE5CC125658CF400472077 /* Debug */, 576 | 8FEE5CC225658CF400472077 /* Release */, 577 | ); 578 | defaultConfigurationIsVisible = 0; 579 | defaultConfigurationName = Release; 580 | }; 581 | 8FEE5CC325658CF400472077 /* Build configuration list for PBXNativeTarget "CoreDataExampleUITests" */ = { 582 | isa = XCConfigurationList; 583 | buildConfigurations = ( 584 | 8FEE5CC425658CF400472077 /* Debug */, 585 | 8FEE5CC525658CF400472077 /* Release */, 586 | ); 587 | defaultConfigurationIsVisible = 0; 588 | defaultConfigurationName = Release; 589 | }; 590 | /* End XCConfigurationList section */ 591 | 592 | /* Begin XCVersionGroup section */ 593 | 8FEE5CA125658CF400472077 /* CoreDataExample.xcdatamodeld */ = { 594 | isa = XCVersionGroup; 595 | children = ( 596 | 8FEE5CA225658CF400472077 /* CoreDataExample.xcdatamodel */, 597 | ); 598 | currentVersion = 8FEE5CA225658CF400472077 /* CoreDataExample.xcdatamodel */; 599 | path = CoreDataExample.xcdatamodeld; 600 | sourceTree = ""; 601 | versionGroupType = wrapper.xcdatamodel; 602 | }; 603 | /* End XCVersionGroup section */ 604 | }; 605 | rootObject = 8FEE5C8B25658CF300472077 /* Project object */; 606 | } 607 | -------------------------------------------------------------------------------- /CoreDataExample.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /CoreDataExample.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /CoreDataExample.xcodeproj/project.xcworkspace/xcuserdata/deda.xcuserdatad/UserInterfaceState.xcuserstate: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deda9/CoreData-Combine/f7a0c6a381b7c8b2b8e8c506078776bf7330c197/CoreDataExample.xcodeproj/project.xcworkspace/xcuserdata/deda.xcuserdatad/UserInterfaceState.xcuserstate -------------------------------------------------------------------------------- /CoreDataExample.xcodeproj/xcuserdata/deda.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist: -------------------------------------------------------------------------------- 1 | 2 | 6 | 7 | -------------------------------------------------------------------------------- /CoreDataExample.xcodeproj/xcuserdata/deda.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | CoreDataExample.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 0 11 | 12 | 13 | 14 | 15 | -------------------------------------------------------------------------------- /CoreDataExample/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /CoreDataExample/Assets.xcassets/AppIcon.appiconset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "iphone", 5 | "scale" : "2x", 6 | "size" : "20x20" 7 | }, 8 | { 9 | "idiom" : "iphone", 10 | "scale" : "3x", 11 | "size" : "20x20" 12 | }, 13 | { 14 | "idiom" : "iphone", 15 | "scale" : "2x", 16 | "size" : "29x29" 17 | }, 18 | { 19 | "idiom" : "iphone", 20 | "scale" : "3x", 21 | "size" : "29x29" 22 | }, 23 | { 24 | "idiom" : "iphone", 25 | "scale" : "2x", 26 | "size" : "40x40" 27 | }, 28 | { 29 | "idiom" : "iphone", 30 | "scale" : "3x", 31 | "size" : "40x40" 32 | }, 33 | { 34 | "idiom" : "iphone", 35 | "scale" : "2x", 36 | "size" : "60x60" 37 | }, 38 | { 39 | "idiom" : "iphone", 40 | "scale" : "3x", 41 | "size" : "60x60" 42 | }, 43 | { 44 | "idiom" : "ipad", 45 | "scale" : "1x", 46 | "size" : "20x20" 47 | }, 48 | { 49 | "idiom" : "ipad", 50 | "scale" : "2x", 51 | "size" : "20x20" 52 | }, 53 | { 54 | "idiom" : "ipad", 55 | "scale" : "1x", 56 | "size" : "29x29" 57 | }, 58 | { 59 | "idiom" : "ipad", 60 | "scale" : "2x", 61 | "size" : "29x29" 62 | }, 63 | { 64 | "idiom" : "ipad", 65 | "scale" : "1x", 66 | "size" : "40x40" 67 | }, 68 | { 69 | "idiom" : "ipad", 70 | "scale" : "2x", 71 | "size" : "40x40" 72 | }, 73 | { 74 | "idiom" : "ipad", 75 | "scale" : "1x", 76 | "size" : "76x76" 77 | }, 78 | { 79 | "idiom" : "ipad", 80 | "scale" : "2x", 81 | "size" : "76x76" 82 | }, 83 | { 84 | "idiom" : "ipad", 85 | "scale" : "2x", 86 | "size" : "83.5x83.5" 87 | }, 88 | { 89 | "idiom" : "ios-marketing", 90 | "scale" : "1x", 91 | "size" : "1024x1024" 92 | } 93 | ], 94 | "info" : { 95 | "author" : "xcode", 96 | "version" : 1 97 | } 98 | } 99 | -------------------------------------------------------------------------------- /CoreDataExample/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /CoreDataExample/ContentView.swift: -------------------------------------------------------------------------------- 1 | import SwiftUI 2 | import CoreData 3 | import Combine 4 | 5 | var bag: [AnyCancellable] = [] 6 | 7 | struct ContentView: View { 8 | let coreDataStore: CoreDataStoring! 9 | 10 | @State private var number_of_persons: Int = 0 11 | @State private var message: String = "None" 12 | 13 | var body: some View { 14 | Text("Message") 15 | Text("\(message)").foregroundColor(.green) 16 | Text("number of persons \(number_of_persons)") 17 | .padding() 18 | 19 | Button(action: addPerson) { 20 | Label("Add Person", systemImage: "plus") 21 | }.padding() 22 | 23 | Button(action: fetchPersons) { 24 | Text("Fetch Persons Count") 25 | }.padding() 26 | 27 | Button(action: deleteAllPersons) { 28 | Text("Delete All Persons") 29 | }.padding() 30 | 31 | } 32 | 33 | private func addPerson() { 34 | let action: Action = { 35 | let bezo: Person = coreDataStore.createEntity() 36 | bezo.first_name = "Deda" 37 | bezo.last_name = "Deda" 38 | 39 | let volksCar: Car = coreDataStore.createEntity() 40 | volksCar.name = "Volkswagen" 41 | volksCar.owner = bezo 42 | 43 | let bmwCar: Car = coreDataStore.createEntity() 44 | bmwCar.name = "BMW" 45 | bmwCar.owner = bezo 46 | 47 | bezo.cars = [volksCar, bmwCar] 48 | } 49 | 50 | coreDataStore 51 | .publicher(save: action) 52 | .sink { completion in 53 | if case .failure(let error) = completion { 54 | message = error.localizedDescription 55 | } 56 | } receiveValue: { success in 57 | if success { 58 | message = "Saving entities succeeded" 59 | number_of_persons += 1 60 | } 61 | } 62 | .store(in: &bag) 63 | } 64 | 65 | private func fetchPersons() { 66 | let request = NSFetchRequest(entityName: Person.entityName) 67 | coreDataStore 68 | .publicher(fetch: request) 69 | .sink { completion in 70 | if case .failure(let error) = completion { 71 | message = error.localizedDescription 72 | } 73 | } receiveValue: { persons in 74 | message = "Fetching entities succeeded" 75 | number_of_persons = persons.count 76 | } 77 | .store(in: &bag) 78 | } 79 | 80 | private func deleteAllPersons() { 81 | let request = NSFetchRequest(entityName: Person.entityName) 82 | request.predicate = NSPredicate(format: "first_name LIKE[cd] %@", "Deda") 83 | coreDataStore 84 | .publicher(delete: request) 85 | .sink { completion in 86 | if case .failure(let error) = completion { 87 | message = error.localizedDescription 88 | } 89 | } receiveValue: { _ in 90 | message = "Deleting entities succeeded" 91 | number_of_persons = 0 92 | } 93 | .store(in: &bag) 94 | 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /CoreDataExample/CoreDataExample.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | CoreDataExample.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /CoreDataExample/CoreDataExample.xcdatamodeld/CoreDataExample.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | -------------------------------------------------------------------------------- /CoreDataExample/CoreDataExampleApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoreDataExampleApp.swift 3 | // CoreDataExample 4 | // 5 | // Created by Deda on 18.11.20. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct CoreDataExampleApp: App { 12 | var body: some Scene { 13 | WindowGroup { 14 | ContentView(coreDataStore: CoreDataStore.default) 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /CoreDataExample/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | LSRequiresIPhoneOS 22 | 23 | UIApplicationSceneManifest 24 | 25 | UIApplicationSupportsMultipleScenes 26 | 27 | 28 | UIApplicationSupportsIndirectInputEvents 29 | 30 | UILaunchScreen 31 | 32 | UIRequiredDeviceCapabilities 33 | 34 | armv7 35 | 36 | UISupportedInterfaceOrientations 37 | 38 | UIInterfaceOrientationPortrait 39 | UIInterfaceOrientationLandscapeLeft 40 | UIInterfaceOrientationLandscapeRight 41 | 42 | UISupportedInterfaceOrientations~ipad 43 | 44 | UIInterfaceOrientationPortrait 45 | UIInterfaceOrientationPortraitUpsideDown 46 | UIInterfaceOrientationLandscapeLeft 47 | UIInterfaceOrientationLandscapeRight 48 | 49 | 50 | 51 | -------------------------------------------------------------------------------- /CoreDataExample/Source/CoreDataDeleteModelPublisher.swift: -------------------------------------------------------------------------------- 1 | import Combine 2 | import CoreData 3 | 4 | struct CoreDataDeleteModelPublisher: Publisher { 5 | typealias Output = NSBatchDeleteResult 6 | typealias Failure = NSError 7 | 8 | private let request: NSFetchRequest 9 | private let context: NSManagedObjectContext 10 | 11 | init(delete request: NSFetchRequest, context: NSManagedObjectContext) { 12 | self.request = request 13 | self.context = context 14 | } 15 | 16 | func receive(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input { 17 | let subscription = Subscription(subscriber: subscriber, context: context, request: request) 18 | subscriber.receive(subscription: subscription) 19 | } 20 | } 21 | 22 | extension CoreDataDeleteModelPublisher { 23 | class Subscription where S : Subscriber, Failure == S.Failure, Output == S.Input { 24 | private var subscriber: S? 25 | private let request: NSFetchRequest 26 | private var context: NSManagedObjectContext 27 | 28 | init(subscriber: S, context: NSManagedObjectContext, request: NSFetchRequest) { 29 | self.subscriber = subscriber 30 | self.context = context 31 | self.request = request 32 | } 33 | } 34 | } 35 | 36 | extension CoreDataDeleteModelPublisher.Subscription: Subscription { 37 | func request(_ demand: Subscribers.Demand) { 38 | var demand = demand 39 | guard let subscriber = subscriber, demand > 0 else { return } 40 | 41 | do { 42 | demand -= 1 43 | let batchDeleteRequest = NSBatchDeleteRequest(fetchRequest: self.request) 44 | batchDeleteRequest.resultType = .resultTypeCount 45 | 46 | if let result = try context.execute(batchDeleteRequest) as? NSBatchDeleteResult { 47 | demand += subscriber.receive(result) 48 | } 49 | else { 50 | subscriber.receive(completion: .failure(NSError())) 51 | } 52 | 53 | } catch { 54 | subscriber.receive(completion: .failure(error as NSError)) 55 | } 56 | } 57 | } 58 | 59 | extension CoreDataDeleteModelPublisher.Subscription: Cancellable { 60 | func cancel() { 61 | subscriber = nil 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /CoreDataExample/Source/CoreDataFetchResultsPublisher.swift: -------------------------------------------------------------------------------- 1 | import Combine 2 | import CoreData 3 | 4 | struct CoreDataFetchResultsPublisher: Publisher where Entity: NSManagedObject { 5 | typealias Output = [Entity] 6 | typealias Failure = NSError 7 | 8 | private let request: NSFetchRequest 9 | private let context: NSManagedObjectContext 10 | 11 | init(request: NSFetchRequest, context: NSManagedObjectContext) { 12 | self.request = request 13 | self.context = context 14 | } 15 | 16 | func receive(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input { 17 | let subscription = Subscription(subscriber: subscriber, context: context, request: request) 18 | subscriber.receive(subscription: subscription) 19 | } 20 | } 21 | 22 | extension CoreDataFetchResultsPublisher { 23 | class Subscription where S : Subscriber, Failure == S.Failure, Output == S.Input { 24 | private var subscriber: S? 25 | private var request: NSFetchRequest 26 | private var context: NSManagedObjectContext 27 | 28 | init(subscriber: S, context: NSManagedObjectContext, request: NSFetchRequest) { 29 | self.subscriber = subscriber 30 | self.context = context 31 | self.request = request 32 | } 33 | } 34 | } 35 | 36 | extension CoreDataFetchResultsPublisher.Subscription: Subscription { 37 | func request(_ demand: Subscribers.Demand) { 38 | var demand = demand 39 | guard let subscriber = subscriber, demand > 0 else { return } 40 | do { 41 | demand -= 1 42 | let items = try context.fetch(request) 43 | demand += subscriber.receive(items) 44 | } catch { 45 | subscriber.receive(completion: .failure(error as NSError)) 46 | } 47 | } 48 | } 49 | 50 | extension CoreDataFetchResultsPublisher.Subscription: Cancellable { 51 | func cancel() { 52 | subscriber = nil 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /CoreDataExample/Source/CoreDataSaveModelPublisher.swift: -------------------------------------------------------------------------------- 1 | import Combine 2 | import CoreData 3 | 4 | typealias Action = (()->()) 5 | 6 | struct CoreDataSaveModelPublisher: Publisher { 7 | typealias Output = Bool 8 | typealias Failure = NSError 9 | 10 | private let action: Action 11 | private let context: NSManagedObjectContext 12 | 13 | init(action: @escaping Action, context: NSManagedObjectContext) { 14 | self.action = action 15 | self.context = context 16 | } 17 | 18 | func receive(subscriber: S) where S : Subscriber, Self.Failure == S.Failure, Self.Output == S.Input { 19 | let subscription = Subscription(subscriber: subscriber, context: context, action: action) 20 | subscriber.receive(subscription: subscription) 21 | } 22 | } 23 | 24 | extension CoreDataSaveModelPublisher { 25 | class Subscription where S : Subscriber, Failure == S.Failure, Output == S.Input { 26 | private var subscriber: S? 27 | private let action: Action 28 | private let context: NSManagedObjectContext 29 | 30 | init(subscriber: S, context: NSManagedObjectContext, action: @escaping Action) { 31 | self.subscriber = subscriber 32 | self.context = context 33 | self.action = action 34 | } 35 | } 36 | } 37 | 38 | extension CoreDataSaveModelPublisher.Subscription: Subscription { 39 | func request(_ demand: Subscribers.Demand) { 40 | var demand = demand 41 | guard let subscriber = subscriber, demand > 0 else { return } 42 | 43 | do { 44 | action() 45 | demand -= 1 46 | try context.save() 47 | demand += subscriber.receive(true) 48 | } catch { 49 | subscriber.receive(completion: .failure(error as NSError)) 50 | } 51 | } 52 | } 53 | 54 | extension CoreDataSaveModelPublisher.Subscription: Cancellable { 55 | func cancel() { 56 | subscriber = nil 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /CoreDataExample/Source/CoreDataStore.swift: -------------------------------------------------------------------------------- 1 | import CoreData 2 | 3 | enum StorageType { 4 | case persistent, inMemory 5 | } 6 | 7 | extension NSManagedObject { 8 | class var entityName: String { 9 | return String(describing: self).components(separatedBy: ".").last! 10 | } 11 | } 12 | 13 | protocol EntityCreating { 14 | var viewContext: NSManagedObjectContext { get } 15 | func createEntity() -> T 16 | } 17 | 18 | extension EntityCreating { 19 | func createEntity() -> T { 20 | T(context: viewContext) 21 | } 22 | } 23 | 24 | protocol CoreDataFetchResultsPublishing { 25 | var viewContext: NSManagedObjectContext { get } 26 | func publicher(fetch request: NSFetchRequest) -> CoreDataFetchResultsPublisher 27 | } 28 | 29 | extension CoreDataFetchResultsPublishing { 30 | func publicher(fetch request: NSFetchRequest) -> CoreDataFetchResultsPublisher { 31 | return CoreDataFetchResultsPublisher(request: request, context: viewContext) 32 | } 33 | } 34 | 35 | protocol CoreDataDeleteModelPublishing { 36 | var viewContext: NSManagedObjectContext { get } 37 | func publicher(delete request: NSFetchRequest) -> CoreDataDeleteModelPublisher 38 | } 39 | 40 | extension CoreDataDeleteModelPublishing { 41 | func publicher(delete request: NSFetchRequest) -> CoreDataDeleteModelPublisher { 42 | return CoreDataDeleteModelPublisher(delete: request, context: viewContext) 43 | } 44 | } 45 | 46 | protocol CoreDataSaveModelPublishing { 47 | var viewContext: NSManagedObjectContext { get } 48 | func publicher(save action: @escaping Action) -> CoreDataSaveModelPublisher 49 | } 50 | 51 | extension CoreDataSaveModelPublishing { 52 | func publicher(save action: @escaping Action) -> CoreDataSaveModelPublisher { 53 | return CoreDataSaveModelPublisher(action: action, context: viewContext) 54 | } 55 | } 56 | 57 | protocol CoreDataStoring: EntityCreating, CoreDataFetchResultsPublishing, CoreDataDeleteModelPublishing, CoreDataSaveModelPublishing { 58 | var viewContext: NSManagedObjectContext { get } 59 | } 60 | 61 | class CoreDataStore: CoreDataStoring { 62 | 63 | private let container: NSPersistentContainer 64 | 65 | static var `default`: CoreDataStoring = { 66 | return CoreDataStore(name: "CoreDataExample", in: .persistent) 67 | }() 68 | 69 | var viewContext: NSManagedObjectContext { 70 | return self.container.viewContext 71 | } 72 | 73 | init(name: String, in storageType: StorageType) { 74 | self.container = NSPersistentContainer(name: name) 75 | self.setupIfMemoryStorage(storageType) 76 | self.container.loadPersistentStores { (storeDescription, error) in 77 | if let error = error as NSError? { 78 | fatalError("Unresolved error \(error), \(error.userInfo)") 79 | } 80 | } 81 | } 82 | 83 | private func setupIfMemoryStorage(_ storageType: StorageType) { 84 | if storageType == .inMemory { 85 | let description = NSPersistentStoreDescription() 86 | description.url = URL(fileURLWithPath: "/dev/null") 87 | self.container.persistentStoreDescriptions = [description] 88 | } 89 | } 90 | } 91 | -------------------------------------------------------------------------------- /CoreDataExampleTests/CoreDataExampleTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoreDataExampleTests.swift 3 | // CoreDataExampleTests 4 | // 5 | // Created by Deda on 18.11.20. 6 | // 7 | 8 | import XCTest 9 | @testable import CoreDataExample 10 | 11 | class CoreDataExampleTests: XCTestCase { 12 | 13 | override func setUpWithError() throws { 14 | // Put setup code here. This method is called before the invocation of each test method in the class. 15 | } 16 | 17 | override func tearDownWithError() throws { 18 | // Put teardown code here. This method is called after the invocation of each test method in the class. 19 | } 20 | 21 | func testExample() throws { 22 | // This is an example of a functional test case. 23 | // Use XCTAssert and related functions to verify your tests produce the correct results. 24 | } 25 | 26 | func testPerformanceExample() throws { 27 | // This is an example of a performance test case. 28 | self.measure { 29 | // Put the code you want to measure the time of here. 30 | } 31 | } 32 | 33 | } 34 | -------------------------------------------------------------------------------- /CoreDataExampleTests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /CoreDataExampleUITests/CoreDataExampleUITests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CoreDataExampleUITests.swift 3 | // CoreDataExampleUITests 4 | // 5 | // Created by Deda on 18.11.20. 6 | // 7 | 8 | import XCTest 9 | 10 | class CoreDataExampleUITests: XCTestCase { 11 | 12 | override func setUpWithError() throws { 13 | // Put setup code here. This method is called before the invocation of each test method in the class. 14 | 15 | // In UI tests it is usually best to stop immediately when a failure occurs. 16 | continueAfterFailure = false 17 | 18 | // In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this. 19 | } 20 | 21 | override func tearDownWithError() throws { 22 | // Put teardown code here. This method is called after the invocation of each test method in the class. 23 | } 24 | 25 | func testExample() throws { 26 | // UI tests must launch the application that they test. 27 | let app = XCUIApplication() 28 | app.launch() 29 | 30 | // Use recording to get started writing UI tests. 31 | // Use XCTAssert and related functions to verify your tests produce the correct results. 32 | } 33 | 34 | func testLaunchPerformance() throws { 35 | if #available(macOS 10.15, iOS 13.0, tvOS 13.0, *) { 36 | // This measures how long it takes to launch your application. 37 | measure(metrics: [XCTApplicationLaunchMetric()]) { 38 | XCUIApplication().launch() 39 | } 40 | } 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /CoreDataExampleUITests/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIdentifier 10 | $(PRODUCT_BUNDLE_IDENTIFIER) 11 | CFBundleInfoDictionaryVersion 12 | 6.0 13 | CFBundleName 14 | $(PRODUCT_NAME) 15 | CFBundlePackageType 16 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 17 | CFBundleShortVersionString 18 | 1.0 19 | CFBundleVersion 20 | 1 21 | 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # CoreDataExample 2 | 3 | 4 | 5 | 6 | ## We learn how to save entity, delete or fetch from CoreData by Combine 7 | 8 | 9 | ## Save 10 | ```Swift 11 | private func addPerson() { 12 | let action: Action = { 13 | let bezo: Person = coreDataStore.createEntity() 14 | bezo.first_name = "Bezo" 15 | bezo.last_name = "Deda" 16 | 17 | let volksCar: Car = coreDataStore.createEntity() 18 | volksCar.name = "Volkswagen" 19 | volksCar.owner = bezo 20 | 21 | let bmwCar: Car = coreDataStore.createEntity() 22 | bmwCar.name = "BMW" 23 | bmwCar.owner = bezo 24 | 25 | bezo.cars = [volksCar, bmwCar] 26 | } 27 | 28 | coreDataStore 29 | .publicher(save: action) 30 | .sink { completion in 31 | if case .failure(let error) = completion { 32 | message = error.localizedDescription 33 | } 34 | } receiveValue: { success in 35 | if success { 36 | message = "Saving entities succeeded" 37 | number_of_persons += 1 38 | } 39 | } 40 | .store(in: &bag) 41 | } 42 | ``` 43 | 44 | ## Fetch 45 | ```Swift 46 | private func fetchPersons() { 47 | let request = NSFetchRequest(entityName: Person.entityName) 48 | coreDataStore 49 | .publicher(fetch: request) 50 | .sink { completion in 51 | if case .failure(let error) = completion { 52 | message = error.localizedDescription 53 | } 54 | } receiveValue: { persons in 55 | message = "Fetching entities succeeded" 56 | number_of_persons = persons.count 57 | } 58 | .store(in: &bag) 59 | } 60 | ``` 61 | 62 | ## Delete 63 | ```Swift 64 | private func deleteAllPersons() { 65 | let request = NSFetchRequest(entityName: Person.entityName) 66 | request.predicate = NSPredicate(format: "first_name LIKE[cd] %@", "Deda") 67 | coreDataStore 68 | .publicher(delete: request) 69 | .sink { completion in 70 | if case .failure(let error) = completion { 71 | message = error.localizedDescription 72 | } 73 | } receiveValue: { _ in 74 | message = "Deleting entities succeeded" 75 | number_of_persons = 0 76 | } 77 | .store(in: &bag) 78 | 79 | } 80 | ``` 81 | 82 | 83 | - You can read the tutorial on Medium [Tutorial Link](https://deda9.medium.com/ios-core-data-with-combine-c80373c5484) 84 | -------------------------------------------------------------------------------- /Simulator Screen Shot - iPhone 11 Pro - 2020-11-19 at 21.00.11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/deda9/CoreData-Combine/f7a0c6a381b7c8b2b8e8c506078776bf7330c197/Simulator Screen Shot - iPhone 11 Pro - 2020-11-19 at 21.00.11.png --------------------------------------------------------------------------------