├── Core Data Super Ultra Fun Fest.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ └── IDEWorkspaceChecks.plist └── xcuserdata │ └── dan.xcuserdatad │ └── xcschemes │ └── xcschememanagement.plist ├── README.md ├── Shared ├── Assets.xcassets │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ └── Contents.json │ └── Contents.json ├── ContentView.swift ├── Core_Data_Super_Ultra_Fun_FestApp.swift ├── Counter.swift ├── Model.xcdatamodeld │ └── Model.xcdatamodel │ │ └── contents └── ViewModel.swift ├── iOS └── Info.plist └── macOS ├── Info.plist └── macOS.entitlements /Core Data Super Ultra Fun Fest.xcodeproj/project.pbxproj: -------------------------------------------------------------------------------- 1 | // !$*UTF8*$! 2 | { 3 | archiveVersion = 1; 4 | classes = { 5 | }; 6 | objectVersion = 50; 7 | objects = { 8 | 9 | /* Begin PBXBuildFile section */ 10 | 3786FE0424B79AF2003E6AB7 /* README.md in Resources */ = {isa = PBXBuildFile; fileRef = 3786FE0324B79AF2003E6AB7 /* README.md */; }; 11 | 378B45FF24B4F61800241490 /* Core_Data_Super_Ultra_Fun_FestApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378B45EC24B4F61500241490 /* Core_Data_Super_Ultra_Fun_FestApp.swift */; }; 12 | 378B460024B4F61800241490 /* Core_Data_Super_Ultra_Fun_FestApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378B45EC24B4F61500241490 /* Core_Data_Super_Ultra_Fun_FestApp.swift */; }; 13 | 378B460124B4F61800241490 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378B45ED24B4F61500241490 /* ContentView.swift */; }; 14 | 378B460224B4F61800241490 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378B45ED24B4F61500241490 /* ContentView.swift */; }; 15 | 378B460324B4F61800241490 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 378B45EE24B4F61800241490 /* Assets.xcassets */; }; 16 | 378B460424B4F61800241490 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 378B45EE24B4F61800241490 /* Assets.xcassets */; }; 17 | 378B460E24B4F63B00241490 /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378B460D24B4F63B00241490 /* ViewModel.swift */; }; 18 | 378B460F24B4F63B00241490 /* ViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378B460D24B4F63B00241490 /* ViewModel.swift */; }; 19 | 378B461224B4F67400241490 /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 378B461024B4F67400241490 /* Model.xcdatamodeld */; }; 20 | 378B461324B4F67400241490 /* Model.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 378B461024B4F67400241490 /* Model.xcdatamodeld */; }; 21 | 378B461624B4F80400241490 /* CoreData.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 378B461524B4F80400241490 /* CoreData.framework */; }; 22 | 378B461824B4F88900241490 /* Counter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378B461724B4F88900241490 /* Counter.swift */; }; 23 | 378B461924B4F88900241490 /* Counter.swift in Sources */ = {isa = PBXBuildFile; fileRef = 378B461724B4F88900241490 /* Counter.swift */; }; 24 | /* End PBXBuildFile section */ 25 | 26 | /* Begin PBXFileReference section */ 27 | 3786FE0324B79AF2003E6AB7 /* README.md */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = net.daringfireball.markdown; path = README.md; sourceTree = ""; }; 28 | 378B45EC24B4F61500241490 /* Core_Data_Super_Ultra_Fun_FestApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Core_Data_Super_Ultra_Fun_FestApp.swift; sourceTree = ""; }; 29 | 378B45ED24B4F61500241490 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 30 | 378B45EE24B4F61800241490 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 31 | 378B45F324B4F61800241490 /* Core Data Super Ultra Fun Fest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Core Data Super Ultra Fun Fest.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 32 | 378B45F624B4F61800241490 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 33 | 378B45FB24B4F61800241490 /* Core Data Super Ultra Fun Fest.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = "Core Data Super Ultra Fun Fest.app"; sourceTree = BUILT_PRODUCTS_DIR; }; 34 | 378B45FD24B4F61800241490 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; 35 | 378B45FE24B4F61800241490 /* macOS.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = macOS.entitlements; sourceTree = ""; }; 36 | 378B460D24B4F63B00241490 /* ViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ViewModel.swift; sourceTree = ""; }; 37 | 378B461124B4F67400241490 /* Model.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Model.xcdatamodel; sourceTree = ""; }; 38 | 378B461524B4F80400241490 /* CoreData.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CoreData.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS14.0.sdk/System/Library/Frameworks/CoreData.framework; sourceTree = DEVELOPER_DIR; }; 39 | 378B461724B4F88900241490 /* Counter.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Counter.swift; sourceTree = ""; }; 40 | /* End PBXFileReference section */ 41 | 42 | /* Begin PBXFrameworksBuildPhase section */ 43 | 378B45F024B4F61800241490 /* Frameworks */ = { 44 | isa = PBXFrameworksBuildPhase; 45 | buildActionMask = 2147483647; 46 | files = ( 47 | 378B461624B4F80400241490 /* CoreData.framework in Frameworks */, 48 | ); 49 | runOnlyForDeploymentPostprocessing = 0; 50 | }; 51 | 378B45F824B4F61800241490 /* Frameworks */ = { 52 | isa = PBXFrameworksBuildPhase; 53 | buildActionMask = 2147483647; 54 | files = ( 55 | ); 56 | runOnlyForDeploymentPostprocessing = 0; 57 | }; 58 | /* End PBXFrameworksBuildPhase section */ 59 | 60 | /* Begin PBXGroup section */ 61 | 378B45E624B4F61500241490 = { 62 | isa = PBXGroup; 63 | children = ( 64 | 3786FE0324B79AF2003E6AB7 /* README.md */, 65 | 378B45EB24B4F61500241490 /* Shared */, 66 | 378B45F524B4F61800241490 /* iOS */, 67 | 378B45FC24B4F61800241490 /* macOS */, 68 | 378B45F424B4F61800241490 /* Products */, 69 | 378B461424B4F80400241490 /* Frameworks */, 70 | ); 71 | sourceTree = ""; 72 | }; 73 | 378B45EB24B4F61500241490 /* Shared */ = { 74 | isa = PBXGroup; 75 | children = ( 76 | 378B45EC24B4F61500241490 /* Core_Data_Super_Ultra_Fun_FestApp.swift */, 77 | 378B45ED24B4F61500241490 /* ContentView.swift */, 78 | 378B45EE24B4F61800241490 /* Assets.xcassets */, 79 | 378B460D24B4F63B00241490 /* ViewModel.swift */, 80 | 378B461024B4F67400241490 /* Model.xcdatamodeld */, 81 | 378B461724B4F88900241490 /* Counter.swift */, 82 | ); 83 | path = Shared; 84 | sourceTree = ""; 85 | }; 86 | 378B45F424B4F61800241490 /* Products */ = { 87 | isa = PBXGroup; 88 | children = ( 89 | 378B45F324B4F61800241490 /* Core Data Super Ultra Fun Fest.app */, 90 | 378B45FB24B4F61800241490 /* Core Data Super Ultra Fun Fest.app */, 91 | ); 92 | name = Products; 93 | sourceTree = ""; 94 | }; 95 | 378B45F524B4F61800241490 /* iOS */ = { 96 | isa = PBXGroup; 97 | children = ( 98 | 378B45F624B4F61800241490 /* Info.plist */, 99 | ); 100 | path = iOS; 101 | sourceTree = ""; 102 | }; 103 | 378B45FC24B4F61800241490 /* macOS */ = { 104 | isa = PBXGroup; 105 | children = ( 106 | 378B45FD24B4F61800241490 /* Info.plist */, 107 | 378B45FE24B4F61800241490 /* macOS.entitlements */, 108 | ); 109 | path = macOS; 110 | sourceTree = ""; 111 | }; 112 | 378B461424B4F80400241490 /* Frameworks */ = { 113 | isa = PBXGroup; 114 | children = ( 115 | 378B461524B4F80400241490 /* CoreData.framework */, 116 | ); 117 | name = Frameworks; 118 | sourceTree = ""; 119 | }; 120 | /* End PBXGroup section */ 121 | 122 | /* Begin PBXNativeTarget section */ 123 | 378B45F224B4F61800241490 /* iOS */ = { 124 | isa = PBXNativeTarget; 125 | buildConfigurationList = 378B460724B4F61800241490 /* Build configuration list for PBXNativeTarget "iOS" */; 126 | buildPhases = ( 127 | 378B45EF24B4F61800241490 /* Sources */, 128 | 378B45F024B4F61800241490 /* Frameworks */, 129 | 378B45F124B4F61800241490 /* Resources */, 130 | ); 131 | buildRules = ( 132 | ); 133 | dependencies = ( 134 | ); 135 | name = iOS; 136 | productName = iOS; 137 | productReference = 378B45F324B4F61800241490 /* Core Data Super Ultra Fun Fest.app */; 138 | productType = "com.apple.product-type.application"; 139 | }; 140 | 378B45FA24B4F61800241490 /* macOS */ = { 141 | isa = PBXNativeTarget; 142 | buildConfigurationList = 378B460A24B4F61800241490 /* Build configuration list for PBXNativeTarget "macOS" */; 143 | buildPhases = ( 144 | 378B45F724B4F61800241490 /* Sources */, 145 | 378B45F824B4F61800241490 /* Frameworks */, 146 | 378B45F924B4F61800241490 /* Resources */, 147 | ); 148 | buildRules = ( 149 | ); 150 | dependencies = ( 151 | ); 152 | name = macOS; 153 | productName = macOS; 154 | productReference = 378B45FB24B4F61800241490 /* Core Data Super Ultra Fun Fest.app */; 155 | productType = "com.apple.product-type.application"; 156 | }; 157 | /* End PBXNativeTarget section */ 158 | 159 | /* Begin PBXProject section */ 160 | 378B45E724B4F61500241490 /* Project object */ = { 161 | isa = PBXProject; 162 | attributes = { 163 | LastSwiftUpdateCheck = 1200; 164 | LastUpgradeCheck = 1200; 165 | TargetAttributes = { 166 | 378B45F224B4F61800241490 = { 167 | CreatedOnToolsVersion = 12.0; 168 | }; 169 | 378B45FA24B4F61800241490 = { 170 | CreatedOnToolsVersion = 12.0; 171 | }; 172 | }; 173 | }; 174 | buildConfigurationList = 378B45EA24B4F61500241490 /* Build configuration list for PBXProject "Core Data Super Ultra Fun Fest" */; 175 | compatibilityVersion = "Xcode 9.3"; 176 | developmentRegion = en; 177 | hasScannedForEncodings = 0; 178 | knownRegions = ( 179 | en, 180 | Base, 181 | ); 182 | mainGroup = 378B45E624B4F61500241490; 183 | productRefGroup = 378B45F424B4F61800241490 /* Products */; 184 | projectDirPath = ""; 185 | projectRoot = ""; 186 | targets = ( 187 | 378B45F224B4F61800241490 /* iOS */, 188 | 378B45FA24B4F61800241490 /* macOS */, 189 | ); 190 | }; 191 | /* End PBXProject section */ 192 | 193 | /* Begin PBXResourcesBuildPhase section */ 194 | 378B45F124B4F61800241490 /* Resources */ = { 195 | isa = PBXResourcesBuildPhase; 196 | buildActionMask = 2147483647; 197 | files = ( 198 | 3786FE0424B79AF2003E6AB7 /* README.md in Resources */, 199 | 378B460324B4F61800241490 /* Assets.xcassets in Resources */, 200 | ); 201 | runOnlyForDeploymentPostprocessing = 0; 202 | }; 203 | 378B45F924B4F61800241490 /* Resources */ = { 204 | isa = PBXResourcesBuildPhase; 205 | buildActionMask = 2147483647; 206 | files = ( 207 | 378B460424B4F61800241490 /* Assets.xcassets in Resources */, 208 | ); 209 | runOnlyForDeploymentPostprocessing = 0; 210 | }; 211 | /* End PBXResourcesBuildPhase section */ 212 | 213 | /* Begin PBXSourcesBuildPhase section */ 214 | 378B45EF24B4F61800241490 /* Sources */ = { 215 | isa = PBXSourcesBuildPhase; 216 | buildActionMask = 2147483647; 217 | files = ( 218 | 378B461824B4F88900241490 /* Counter.swift in Sources */, 219 | 378B460124B4F61800241490 /* ContentView.swift in Sources */, 220 | 378B460E24B4F63B00241490 /* ViewModel.swift in Sources */, 221 | 378B45FF24B4F61800241490 /* Core_Data_Super_Ultra_Fun_FestApp.swift in Sources */, 222 | 378B461224B4F67400241490 /* Model.xcdatamodeld in Sources */, 223 | ); 224 | runOnlyForDeploymentPostprocessing = 0; 225 | }; 226 | 378B45F724B4F61800241490 /* Sources */ = { 227 | isa = PBXSourcesBuildPhase; 228 | buildActionMask = 2147483647; 229 | files = ( 230 | 378B461924B4F88900241490 /* Counter.swift in Sources */, 231 | 378B460224B4F61800241490 /* ContentView.swift in Sources */, 232 | 378B460F24B4F63B00241490 /* ViewModel.swift in Sources */, 233 | 378B460024B4F61800241490 /* Core_Data_Super_Ultra_Fun_FestApp.swift in Sources */, 234 | 378B461324B4F67400241490 /* Model.xcdatamodeld in Sources */, 235 | ); 236 | runOnlyForDeploymentPostprocessing = 0; 237 | }; 238 | /* End PBXSourcesBuildPhase section */ 239 | 240 | /* Begin XCBuildConfiguration section */ 241 | 378B460524B4F61800241490 /* Debug */ = { 242 | isa = XCBuildConfiguration; 243 | buildSettings = { 244 | ALWAYS_SEARCH_USER_PATHS = NO; 245 | CLANG_ANALYZER_NONNULL = YES; 246 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 247 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 248 | CLANG_CXX_LIBRARY = "libc++"; 249 | CLANG_ENABLE_MODULES = YES; 250 | CLANG_ENABLE_OBJC_ARC = YES; 251 | CLANG_ENABLE_OBJC_WEAK = YES; 252 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 253 | CLANG_WARN_BOOL_CONVERSION = YES; 254 | CLANG_WARN_COMMA = YES; 255 | CLANG_WARN_CONSTANT_CONVERSION = YES; 256 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 257 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 258 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 259 | CLANG_WARN_EMPTY_BODY = YES; 260 | CLANG_WARN_ENUM_CONVERSION = YES; 261 | CLANG_WARN_INFINITE_RECURSION = YES; 262 | CLANG_WARN_INT_CONVERSION = YES; 263 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 264 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 265 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 266 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 267 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 268 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 269 | CLANG_WARN_STRICT_PROTOTYPES = YES; 270 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 271 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 272 | CLANG_WARN_UNREACHABLE_CODE = YES; 273 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 274 | COPY_PHASE_STRIP = NO; 275 | DEBUG_INFORMATION_FORMAT = dwarf; 276 | ENABLE_STRICT_OBJC_MSGSEND = YES; 277 | ENABLE_TESTABILITY = YES; 278 | GCC_C_LANGUAGE_STANDARD = gnu11; 279 | GCC_DYNAMIC_NO_PIC = NO; 280 | GCC_NO_COMMON_BLOCKS = YES; 281 | GCC_OPTIMIZATION_LEVEL = 0; 282 | GCC_PREPROCESSOR_DEFINITIONS = ( 283 | "DEBUG=1", 284 | "$(inherited)", 285 | ); 286 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 287 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 288 | GCC_WARN_UNDECLARED_SELECTOR = YES; 289 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 290 | GCC_WARN_UNUSED_FUNCTION = YES; 291 | GCC_WARN_UNUSED_VARIABLE = YES; 292 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; 293 | MTL_FAST_MATH = YES; 294 | ONLY_ACTIVE_ARCH = YES; 295 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG; 296 | SWIFT_OPTIMIZATION_LEVEL = "-Onone"; 297 | }; 298 | name = Debug; 299 | }; 300 | 378B460624B4F61800241490 /* Release */ = { 301 | isa = XCBuildConfiguration; 302 | buildSettings = { 303 | ALWAYS_SEARCH_USER_PATHS = NO; 304 | CLANG_ANALYZER_NONNULL = YES; 305 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; 306 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14"; 307 | CLANG_CXX_LIBRARY = "libc++"; 308 | CLANG_ENABLE_MODULES = YES; 309 | CLANG_ENABLE_OBJC_ARC = YES; 310 | CLANG_ENABLE_OBJC_WEAK = YES; 311 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; 312 | CLANG_WARN_BOOL_CONVERSION = YES; 313 | CLANG_WARN_COMMA = YES; 314 | CLANG_WARN_CONSTANT_CONVERSION = YES; 315 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; 316 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; 317 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES; 318 | CLANG_WARN_EMPTY_BODY = YES; 319 | CLANG_WARN_ENUM_CONVERSION = YES; 320 | CLANG_WARN_INFINITE_RECURSION = YES; 321 | CLANG_WARN_INT_CONVERSION = YES; 322 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; 323 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; 324 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; 325 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; 326 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; 327 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; 328 | CLANG_WARN_STRICT_PROTOTYPES = YES; 329 | CLANG_WARN_SUSPICIOUS_MOVE = YES; 330 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; 331 | CLANG_WARN_UNREACHABLE_CODE = YES; 332 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; 333 | COPY_PHASE_STRIP = NO; 334 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; 335 | ENABLE_NS_ASSERTIONS = NO; 336 | ENABLE_STRICT_OBJC_MSGSEND = YES; 337 | GCC_C_LANGUAGE_STANDARD = gnu11; 338 | GCC_NO_COMMON_BLOCKS = YES; 339 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES; 340 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; 341 | GCC_WARN_UNDECLARED_SELECTOR = YES; 342 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; 343 | GCC_WARN_UNUSED_FUNCTION = YES; 344 | GCC_WARN_UNUSED_VARIABLE = YES; 345 | MTL_ENABLE_DEBUG_INFO = NO; 346 | MTL_FAST_MATH = YES; 347 | SWIFT_COMPILATION_MODE = wholemodule; 348 | SWIFT_OPTIMIZATION_LEVEL = "-O"; 349 | }; 350 | name = Release; 351 | }; 352 | 378B460824B4F61800241490 /* Debug */ = { 353 | isa = XCBuildConfiguration; 354 | buildSettings = { 355 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 356 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 357 | CODE_SIGN_STYLE = Automatic; 358 | DEVELOPMENT_TEAM = P8HN93WN5C; 359 | ENABLE_PREVIEWS = YES; 360 | INFOPLIST_FILE = iOS/Info.plist; 361 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 362 | LD_RUNPATH_SEARCH_PATHS = ( 363 | "$(inherited)", 364 | "@executable_path/Frameworks", 365 | ); 366 | PRODUCT_BUNDLE_IDENTIFIER = "com.danielpasco.Core-Data-Super-Ultra-Fun-Fest"; 367 | PRODUCT_NAME = "Core Data Super Ultra Fun Fest"; 368 | SDKROOT = iphoneos; 369 | SWIFT_VERSION = 5.0; 370 | TARGETED_DEVICE_FAMILY = "1,2"; 371 | }; 372 | name = Debug; 373 | }; 374 | 378B460924B4F61800241490 /* Release */ = { 375 | isa = XCBuildConfiguration; 376 | buildSettings = { 377 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 378 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 379 | CODE_SIGN_STYLE = Automatic; 380 | DEVELOPMENT_TEAM = P8HN93WN5C; 381 | ENABLE_PREVIEWS = YES; 382 | INFOPLIST_FILE = iOS/Info.plist; 383 | IPHONEOS_DEPLOYMENT_TARGET = 14.0; 384 | LD_RUNPATH_SEARCH_PATHS = ( 385 | "$(inherited)", 386 | "@executable_path/Frameworks", 387 | ); 388 | PRODUCT_BUNDLE_IDENTIFIER = "com.danielpasco.Core-Data-Super-Ultra-Fun-Fest"; 389 | PRODUCT_NAME = "Core Data Super Ultra Fun Fest"; 390 | SDKROOT = iphoneos; 391 | SWIFT_VERSION = 5.0; 392 | TARGETED_DEVICE_FAMILY = "1,2"; 393 | VALIDATE_PRODUCT = YES; 394 | }; 395 | name = Release; 396 | }; 397 | 378B460B24B4F61800241490 /* Debug */ = { 398 | isa = XCBuildConfiguration; 399 | buildSettings = { 400 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 401 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 402 | CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements; 403 | CODE_SIGN_STYLE = Automatic; 404 | COMBINE_HIDPI_IMAGES = YES; 405 | DEVELOPMENT_TEAM = P8HN93WN5C; 406 | ENABLE_HARDENED_RUNTIME = YES; 407 | ENABLE_PREVIEWS = YES; 408 | INFOPLIST_FILE = macOS/Info.plist; 409 | LD_RUNPATH_SEARCH_PATHS = ( 410 | "$(inherited)", 411 | "@executable_path/../Frameworks", 412 | ); 413 | MACOSX_DEPLOYMENT_TARGET = 10.16; 414 | PRODUCT_BUNDLE_IDENTIFIER = "com.danielpasco.Core-Data-Super-Ultra-Fun-Fest"; 415 | PRODUCT_NAME = "Core Data Super Ultra Fun Fest"; 416 | SDKROOT = macosx; 417 | SWIFT_VERSION = 5.0; 418 | }; 419 | name = Debug; 420 | }; 421 | 378B460C24B4F61800241490 /* Release */ = { 422 | isa = XCBuildConfiguration; 423 | buildSettings = { 424 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; 425 | ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; 426 | CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements; 427 | CODE_SIGN_STYLE = Automatic; 428 | COMBINE_HIDPI_IMAGES = YES; 429 | DEVELOPMENT_TEAM = P8HN93WN5C; 430 | ENABLE_HARDENED_RUNTIME = YES; 431 | ENABLE_PREVIEWS = YES; 432 | INFOPLIST_FILE = macOS/Info.plist; 433 | LD_RUNPATH_SEARCH_PATHS = ( 434 | "$(inherited)", 435 | "@executable_path/../Frameworks", 436 | ); 437 | MACOSX_DEPLOYMENT_TARGET = 10.16; 438 | PRODUCT_BUNDLE_IDENTIFIER = "com.danielpasco.Core-Data-Super-Ultra-Fun-Fest"; 439 | PRODUCT_NAME = "Core Data Super Ultra Fun Fest"; 440 | SDKROOT = macosx; 441 | SWIFT_VERSION = 5.0; 442 | }; 443 | name = Release; 444 | }; 445 | /* End XCBuildConfiguration section */ 446 | 447 | /* Begin XCConfigurationList section */ 448 | 378B45EA24B4F61500241490 /* Build configuration list for PBXProject "Core Data Super Ultra Fun Fest" */ = { 449 | isa = XCConfigurationList; 450 | buildConfigurations = ( 451 | 378B460524B4F61800241490 /* Debug */, 452 | 378B460624B4F61800241490 /* Release */, 453 | ); 454 | defaultConfigurationIsVisible = 0; 455 | defaultConfigurationName = Release; 456 | }; 457 | 378B460724B4F61800241490 /* Build configuration list for PBXNativeTarget "iOS" */ = { 458 | isa = XCConfigurationList; 459 | buildConfigurations = ( 460 | 378B460824B4F61800241490 /* Debug */, 461 | 378B460924B4F61800241490 /* Release */, 462 | ); 463 | defaultConfigurationIsVisible = 0; 464 | defaultConfigurationName = Release; 465 | }; 466 | 378B460A24B4F61800241490 /* Build configuration list for PBXNativeTarget "macOS" */ = { 467 | isa = XCConfigurationList; 468 | buildConfigurations = ( 469 | 378B460B24B4F61800241490 /* Debug */, 470 | 378B460C24B4F61800241490 /* Release */, 471 | ); 472 | defaultConfigurationIsVisible = 0; 473 | defaultConfigurationName = Release; 474 | }; 475 | /* End XCConfigurationList section */ 476 | 477 | /* Begin XCVersionGroup section */ 478 | 378B461024B4F67400241490 /* Model.xcdatamodeld */ = { 479 | isa = XCVersionGroup; 480 | children = ( 481 | 378B461124B4F67400241490 /* Model.xcdatamodel */, 482 | ); 483 | currentVersion = 378B461124B4F67400241490 /* Model.xcdatamodel */; 484 | path = Model.xcdatamodeld; 485 | sourceTree = ""; 486 | versionGroupType = wrapper.xcdatamodel; 487 | }; 488 | /* End XCVersionGroup section */ 489 | }; 490 | rootObject = 378B45E724B4F61500241490 /* Project object */; 491 | } 492 | -------------------------------------------------------------------------------- /Core Data Super Ultra Fun Fest.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Core Data Super Ultra Fun Fest.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Core Data Super Ultra Fun Fest.xcodeproj/xcuserdata/dan.xcuserdatad/xcschemes/xcschememanagement.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | SchemeUserState 6 | 7 | iOS.xcscheme_^#shared#^_ 8 | 9 | orderHint 10 | 1 11 | 12 | macOS.xcscheme_^#shared#^_ 13 | 14 | orderHint 15 | 0 16 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | 2 | This project demostrates three different ways of updating data in a background thread in a swiftUI project. 3 | 4 | Thanks to Tim Ritchey (@tritchey) for some suggesting the addition of 5 | 6 | self.context.automaticallyMergesChangesFromParent = true 7 | 8 | to the ViewModel init() function. This is required to get changes to the background moc to show up on the viewContext. 9 | 10 | Thanks to Toomas Vahter (@laevandus) for his [CoreDataCombineSwiftUI](http://github.com/laevandus/CoreDataCombineSwiftUI) demo, which was instrumental in putting this together 11 | -------------------------------------------------------------------------------- /Shared/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 | -------------------------------------------------------------------------------- /Shared/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 | "idiom" : "mac", 95 | "scale" : "1x", 96 | "size" : "16x16" 97 | }, 98 | { 99 | "idiom" : "mac", 100 | "scale" : "2x", 101 | "size" : "16x16" 102 | }, 103 | { 104 | "idiom" : "mac", 105 | "scale" : "1x", 106 | "size" : "32x32" 107 | }, 108 | { 109 | "idiom" : "mac", 110 | "scale" : "2x", 111 | "size" : "32x32" 112 | }, 113 | { 114 | "idiom" : "mac", 115 | "scale" : "1x", 116 | "size" : "128x128" 117 | }, 118 | { 119 | "idiom" : "mac", 120 | "scale" : "2x", 121 | "size" : "128x128" 122 | }, 123 | { 124 | "idiom" : "mac", 125 | "scale" : "1x", 126 | "size" : "256x256" 127 | }, 128 | { 129 | "idiom" : "mac", 130 | "scale" : "2x", 131 | "size" : "256x256" 132 | }, 133 | { 134 | "idiom" : "mac", 135 | "scale" : "1x", 136 | "size" : "512x512" 137 | }, 138 | { 139 | "idiom" : "mac", 140 | "scale" : "2x", 141 | "size" : "512x512" 142 | } 143 | ], 144 | "info" : { 145 | "author" : "xcode", 146 | "version" : 1 147 | } 148 | } 149 | -------------------------------------------------------------------------------- /Shared/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Shared/ContentView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContentView.swift 3 | // Shared 4 | // 5 | // Created by Daniel Pasco on 7/7/20. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ContentView: View { 11 | @ObservedObject var viewModel:ViewModel 12 | 13 | init () { 14 | viewModel = ViewModel() 15 | } 16 | 17 | func addCounter() { 18 | print("Adding a counter") 19 | viewModel.addCounter() 20 | } 21 | 22 | func incrementCounts() { 23 | print("incrementing counts") 24 | viewModel.incrementCounts() 25 | } 26 | 27 | var body: some View { 28 | NavigationView { 29 | VStack { 30 | List(viewModel.counters, id: \.objectID) { (counter) in 31 | CounterRow(counter: counter) 32 | } 33 | .navigationTitle("Counters") 34 | .toolbar { 35 | Button("Add", action:addCounter) 36 | Spacer() 37 | Button("Increment counts", action:incrementCounts) 38 | } 39 | } 40 | } 41 | } 42 | } 43 | 44 | struct CounterRow: View { 45 | @ObservedObject var counter:Counter 46 | 47 | var body: some View { 48 | Text("\(counter.name!): \(counter.count)").padding() 49 | } 50 | } 51 | 52 | struct ContentView_Previews: PreviewProvider { 53 | static var previews: some View { 54 | ContentView() 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Shared/Core_Data_Super_Ultra_Fun_FestApp.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Core_Data_Super_Ultra_Fun_FestApp.swift 3 | // Shared 4 | // 5 | // Created by Daniel Pasco on 7/7/20. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @main 11 | struct Core_Data_Super_Ultra_Fun_FestApp: App { 12 | var body: some Scene { 13 | WindowGroup { 14 | ContentView() 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Shared/Counter.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Counter.swift 3 | // Core Data Super Ultra Fun Fest 4 | // 5 | // Created by Daniel Pasco on 7/7/20. 6 | // 7 | 8 | import Foundation 9 | import CoreData 10 | 11 | extension Counter { 12 | static let entityName = "Counter" 13 | 14 | convenience init(context: NSManagedObjectContext) { 15 | let entity = NSEntityDescription.entity(forEntityName: Self.entityName, in: context)! 16 | self.init(entity: entity, insertInto: context) 17 | } 18 | 19 | static func resultsController(context: NSManagedObjectContext, sortDescriptors: [NSSortDescriptor] = []) -> NSFetchedResultsController { 20 | let request:NSFetchRequest = fetchRequest() 21 | request.sortDescriptors = sortDescriptors.isEmpty ? nil : sortDescriptors 22 | return NSFetchedResultsController(fetchRequest: request, managedObjectContext: context, sectionNameKeyPath: nil, cacheName: nil) 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Shared/Model.xcdatamodeld/Model.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /Shared/ViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewModel.swift 3 | // Core Data Super Ultra Fun Fest 4 | // 5 | // Created by Daniel Pasco on 7/7/20. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | import CoreData 11 | import SwiftUI 12 | 13 | final class ViewModel: NSObject, NSFetchedResultsControllerDelegate, ObservableObject { 14 | private var counterController: NSFetchedResultsController 15 | 16 | var initialized = false 17 | var container:NSPersistentContainer? = nil 18 | var cancellables = [AnyCancellable]() 19 | public var context: NSManagedObjectContext 20 | 21 | // MARK: - Initializer 22 | override init() { 23 | container = NSPersistentContainer(name: "Model") 24 | if container != nil { 25 | container!.loadPersistentStores(completionHandler: { (storeDescription, error) in 26 | if let error = error as NSError? { 27 | fatalError("Unresolved error \(error), \(error.userInfo)") 28 | } 29 | }) 30 | 31 | // This allows us to do upserts on our final, main thread save. 32 | container!.viewContext.mergePolicy = NSMergeByPropertyObjectTrumpMergePolicy 33 | } 34 | 35 | let sortDescriptors = [NSSortDescriptor(keyPath: \Counter.name, ascending: true)] 36 | counterController = Counter.resultsController(context: container!.viewContext, sortDescriptors: sortDescriptors) 37 | 38 | self.context = container!.viewContext 39 | 40 | // IMPORTANT: We need to set this to true so that our viewContext picks up changes saved to the container on background threads 41 | self.context.automaticallyMergesChangesFromParent = true 42 | super.init() 43 | 44 | observeChangeNotification() 45 | counterController.delegate = self 46 | try? counterController.performFetch() 47 | } 48 | 49 | func controllerWillChangeContent(_ controller: NSFetchedResultsController) { 50 | objectWillChange.send() 51 | } 52 | 53 | var counters: [Counter] { 54 | return counterController.fetchedObjects ?? [] 55 | } 56 | 57 | public func addCounter() { 58 | if(container == nil) { 59 | print("nil container, can't create object") 60 | return 61 | } 62 | container!.performBackgroundTask { (moc) in 63 | do { 64 | // This should be safe on any thread if we don't access the object attributes 65 | let lastCount = self.counters.count 66 | let counter = Counter(context: moc) 67 | 68 | counter.name = "Counter #\(lastCount + 1)" 69 | counter.count = 0 70 | try moc.save() 71 | } 72 | catch { 73 | print("Error creating new counter. \(error), \(error.localizedDescription)") 74 | } 75 | } 76 | } 77 | 78 | 79 | public func incrementCounts() { 80 | if(container == nil) { 81 | print("nil container, can't create object") 82 | return 83 | } 84 | 85 | container!.performBackgroundTask { (moc) in 86 | do { 87 | let fetchRequest = NSFetchRequest(entityName: "Counter") 88 | var counterList: [Counter] = [] 89 | counterList = try self.context.fetch(fetchRequest) 90 | print("found \(counterList.count) counters") 91 | for counter in counterList { 92 | counter.count += 1 93 | } 94 | try moc.save() 95 | } 96 | catch { 97 | print("Error creating new counter. \(error), \(error.localizedDescription)") 98 | } 99 | } 100 | } 101 | 102 | // Observing Change Notifications 103 | private func observeChangeNotification() { 104 | let cancellable = NotificationCenter.default.publisher(for: .NSManagedObjectContextObjectsDidChange, object: container!.viewContext) 105 | .compactMap({ ManagedObjectContextChanges(notification: $0) }).sink { (changes) in 106 | self.container!.viewContext.saveIfNeeded() 107 | } 108 | cancellables.append(cancellable) 109 | } 110 | } 111 | 112 | struct ManagedObjectContextChanges { 113 | let inserted: Set 114 | let deleted: Set 115 | let updated: Set 116 | 117 | init?(notification: Notification) { 118 | let unpack: (String) -> Set = { key in 119 | let managedObjects = (notification.userInfo?[key] as? Set) ?? [] 120 | return Set(managedObjects.compactMap({ $0 as? T })) 121 | } 122 | deleted = unpack(NSDeletedObjectsKey) 123 | inserted = unpack(NSInsertedObjectsKey) 124 | updated = unpack(NSUpdatedObjectsKey).union(unpack(NSRefreshedObjectsKey)) 125 | if deleted.isEmpty, inserted.isEmpty, updated.isEmpty { 126 | return nil 127 | } 128 | } 129 | } 130 | 131 | extension NSManagedObjectContext { 132 | func saveIfNeeded() { 133 | guard hasChanges else { return } 134 | do { 135 | try save() 136 | } 137 | catch let nsError as NSError { 138 | fatalError("Unresolved error \(nsError), \(nsError.userInfo)") 139 | } 140 | } 141 | } 142 | -------------------------------------------------------------------------------- /iOS/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 | -------------------------------------------------------------------------------- /macOS/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CFBundleDevelopmentRegion 6 | $(DEVELOPMENT_LANGUAGE) 7 | CFBundleExecutable 8 | $(EXECUTABLE_NAME) 9 | CFBundleIconFile 10 | 11 | CFBundleIdentifier 12 | $(PRODUCT_BUNDLE_IDENTIFIER) 13 | CFBundleInfoDictionaryVersion 14 | 6.0 15 | CFBundleName 16 | $(PRODUCT_NAME) 17 | CFBundlePackageType 18 | $(PRODUCT_BUNDLE_PACKAGE_TYPE) 19 | CFBundleShortVersionString 20 | 1.0 21 | CFBundleVersion 22 | 1 23 | LSMinimumSystemVersion 24 | $(MACOSX_DEPLOYMENT_TARGET) 25 | 26 | 27 | -------------------------------------------------------------------------------- /macOS/macOS.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.app-sandbox 6 | 7 | com.apple.security.files.user-selected.read-only 8 | 9 | 10 | 11 | --------------------------------------------------------------------------------