diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/._.DS_Store" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/._.DS_Store" new file mode 100644 index 0000000..8e82ed9 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/._.DS_Store" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/._ToDos" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/._ToDos" new file mode 100644 index 0000000..904e3be Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/._ToDos" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/._.DS_Store" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/._.DS_Store" new file mode 100644 index 0000000..8e82ed9 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/._.DS_Store" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/._ToDos.xcodeproj" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/._ToDos.xcodeproj" new file mode 100644 index 0000000..6cc43fc Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/._ToDos.xcodeproj" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/._project.xcworkspace" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/._project.xcworkspace" new file mode 100644 index 0000000..faa89ae Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/._project.xcworkspace" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.pbxproj" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.pbxproj" new file mode 100644 index 0000000..d733f00 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.pbxproj" @@ -0,0 +1,410 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + E703406A2A9D04E300BD054F /* DefaultToDos.json in Resources */ = {isa = PBXBuildFile; fileRef = E70340692A9D04E300BD054F /* DefaultToDos.json */; }; + E7325DC82A2F839400130DAE /* ToDosApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7325DC72A2F839400130DAE /* ToDosApp.swift */; }; + E7325DCA2A2F839400130DAE /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7325DC92A2F839400130DAE /* ContentView.swift */; }; + E7325DCC2A2F839500130DAE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E7325DCB2A2F839500130DAE /* Assets.xcassets */; }; + E7325DCF2A2F839500130DAE /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E7325DCE2A2F839500130DAE /* Preview Assets.xcassets */; }; + E7325DD12A2F839500130DAE /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7325DD02A2F839500130DAE /* Item.swift */; }; + E7325DD82A2F83B600130DAE /* ModifyTodoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7325DD72A2F83B600130DAE /* ModifyTodoView.swift */; }; + E7325DDA2A2F8A9C00130DAE /* TodoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7325DD92A2F8A9C00130DAE /* TodoView.swift */; }; + E79BE2CB2A3259F5002AFC0C /* UpdateToDoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E79BE2CA2A3259F5002AFC0C /* UpdateToDoView.swift */; }; + E79BE2CD2A325A32002AFC0C /* CreateCategoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E79BE2CC2A325A32002AFC0C /* CreateCategoryView.swift */; }; + E7C2BEDF2A532E7D00197F9A /* DefaultCategories.json in Resources */ = {isa = PBXBuildFile; fileRef = E7C2BEDE2A532E7D00197F9A /* DefaultCategories.json */; }; + E7C2BEE12A5332FA00197F9A /* CategoriesJSONDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7C2BEE02A5332FA00197F9A /* CategoriesJSONDecoder.swift */; }; + E7E15E5D2A84057B0048DFAA /* SwiftUIImageViewer in Frameworks */ = {isa = PBXBuildFile; productRef = E7E15E5C2A84057B0048DFAA /* SwiftUIImageViewer */; }; + E7F0333E2A4B449700B40C93 /* ItemsContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7F0333D2A4B449700B40C93 /* ItemsContainer.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + E70340692A9D04E300BD054F /* DefaultToDos.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = DefaultToDos.json; sourceTree = ""; }; + E7325DC42A2F839400130DAE /* ToDos.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ToDos.app; sourceTree = BUILT_PRODUCTS_DIR; }; + E7325DC72A2F839400130DAE /* ToDosApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToDosApp.swift; sourceTree = ""; }; + E7325DC92A2F839400130DAE /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + E7325DCB2A2F839500130DAE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + E7325DCE2A2F839500130DAE /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + E7325DD02A2F839500130DAE /* Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Item.swift; sourceTree = ""; }; + E7325DD72A2F83B600130DAE /* ModifyTodoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModifyTodoView.swift; sourceTree = ""; }; + E7325DD92A2F8A9C00130DAE /* TodoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodoView.swift; sourceTree = ""; }; + E79BE2CA2A3259F5002AFC0C /* UpdateToDoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateToDoView.swift; sourceTree = ""; }; + E79BE2CC2A325A32002AFC0C /* CreateCategoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateCategoryView.swift; sourceTree = ""; }; + E7C2BEDE2A532E7D00197F9A /* DefaultCategories.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = DefaultCategories.json; sourceTree = ""; }; + E7C2BEE02A5332FA00197F9A /* CategoriesJSONDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoriesJSONDecoder.swift; sourceTree = ""; }; + E7F0333D2A4B449700B40C93 /* ItemsContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemsContainer.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + E7325DC12A2F839400130DAE /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E7E15E5D2A84057B0048DFAA /* SwiftUIImageViewer in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + E7325DBB2A2F839400130DAE = { + isa = PBXGroup; + children = ( + E7325DC62A2F839400130DAE /* ToDos */, + E7325DC52A2F839400130DAE /* Products */, + ); + sourceTree = ""; + }; + E7325DC52A2F839400130DAE /* Products */ = { + isa = PBXGroup; + children = ( + E7325DC42A2F839400130DAE /* ToDos.app */, + ); + name = Products; + sourceTree = ""; + }; + E7325DC62A2F839400130DAE /* ToDos */ = { + isa = PBXGroup; + children = ( + E7325DC72A2F839400130DAE /* ToDosApp.swift */, + E7325DC92A2F839400130DAE /* ContentView.swift */, + E7325DD92A2F8A9C00130DAE /* TodoView.swift */, + E7325DCB2A2F839500130DAE /* Assets.xcassets */, + E7325DD02A2F839500130DAE /* Item.swift */, + E7325DCD2A2F839500130DAE /* Preview Content */, + E7325DD72A2F83B600130DAE /* ModifyTodoView.swift */, + E79BE2CA2A3259F5002AFC0C /* UpdateToDoView.swift */, + E79BE2CC2A325A32002AFC0C /* CreateCategoryView.swift */, + E7C2BEE02A5332FA00197F9A /* CategoriesJSONDecoder.swift */, + E7F0333D2A4B449700B40C93 /* ItemsContainer.swift */, + E7C2BEDE2A532E7D00197F9A /* DefaultCategories.json */, + E70340692A9D04E300BD054F /* DefaultToDos.json */, + ); + path = ToDos; + sourceTree = ""; + }; + E7325DCD2A2F839500130DAE /* Preview Content */ = { + isa = PBXGroup; + children = ( + E7325DCE2A2F839500130DAE /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + E7325DC32A2F839400130DAE /* ToDos */ = { + isa = PBXNativeTarget; + buildConfigurationList = E7325DD42A2F839500130DAE /* Build configuration list for PBXNativeTarget "ToDos" */; + buildPhases = ( + E7325DC02A2F839400130DAE /* Sources */, + E7325DC12A2F839400130DAE /* Frameworks */, + E7325DC22A2F839400130DAE /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ToDos; + packageProductDependencies = ( + E7E15E5C2A84057B0048DFAA /* SwiftUIImageViewer */, + ); + productName = ToDos; + productReference = E7325DC42A2F839400130DAE /* ToDos.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + E7325DBC2A2F839400130DAE /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1500; + LastUpgradeCheck = 1500; + TargetAttributes = { + E7325DC32A2F839400130DAE = { + CreatedOnToolsVersion = 15.0; + }; + }; + }; + buildConfigurationList = E7325DBF2A2F839400130DAE /* Build configuration list for PBXProject "ToDos" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = E7325DBB2A2F839400130DAE; + packageReferences = ( + E7E15E5B2A84057B0048DFAA /* XCRemoteSwiftPackageReference "swiftui-image-viewer" */, + ); + productRefGroup = E7325DC52A2F839400130DAE /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + E7325DC32A2F839400130DAE /* ToDos */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + E7325DC22A2F839400130DAE /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E7325DCF2A2F839500130DAE /* Preview Assets.xcassets in Resources */, + E7325DCC2A2F839500130DAE /* Assets.xcassets in Resources */, + E7C2BEDF2A532E7D00197F9A /* DefaultCategories.json in Resources */, + E703406A2A9D04E300BD054F /* DefaultToDos.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + E7325DC02A2F839400130DAE /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E7325DD82A2F83B600130DAE /* ModifyTodoView.swift in Sources */, + E7325DCA2A2F839400130DAE /* ContentView.swift in Sources */, + E79BE2CD2A325A32002AFC0C /* CreateCategoryView.swift in Sources */, + E7C2BEE12A5332FA00197F9A /* CategoriesJSONDecoder.swift in Sources */, + E7325DDA2A2F8A9C00130DAE /* TodoView.swift in Sources */, + E7F0333E2A4B449700B40C93 /* ItemsContainer.swift in Sources */, + E79BE2CB2A3259F5002AFC0C /* UpdateToDoView.swift in Sources */, + E7325DD12A2F839500130DAE /* Item.swift in Sources */, + E7325DC82A2F839400130DAE /* ToDosApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + E7325DD22A2F839500130DAE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + E7325DD32A2F839500130DAE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + E7325DD52A2F839500130DAE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"ToDos/Preview Content\""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.tundsdev.ToDos; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + E7325DD62A2F839500130DAE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"ToDos/Preview Content\""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.tundsdev.ToDos; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + E7325DBF2A2F839400130DAE /* Build configuration list for PBXProject "ToDos" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E7325DD22A2F839500130DAE /* Debug */, + E7325DD32A2F839500130DAE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E7325DD42A2F839500130DAE /* Build configuration list for PBXNativeTarget "ToDos" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E7325DD52A2F839500130DAE /* Debug */, + E7325DD62A2F839500130DAE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + E7E15E5B2A84057B0048DFAA /* XCRemoteSwiftPackageReference "swiftui-image-viewer" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/fuzzzlove/swiftui-image-viewer.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + E7E15E5C2A84057B0048DFAA /* SwiftUIImageViewer */ = { + isa = XCSwiftPackageProductDependency; + package = E7E15E5B2A84057B0048DFAA /* XCRemoteSwiftPackageReference "swiftui-image-viewer" */; + productName = SwiftUIImageViewer; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = E7325DBC2A2F839400130DAE /* Project object */; +} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/contents.xcworkspacedata" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/contents.xcworkspacedata" new file mode 100644 index 0000000..919434a --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/contents.xcworkspacedata" @@ -0,0 +1,7 @@ + + + + + diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist" new file mode 100644 index 0000000..18d9810 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist" @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved" new file mode 100644 index 0000000..c5dd3c9 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved" @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "swiftui-image-viewer", + "kind" : "remoteSourceControl", + "location" : "https://github.com/fuzzzlove/swiftui-image-viewer.git", + "state" : { + "revision" : "ca77123064ae046eac2be01654ee756085c2b182", + "version" : "1.0.0" + } + } + ], + "version" : 2 +} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/xcuserdata/tunde.adegoroye.xcuserdatad/UserInterfaceState.xcuserstate" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/xcuserdata/tunde.adegoroye.xcuserdatad/UserInterfaceState.xcuserstate" new file mode 100644 index 0000000..caf5fc7 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/xcuserdata/tunde.adegoroye.xcuserdatad/UserInterfaceState.xcuserstate" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist" new file mode 100644 index 0000000..2068192 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist" @@ -0,0 +1,40 @@ + + + + + + + + + + + + + diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcschemes/xcschememanagement.plist" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcschemes/xcschememanagement.plist" new file mode 100644 index 0000000..5f45eb7 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcschemes/xcschememanagement.plist" @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + ToDos.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._CategoriesJSONDecoder.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._CategoriesJSONDecoder.swift" new file mode 100644 index 0000000..da3e95d Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._CategoriesJSONDecoder.swift" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ContentView.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ContentView.swift" new file mode 100644 index 0000000..01903dd Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ContentView.swift" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._CreateCategoryView.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._CreateCategoryView.swift" new file mode 100644 index 0000000..60490de Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._CreateCategoryView.swift" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._DefaultCategories.json" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._DefaultCategories.json" new file mode 100644 index 0000000..0e01ddd Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._DefaultCategories.json" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._DefaultToDos.json" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._DefaultToDos.json" new file mode 100644 index 0000000..e97da6d Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._DefaultToDos.json" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._Item.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._Item.swift" new file mode 100644 index 0000000..c7d684c Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._Item.swift" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ItemsContainer.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ItemsContainer.swift" new file mode 100644 index 0000000..f349e3e Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ItemsContainer.swift" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ModifyTodoView.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ModifyTodoView.swift" new file mode 100644 index 0000000..39843e3 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ModifyTodoView.swift" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ToDosApp.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ToDosApp.swift" new file mode 100644 index 0000000..b94a198 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ToDosApp.swift" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._TodoView.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._TodoView.swift" new file mode 100644 index 0000000..5a591a3 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._TodoView.swift" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._UpdateToDoView.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._UpdateToDoView.swift" new file mode 100644 index 0000000..a64f931 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._UpdateToDoView.swift" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/._Contents.json" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/._Contents.json" new file mode 100644 index 0000000..c893829 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/._Contents.json" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/Contents.json" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/Contents.json" new file mode 100644 index 0000000..eb87897 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/Contents.json" @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/AppIcon.appiconset/Contents.json" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/AppIcon.appiconset/Contents.json" new file mode 100644 index 0000000..13613e3 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/AppIcon.appiconset/Contents.json" @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/Contents.json" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/Contents.json" new file mode 100644 index 0000000..73c0059 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/Contents.json" @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/holiday.imageset/Contents.json" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/holiday.imageset/Contents.json" new file mode 100644 index 0000000..3cfc538 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/holiday.imageset/Contents.json" @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "holiday.jpeg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/holiday.imageset/holiday.jpeg" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/holiday.imageset/holiday.jpeg" new file mode 100644 index 0000000..5007c69 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/holiday.imageset/holiday.jpeg" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/CategoriesJSONDecoder.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/CategoriesJSONDecoder.swift" new file mode 100644 index 0000000..5e0adaf --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/CategoriesJSONDecoder.swift" @@ -0,0 +1,29 @@ +// +// CategoriesJSONDecoder.swift +// ToDos +// +// Created by Tunde Adegoroye on 03/07/2023. +// + +import Foundation + +struct DefaultsJSON { + + static func decode(from fileName: String, type: T.Type) -> T? { + + do { + guard let url = Bundle.main.url(forResource: fileName, withExtension: "json"), + let data = try? Data(contentsOf: url) else { + return nil + } + + let result = try JSONDecoder().decode(T.self, from: data) + + return result + } catch { + print(error) + return nil + } + + } +} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/ContentView.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/ContentView.swift" new file mode 100644 index 0000000..dd31bca --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/ContentView.swift" @@ -0,0 +1,282 @@ +// +// ContentView.swift +// ToDos +// +// Created by Tunde Adegoroye on 06/06/2023. +// + +import SwiftUI +import SwiftData +import SwiftUIImageViewer + +enum SortOption: String, CaseIterable { + case title + case date + case category +} + +extension SortOption { + + var systemImage: String { + switch self { + case .title: + "textformat.size.larger" + case .date: + "calendar" + case .category: + "folder" + } + } +} + +struct ContentView: View { + + @Environment(\.modelContext) private var modelContext + @Query private var items: [Item] + + @State private var searchQuery = "" + @State private var showCreateCategory = false + @State private var showCreateToDo = false + @State private var toDoToEdit: Item? + + @State private var isImageViewerPresented = false + + @State private var selectedSortOption = SortOption.allCases.first! + + var filteredItems: [Item] { + + if searchQuery.isEmpty { + return items.sort(on: selectedSortOption) + } + + let filteredItems = items.compactMap { item in + + let titleContainsQuery = item.title.range(of: searchQuery, + options: .caseInsensitive) != nil + + let categoryTitleContainsQuery = item.category?.title.range(of: searchQuery, + options: .caseInsensitive) != nil + + return (titleContainsQuery || categoryTitleContainsQuery) ? item : nil + } + + return filteredItems.sort(on: selectedSortOption) + + } + + var body: some View { + NavigationStack { + List { + ForEach(filteredItems) { item in + VStack { + HStack { + VStack(alignment: .leading) { + + if item.isCritical { + Image(systemName: "exclamationmark.3") + .symbolVariant(.fill) + .foregroundColor(.red) + .font(.largeTitle) + .bold() + } + + Text(item.title) + .font(.largeTitle) + .bold() + + Text("\(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .shortened))") + .font(.callout) + + if let category = item.category { + Text(category.title) + .foregroundStyle(Color.blue) + .bold() + .padding(.horizontal) + .padding(.vertical, 8) + .background(Color.blue.opacity(0.1), + in: RoundedRectangle(cornerRadius: 8, + style: .continuous)) + } + + } + + + Spacer() + + Button { + withAnimation { + item.isCompleted.toggle() + } + } label: { + + Image(systemName: "checkmark") + .symbolVariant(.circle.fill) + .foregroundStyle(item.isCompleted ? .green : .gray) + .font(.largeTitle) + } + .buttonStyle(.plain) + } + if let selectedPhotoData = item.image, + let uiImage = UIImage(data: selectedPhotoData) { + Image(uiImage: uiImage) + .resizable() + .scaledToFill() + .frame(maxWidth: .infinity, maxHeight: 120) + .clipShape(RoundedRectangle(cornerRadius: 10, + style: .continuous)) + .onTapGesture { + isImageViewerPresented = true + } + .fullScreenCover(isPresented: $isImageViewerPresented) { + SwiftUIImageViewer(image: Image(uiImage: uiImage)) + .overlay(alignment: .topTrailing) { + Button { + isImageViewerPresented = false + } label: { + Image(systemName: "xmark") + .font(.headline) + } + .buttonStyle(.bordered) + .clipShape(Circle()) + .tint(.purple) + .padding() + } + } + } + + } + .swipeActions { + + Button(role: .destructive) { + + withAnimation { + modelContext.delete(item) + } + + } label: { + Label("Delete", systemImage: "trash.fill") + } + + Button { + toDoToEdit = item + } label: { + Label("Edit", systemImage: "pencil") + } + .tint(.orange) + + } + } + } + .navigationTitle("My To Do List") + .animation(/*@START_MENU_TOKEN@*/.easeIn/*@END_MENU_TOKEN@*/, value: filteredItems) + .searchable(text: $searchQuery, + prompt: "Search for a to do or a category") + .overlay { + if filteredItems.isEmpty { + ContentUnavailableView.search + } + } + .sheet(item: $toDoToEdit, + onDismiss: { + toDoToEdit = nil + }, + content: { editItem in + NavigationStack { + UpdateToDoView(item: editItem) + .interactiveDismissDisabled() + } + }) + .sheet(isPresented: $showCreateCategory, + content: { + NavigationStack { + CreateCategoryView() + } + }) + .sheet(isPresented: $showCreateToDo, + content: { + NavigationStack { + CreateTodoView() + } + }) + .toolbar { + + ToolbarItemGroup(placement: .primaryAction) { + Button { + showCreateCategory.toggle() + } label: { + Image(systemName: "plus") + } + } + + ToolbarItemGroup(placement: .topBarTrailing) { + + Menu { + Picker("", selection: $selectedSortOption) { + ForEach(SortOption.allCases, + id: \.rawValue) { option in + Label(option.rawValue.capitalized, + systemImage: option.systemImage) + .tag(option) + } + } + .labelsHidden() + + } label: { + Image(systemName: "ellipsis") + .symbolVariant(.circle) + } + + } + + } + .safeAreaInset(edge: .bottom, + alignment: .leading) { + Button(action: { + showCreateToDo.toggle() + }, label: { + Label("New ToDo", systemImage: "plus") + .bold() + .font(.title2) + .padding(8) + .background(.gray.opacity(0.1), + in: Capsule()) + .padding(.leading) + .symbolVariant(.circle.fill) + + }) + + } + } + } + + private func delete(item: Item) { + withAnimation { + modelContext.delete(item) + } + } +} + +private extension [Item] { + + func sort(on option: SortOption) -> [Item] { + switch option { + case .title: + self.sorted(by: { $0.title < $1.title }) + case .date: + self.sorted(by: { $0.timestamp < $1.timestamp }) + case .category: + self.sorted(by: { + guard let firstItemTitle = $0.category?.title, + let secondItemTitle = $1.category?.title else { return false } + return firstItemTitle < secondItemTitle + }) + } + } +} + +// Xcode 15 Beta 2 has a previews bug so this is why we're commenting this out... +// Ref: https://mastodon.social/@denisdepalatis/110561280521551715 +//#Preview { +// ContentView() +// .modelContainer(for: Item.self, inMemory: true) +//} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/CreateCategoryView.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/CreateCategoryView.swift" new file mode 100644 index 0000000..97e89b2 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/CreateCategoryView.swift" @@ -0,0 +1,117 @@ +// +// CreateCategoryView.swift +// ToDos +// +// Created by Tunde Adegoroye on 08/06/2023. +// + +import SwiftUI +import SwiftData + +@Model +class Category: Codable { + + @Attribute(.unique) + var title: String + + var items: [Item]? + + init(title: String = "") { + self.title = title + } + + enum CodingKeys: String, CodingKey { + case title + } + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.title = try container.decode(String.self, forKey: .title) + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(title, forKey: .title) + } +} + +extension Category { + + static var defaults: [Category] { + [ + .init(title: "🙇🏾‍♂️ Study"), + .init(title: "🤝 Routine"), + .init(title: "🏠 Family") + ] + } +} + +struct CreateCategoryView: View { + + @Environment(\.dismiss) private var dismiss + @Environment(\.modelContext) var modelContext + + @State private var title: String = "" + @Query private var categories: [Category] + + var body: some View { + List { + Section("Category Title") { + TextField("Enter title here", + text: $title) + Button("Add Category") { + withAnimation { + let category = Category(title: title) + modelContext.insert(category) + category.items = [] + title = "" + } + } + .disabled(title.isEmpty) + } + + Section("Categories") { + + if categories.isEmpty { + + ContentUnavailableView("No Categories", + systemImage: "archivebox") + + } else { + + ForEach(categories) { category in + Text(category.title) + .swipeActions { + Button(role: .destructive) { + withAnimation { + modelContext.delete(category) + } + } label: { + Label("Delete", systemImage: "trash.fill") + } + } + } + } + + + } + + } + .navigationTitle("Add Category") + .toolbar { + + ToolbarItem(placement: .cancellationAction) { + Button("Dismiss") { + dismiss() + } + } + } + } +} + +// Xcode 15 Beta 2 has a previews bug so this is why we're commenting this out... +// Ref: https://mastodon.social/@denisdepalatis/110561280521551715//#Preview { +// NavigationStack { +// CreateCategoryView() +// } +//} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/DefaultCategories.json" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/DefaultCategories.json" new file mode 100644 index 0000000..c4d0105 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/DefaultCategories.json" @@ -0,0 +1,11 @@ +[ + { + "title": "🙇🏾‍♂️ Study" + }, + { + "title": "🤝 Routine" + }, + { + "title": "🏠 Family" + } +] diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/DefaultToDos.json" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/DefaultToDos.json" new file mode 100644 index 0000000..f32800a --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/DefaultToDos.json" @@ -0,0 +1,23 @@ +[ + { + "title": "Sangria Time 🍹", + "isCritical": true, + "isCompleted": false, + "category": null, + "imageName": "holiday" + }, + { + "title": "Go & get wedding suit", + "isCritical": false, + "isCompleted": true, + "category": null + }, + { + "title": "Go visit family", + "isCritical": true, + "isCompleted": false, + "category": { + "title": "🏠 Family" + } + } +] diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Item.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Item.swift" new file mode 100644 index 0000000..fae1bca --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Item.swift" @@ -0,0 +1,91 @@ +// +// Item.swift +// ToDos +// +// Created by Tunde Adegoroye on 06/06/2023. +// + +import Foundation +import SwiftData +import UIKit + +@Model +final class Item: Codable { + var title: String + var timestamp: Date + var isCritical: Bool + var isCompleted: Bool + + @Relationship(deleteRule: .nullify, inverse: \Category.items) + var category: Category? + + @Attribute(.externalStorage) + var image: Data? + + enum CodingKeys: String, CodingKey { + case title + case timestamp + case isCritical + case isCompleted + case category + case imageName + } + + init(title: String = "", + timestamp: Date = .now, + isCritical: Bool = false, + isCompleted: Bool = false) { + self.title = title + self.timestamp = timestamp + self.isCritical = isCritical + self.isCompleted = isCompleted + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.title = try container.decode(String.self, forKey: .title) + self.timestamp = Date.randomDateNextWeek() ?? .now + self.isCritical = try container.decode(Bool.self, forKey: .isCritical) + self.isCompleted = try container.decode(Bool.self, forKey: .isCompleted) + self.category = try container.decodeIfPresent(Category.self, forKey: .category) + + if let imageName = try container.decodeIfPresent(String.self, forKey: .imageName), + let imageData = UIImage(named: imageName) { + self.image = imageData.jpegData(compressionQuality: 0.8) + } + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(title, forKey: .title) + try container.encode(timestamp, forKey: .timestamp) + try container.encode(isCritical, forKey: .isCritical) + try container.encode(isCompleted, forKey: .isCompleted) + try container.encode(category, forKey: .category) + } +} + +extension Date { + static func randomDateNextWeek() -> Date? { + let calendar = Calendar.current + let currentDate = Date() + + guard let nextWeekStartDate = calendar.date(byAdding: .day, value: 7, to: currentDate) else { + return nil + } + + let randomTimeInterval = TimeInterval.random(in: 0..<7 * 24 * 60 * 60) // Random time within a week + let randomDate = nextWeekStartDate.addingTimeInterval(randomTimeInterval) + + return randomDate + } +} + +extension Item { + + static var dummy: Item { + .init(title: "Item 1", + timestamp: .now, + isCritical: true) + } +} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/ItemsContainer.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/ItemsContainer.swift" new file mode 100644 index 0000000..2ecb0f8 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/ItemsContainer.swift" @@ -0,0 +1,39 @@ +// +// ItemsContainer.swift +// ToDos +// +// Created by Tunde Adegoroye on 27/06/2023. +// + +import Foundation +import SwiftData + +actor ItemsContainer { + + @MainActor + static func create(shouldCreateDefaults: inout Bool) -> ModelContainer { + let schema = Schema([Item.self]) + let configuration = ModelConfiguration() + let container = try! ModelContainer(for: schema, configurations: [configuration]) + if shouldCreateDefaults { + shouldCreateDefaults = false + + let categories = DefaultsJSON.decode(from: "DefaultCategories", + type: [Category].self) + categories?.forEach { container.mainContext.insert($0) } + + let items = DefaultsJSON.decode(from: "DefaultToDos", + type: [Item].self) + + items?.forEach { item in + + container.mainContext.insert(item) + item.category?.items?.append(item) + } + + } + + return container + } + +} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/ModifyTodoView.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/ModifyTodoView.swift" new file mode 100644 index 0000000..fb859e6 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/ModifyTodoView.swift" @@ -0,0 +1,143 @@ +// +// ModifyTodoView.swift +// ToDos +// +// Created by Tunde Adegoroye on 06/06/2023. +// + +import SwiftUI +import SwiftData +import PhotosUI + +struct CreateTodoView: View { + + @Environment(\.modelContext) var modelContext + @Environment(\.dismiss) var dismiss + + @Query private var categories: [Category] + + @State var item = Item() + @State var selectedCategory: Category? + @State var selectedPhoto: PhotosPickerItem? + + var body: some View { + List { + + Section("To do title") { + TextField("Name", text: $item.title) + } + + Section("General") { + DatePicker("Choose a date", + selection: $item.timestamp) + Toggle("Important?", isOn: $item.isCritical) + } + + Section("Select A Category") { + + + if categories.isEmpty { + + ContentUnavailableView("No Categories", + systemImage: "archivebox") + + } else { + Picker("", selection: $selectedCategory) { + + ForEach(categories) { category in + Text(category.title) + .tag(category as Category?) + } + + Text("None") + .tag(nil as Category?) + } + .labelsHidden() + .pickerStyle(.inline) + } + + + } + + Section { + + if let selectedPhotoData = item.image, + let uiImage = UIImage(data: selectedPhotoData) { + Image(uiImage: uiImage) + .resizable() + .scaledToFill() + .frame(maxWidth: .infinity, maxHeight: 300) + } + + PhotosPicker(selection: $selectedPhoto, + matching: .images, + photoLibrary: .shared()) { + Label("Add Image", systemImage: "photo") + } + + if item.image != nil { + + Button(role: .destructive) { + withAnimation { + selectedPhoto = nil + item.image = nil + } + } label: { + Label("Remove Image", systemImage: "xmark") + .foregroundStyle(.red) + } + } + + } + + + Section { + Button("Create") { + save() + dismiss() + } + } + + } + .navigationTitle("Create ToDo") + .toolbar { + + ToolbarItem(placement: .cancellationAction) { + Button("Dismiss") { + dismiss() + } + } + + ToolbarItem(placement: .primaryAction) { + Button("Done") { + save() + dismiss() + } + .disabled(item.title.isEmpty) + } + } + .task(id: selectedPhoto) { + if let data = try? await selectedPhoto?.loadTransferable(type: Data.self) { + item.image = data + } + } + } +} + +private extension CreateTodoView { + + func save() { + modelContext.insert(item) + item.category = selectedCategory + selectedCategory?.items?.append(item) + } +} +// +// Xcode 15 Beta 2 has a previews bug so this is why we're commenting this out... +// Ref: https://mastodon.social/@denisdepalatis/110561280521551715 +#Preview { + NavigationStack { + CreateTodoView() + .modelContainer(for: Item.self) + } +} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Preview Content/Preview Assets.xcassets/._Contents.json" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Preview Content/Preview Assets.xcassets/._Contents.json" new file mode 100644 index 0000000..c893829 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Preview Content/Preview Assets.xcassets/._Contents.json" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Preview Content/Preview Assets.xcassets/Contents.json" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Preview Content/Preview Assets.xcassets/Contents.json" new file mode 100644 index 0000000..73c0059 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Preview Content/Preview Assets.xcassets/Contents.json" @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/ToDosApp.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/ToDosApp.swift" new file mode 100644 index 0000000..2e6023f --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/ToDosApp.swift" @@ -0,0 +1,22 @@ +// +// ToDosApp.swift +// ToDos +// +// Created by Tunde Adegoroye on 06/06/2023. +// + +import SwiftUI +import SwiftData + +@main +struct ToDosApp: App { + + @AppStorage("isFirstTimeLaunch") private var isFirstTimeLaunch: Bool = true + + var body: some Scene { + WindowGroup { + ContentView() + } + .modelContainer(ItemsContainer.create(shouldCreateDefaults: &isFirstTimeLaunch)) + } +} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/TodoView.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/TodoView.swift" new file mode 100644 index 0000000..2fc75dd --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/TodoView.swift" @@ -0,0 +1,40 @@ +// +// TodoView.swift +// ToDos +// +// Created by Tunde Adegoroye on 06/06/2023. +// + +import SwiftUI + +struct TodoView: View { + + let item: Item + + var body: some View { + VStack(alignment: .leading) { + + if item.isCritical { + Image(systemName: "exclamationmark.3") + .symbolVariant(.fill) + .foregroundColor(.red) + .font(.largeTitle) + .bold() + } + + Text(item.title) + .font(.largeTitle) + .bold() + + Text("\(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))") + .font(.callout) + } + + } +} + +// Xcode 15 Beta 2 has a previews bug so this is why we're commenting this out... +// Ref: https://mastodon.social/@denisdepalatis/110561280521551715 +//#Preview { +// TodoView(item: Item.dummy) +//} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/UpdateToDoView.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/UpdateToDoView.swift" new file mode 100644 index 0000000..29227e1 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/UpdateToDoView.swift" @@ -0,0 +1,133 @@ +// +// UpdateToDoView.swift +// ToDos +// +// Created by Tunde Adegoroye on 08/06/2023. +// + +import SwiftUI +import SwiftData +import PhotosUI + +class OriginalToDo { + var title: String + var timestamp: Date + var isCritical: Bool + + init(item: Item) { + self.title = item.title + self.timestamp = item.timestamp + self.isCritical = item.isCritical + } +} + +struct UpdateToDoView: View { + + @Environment(\.dismiss) var dismiss + + @Query private var categories: [Category] + + @State var selectedCategory: Category? + @State var selectedPhoto: PhotosPickerItem? + + @Bindable var item: Item + + var body: some View { + List { + + Section("To do title") { + TextField("Name", text: $item.title) + } + + Section("General") { + DatePicker("Choose a date", + selection: $item.timestamp) + Toggle("Important?", isOn: $item.isCritical) + } + + + + Section("Select A Category") { + + + if categories.isEmpty { + + ContentUnavailableView("No Categories", + systemImage: "archivebox") + + } else { + + Picker("", selection: $selectedCategory) { + + ForEach(categories) { category in + Text(category.title) + .tag(category as Category?) + } + + Text("None") + .tag(nil as Category?) + } + .labelsHidden() + .pickerStyle(.inline) + } + + + } + + Section { + + if let selectedPhotoData = item.image, + let uiImage = UIImage(data: selectedPhotoData) { + Image(uiImage: uiImage) + .resizable() + .scaledToFill() + .frame(maxWidth: .infinity, maxHeight: 300) + } + + PhotosPicker(selection: $selectedPhoto, + matching: .images, + photoLibrary: .shared()) { + Label("Add Image", systemImage: "photo") + } + + if item.image != nil { + + Button(role: .destructive) { + withAnimation { + selectedPhoto = nil + item.image = nil + } + } label: { + Label("Remove Image", systemImage: "xmark") + .foregroundStyle(.red) + } + } + + } + + Section { + Button("Update") { + item.category = selectedCategory + dismiss() + } + } + } + .navigationTitle("Update ToDo") + .onAppear(perform: { + selectedCategory = item.category + }) + .task(id: selectedPhoto) { + if let data = try? await selectedPhoto?.loadTransferable(type: Data.self) { + item.image = data + } + } + } +} + +// Xcode 15 Beta 2 has a previews bug so this is why we're commenting this out... +// Ref: https://mastodon.social/@denisdepalatis/110561280521551715 +//#Preview { +// UpdateToDoView(item: Item.dummy) +// .modelContainer(for: Item.self) +// +//} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/._.DS_Store" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/._.DS_Store" new file mode 100644 index 0000000..8e82ed9 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/._.DS_Store" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/._ToDos" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/._ToDos" new file mode 100644 index 0000000..904e3be Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/._ToDos" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/._.DS_Store" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/._.DS_Store" new file mode 100644 index 0000000..8e82ed9 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/._.DS_Store" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/._ToDos.xcodeproj" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/._ToDos.xcodeproj" new file mode 100644 index 0000000..6eea53c Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/._ToDos.xcodeproj" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/._project.xcworkspace" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/._project.xcworkspace" new file mode 100644 index 0000000..a89e4e2 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/._project.xcworkspace" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.pbxproj" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.pbxproj" new file mode 100644 index 0000000..e4344fa --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.pbxproj" @@ -0,0 +1,406 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + E7325DC82A2F839400130DAE /* ToDosApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7325DC72A2F839400130DAE /* ToDosApp.swift */; }; + E7325DCA2A2F839400130DAE /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7325DC92A2F839400130DAE /* ContentView.swift */; }; + E7325DCC2A2F839500130DAE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E7325DCB2A2F839500130DAE /* Assets.xcassets */; }; + E7325DCF2A2F839500130DAE /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E7325DCE2A2F839500130DAE /* Preview Assets.xcassets */; }; + E7325DD12A2F839500130DAE /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7325DD02A2F839500130DAE /* Item.swift */; }; + E7325DD82A2F83B600130DAE /* ModifyTodoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7325DD72A2F83B600130DAE /* ModifyTodoView.swift */; }; + E7325DDA2A2F8A9C00130DAE /* TodoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7325DD92A2F8A9C00130DAE /* TodoView.swift */; }; + E79BE2CB2A3259F5002AFC0C /* UpdateToDoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E79BE2CA2A3259F5002AFC0C /* UpdateToDoView.swift */; }; + E79BE2CD2A325A32002AFC0C /* CreateCategoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E79BE2CC2A325A32002AFC0C /* CreateCategoryView.swift */; }; + E7C2BEDF2A532E7D00197F9A /* DefaultCategories.json in Resources */ = {isa = PBXBuildFile; fileRef = E7C2BEDE2A532E7D00197F9A /* DefaultCategories.json */; }; + E7C2BEE12A5332FA00197F9A /* CategoriesJSONDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7C2BEE02A5332FA00197F9A /* CategoriesJSONDecoder.swift */; }; + E7E15E5D2A84057B0048DFAA /* SwiftUIImageViewer in Frameworks */ = {isa = PBXBuildFile; productRef = E7E15E5C2A84057B0048DFAA /* SwiftUIImageViewer */; }; + E7F0333E2A4B449700B40C93 /* ItemsContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7F0333D2A4B449700B40C93 /* ItemsContainer.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + E7325DC42A2F839400130DAE /* ToDos.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ToDos.app; sourceTree = BUILT_PRODUCTS_DIR; }; + E7325DC72A2F839400130DAE /* ToDosApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToDosApp.swift; sourceTree = ""; }; + E7325DC92A2F839400130DAE /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + E7325DCB2A2F839500130DAE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + E7325DCE2A2F839500130DAE /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + E7325DD02A2F839500130DAE /* Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Item.swift; sourceTree = ""; }; + E7325DD72A2F83B600130DAE /* ModifyTodoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModifyTodoView.swift; sourceTree = ""; }; + E7325DD92A2F8A9C00130DAE /* TodoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodoView.swift; sourceTree = ""; }; + E79BE2CA2A3259F5002AFC0C /* UpdateToDoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateToDoView.swift; sourceTree = ""; }; + E79BE2CC2A325A32002AFC0C /* CreateCategoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateCategoryView.swift; sourceTree = ""; }; + E7C2BEDE2A532E7D00197F9A /* DefaultCategories.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = DefaultCategories.json; sourceTree = ""; }; + E7C2BEE02A5332FA00197F9A /* CategoriesJSONDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoriesJSONDecoder.swift; sourceTree = ""; }; + E7F0333D2A4B449700B40C93 /* ItemsContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemsContainer.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + E7325DC12A2F839400130DAE /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E7E15E5D2A84057B0048DFAA /* SwiftUIImageViewer in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + E7325DBB2A2F839400130DAE = { + isa = PBXGroup; + children = ( + E7325DC62A2F839400130DAE /* ToDos */, + E7325DC52A2F839400130DAE /* Products */, + ); + sourceTree = ""; + }; + E7325DC52A2F839400130DAE /* Products */ = { + isa = PBXGroup; + children = ( + E7325DC42A2F839400130DAE /* ToDos.app */, + ); + name = Products; + sourceTree = ""; + }; + E7325DC62A2F839400130DAE /* ToDos */ = { + isa = PBXGroup; + children = ( + E7325DC72A2F839400130DAE /* ToDosApp.swift */, + E7325DC92A2F839400130DAE /* ContentView.swift */, + E7325DD92A2F8A9C00130DAE /* TodoView.swift */, + E7325DCB2A2F839500130DAE /* Assets.xcassets */, + E7325DD02A2F839500130DAE /* Item.swift */, + E7325DCD2A2F839500130DAE /* Preview Content */, + E7325DD72A2F83B600130DAE /* ModifyTodoView.swift */, + E79BE2CA2A3259F5002AFC0C /* UpdateToDoView.swift */, + E79BE2CC2A325A32002AFC0C /* CreateCategoryView.swift */, + E7C2BEE02A5332FA00197F9A /* CategoriesJSONDecoder.swift */, + E7F0333D2A4B449700B40C93 /* ItemsContainer.swift */, + E7C2BEDE2A532E7D00197F9A /* DefaultCategories.json */, + ); + path = ToDos; + sourceTree = ""; + }; + E7325DCD2A2F839500130DAE /* Preview Content */ = { + isa = PBXGroup; + children = ( + E7325DCE2A2F839500130DAE /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + E7325DC32A2F839400130DAE /* ToDos */ = { + isa = PBXNativeTarget; + buildConfigurationList = E7325DD42A2F839500130DAE /* Build configuration list for PBXNativeTarget "ToDos" */; + buildPhases = ( + E7325DC02A2F839400130DAE /* Sources */, + E7325DC12A2F839400130DAE /* Frameworks */, + E7325DC22A2F839400130DAE /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ToDos; + packageProductDependencies = ( + E7E15E5C2A84057B0048DFAA /* SwiftUIImageViewer */, + ); + productName = ToDos; + productReference = E7325DC42A2F839400130DAE /* ToDos.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + E7325DBC2A2F839400130DAE /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1500; + LastUpgradeCheck = 1500; + TargetAttributes = { + E7325DC32A2F839400130DAE = { + CreatedOnToolsVersion = 15.0; + }; + }; + }; + buildConfigurationList = E7325DBF2A2F839400130DAE /* Build configuration list for PBXProject "ToDos" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = E7325DBB2A2F839400130DAE; + packageReferences = ( + E7E15E5B2A84057B0048DFAA /* XCRemoteSwiftPackageReference "swiftui-image-viewer" */, + ); + productRefGroup = E7325DC52A2F839400130DAE /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + E7325DC32A2F839400130DAE /* ToDos */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + E7325DC22A2F839400130DAE /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E7325DCF2A2F839500130DAE /* Preview Assets.xcassets in Resources */, + E7325DCC2A2F839500130DAE /* Assets.xcassets in Resources */, + E7C2BEDF2A532E7D00197F9A /* DefaultCategories.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + E7325DC02A2F839400130DAE /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E7325DD82A2F83B600130DAE /* ModifyTodoView.swift in Sources */, + E7325DCA2A2F839400130DAE /* ContentView.swift in Sources */, + E79BE2CD2A325A32002AFC0C /* CreateCategoryView.swift in Sources */, + E7C2BEE12A5332FA00197F9A /* CategoriesJSONDecoder.swift in Sources */, + E7325DDA2A2F8A9C00130DAE /* TodoView.swift in Sources */, + E7F0333E2A4B449700B40C93 /* ItemsContainer.swift in Sources */, + E79BE2CB2A3259F5002AFC0C /* UpdateToDoView.swift in Sources */, + E7325DD12A2F839500130DAE /* Item.swift in Sources */, + E7325DC82A2F839400130DAE /* ToDosApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + E7325DD22A2F839500130DAE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + E7325DD32A2F839500130DAE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + E7325DD52A2F839500130DAE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"ToDos/Preview Content\""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.tundsdev.ToDos; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + E7325DD62A2F839500130DAE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"ToDos/Preview Content\""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.tundsdev.ToDos; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + E7325DBF2A2F839400130DAE /* Build configuration list for PBXProject "ToDos" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E7325DD22A2F839500130DAE /* Debug */, + E7325DD32A2F839500130DAE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E7325DD42A2F839500130DAE /* Build configuration list for PBXNativeTarget "ToDos" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E7325DD52A2F839500130DAE /* Debug */, + E7325DD62A2F839500130DAE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + E7E15E5B2A84057B0048DFAA /* XCRemoteSwiftPackageReference "swiftui-image-viewer" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/fuzzzlove/swiftui-image-viewer.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + E7E15E5C2A84057B0048DFAA /* SwiftUIImageViewer */ = { + isa = XCSwiftPackageProductDependency; + package = E7E15E5B2A84057B0048DFAA /* XCRemoteSwiftPackageReference "swiftui-image-viewer" */; + productName = SwiftUIImageViewer; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = E7325DBC2A2F839400130DAE /* Project object */; +} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/contents.xcworkspacedata" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/contents.xcworkspacedata" new file mode 100644 index 0000000..919434a --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/contents.xcworkspacedata" @@ -0,0 +1,7 @@ + + + + + diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist" new file mode 100644 index 0000000..18d9810 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist" @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved" new file mode 100644 index 0000000..c5dd3c9 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved" @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "swiftui-image-viewer", + "kind" : "remoteSourceControl", + "location" : "https://github.com/fuzzzlove/swiftui-image-viewer.git", + "state" : { + "revision" : "ca77123064ae046eac2be01654ee756085c2b182", + "version" : "1.0.0" + } + } + ], + "version" : 2 +} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/xcuserdata/tunde.adegoroye.xcuserdatad/UserInterfaceState.xcuserstate" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/xcuserdata/tunde.adegoroye.xcuserdatad/UserInterfaceState.xcuserstate" new file mode 100644 index 0000000..52c76c0 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/xcuserdata/tunde.adegoroye.xcuserdatad/UserInterfaceState.xcuserstate" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist" new file mode 100644 index 0000000..90dcc1d --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist" @@ -0,0 +1,104 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcschemes/xcschememanagement.plist" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcschemes/xcschememanagement.plist" new file mode 100644 index 0000000..5f45eb7 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcschemes/xcschememanagement.plist" @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + ToDos.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._CategoriesJSONDecoder.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._CategoriesJSONDecoder.swift" new file mode 100644 index 0000000..6ecbf49 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._CategoriesJSONDecoder.swift" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ContentView.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ContentView.swift" new file mode 100644 index 0000000..9ee85e7 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ContentView.swift" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._CreateCategoryView.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._CreateCategoryView.swift" new file mode 100644 index 0000000..ab655f8 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._CreateCategoryView.swift" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._DefaultCategories.json" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._DefaultCategories.json" new file mode 100644 index 0000000..a1e2db3 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._DefaultCategories.json" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._Item.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._Item.swift" new file mode 100644 index 0000000..3e0c27b Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._Item.swift" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ItemsContainer.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ItemsContainer.swift" new file mode 100644 index 0000000..e3c061d Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ItemsContainer.swift" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ModifyTodoView.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ModifyTodoView.swift" new file mode 100644 index 0000000..fc2c0bb Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ModifyTodoView.swift" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ToDosApp.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ToDosApp.swift" new file mode 100644 index 0000000..17f7365 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ToDosApp.swift" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._TodoView.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._TodoView.swift" new file mode 100644 index 0000000..5a591a3 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._TodoView.swift" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._UpdateToDoView.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._UpdateToDoView.swift" new file mode 100644 index 0000000..06324e0 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._UpdateToDoView.swift" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/._Contents.json" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/._Contents.json" new file mode 100644 index 0000000..c893829 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/._Contents.json" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/._Contents.json" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/._Contents.json" new file mode 100644 index 0000000..c893829 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/._Contents.json" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/Contents.json" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/Contents.json" new file mode 100644 index 0000000..eb87897 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/Contents.json" @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/AppIcon.appiconset/Contents.json" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/AppIcon.appiconset/Contents.json" new file mode 100644 index 0000000..13613e3 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/AppIcon.appiconset/Contents.json" @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/Contents.json" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/Contents.json" new file mode 100644 index 0000000..73c0059 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/Contents.json" @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/CategoriesJSONDecoder.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/CategoriesJSONDecoder.swift" new file mode 100644 index 0000000..77f9dec --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/CategoriesJSONDecoder.swift" @@ -0,0 +1,25 @@ +// +// CategoriesJSONDecoder.swift +// ToDos +// +// Created by Tunde Adegoroye on 03/07/2023. +// + +import Foundation + +struct CategoryResponse: Codable { + let title: String +} + +struct CategoriesJSONDecoder { + + static func decode(from fileName: String) -> [CategoryResponse] { + guard let url = Bundle.main.url(forResource: fileName, withExtension: "json"), + let data = try? Data(contentsOf: url), + let categories = try? JSONDecoder().decode([CategoryResponse].self, from: data) else { + return [] + } + + return categories + } +} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ContentView.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ContentView.swift" new file mode 100644 index 0000000..dd31bca --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ContentView.swift" @@ -0,0 +1,282 @@ +// +// ContentView.swift +// ToDos +// +// Created by Tunde Adegoroye on 06/06/2023. +// + +import SwiftUI +import SwiftData +import SwiftUIImageViewer + +enum SortOption: String, CaseIterable { + case title + case date + case category +} + +extension SortOption { + + var systemImage: String { + switch self { + case .title: + "textformat.size.larger" + case .date: + "calendar" + case .category: + "folder" + } + } +} + +struct ContentView: View { + + @Environment(\.modelContext) private var modelContext + @Query private var items: [Item] + + @State private var searchQuery = "" + @State private var showCreateCategory = false + @State private var showCreateToDo = false + @State private var toDoToEdit: Item? + + @State private var isImageViewerPresented = false + + @State private var selectedSortOption = SortOption.allCases.first! + + var filteredItems: [Item] { + + if searchQuery.isEmpty { + return items.sort(on: selectedSortOption) + } + + let filteredItems = items.compactMap { item in + + let titleContainsQuery = item.title.range(of: searchQuery, + options: .caseInsensitive) != nil + + let categoryTitleContainsQuery = item.category?.title.range(of: searchQuery, + options: .caseInsensitive) != nil + + return (titleContainsQuery || categoryTitleContainsQuery) ? item : nil + } + + return filteredItems.sort(on: selectedSortOption) + + } + + var body: some View { + NavigationStack { + List { + ForEach(filteredItems) { item in + VStack { + HStack { + VStack(alignment: .leading) { + + if item.isCritical { + Image(systemName: "exclamationmark.3") + .symbolVariant(.fill) + .foregroundColor(.red) + .font(.largeTitle) + .bold() + } + + Text(item.title) + .font(.largeTitle) + .bold() + + Text("\(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .shortened))") + .font(.callout) + + if let category = item.category { + Text(category.title) + .foregroundStyle(Color.blue) + .bold() + .padding(.horizontal) + .padding(.vertical, 8) + .background(Color.blue.opacity(0.1), + in: RoundedRectangle(cornerRadius: 8, + style: .continuous)) + } + + } + + + Spacer() + + Button { + withAnimation { + item.isCompleted.toggle() + } + } label: { + + Image(systemName: "checkmark") + .symbolVariant(.circle.fill) + .foregroundStyle(item.isCompleted ? .green : .gray) + .font(.largeTitle) + } + .buttonStyle(.plain) + } + if let selectedPhotoData = item.image, + let uiImage = UIImage(data: selectedPhotoData) { + Image(uiImage: uiImage) + .resizable() + .scaledToFill() + .frame(maxWidth: .infinity, maxHeight: 120) + .clipShape(RoundedRectangle(cornerRadius: 10, + style: .continuous)) + .onTapGesture { + isImageViewerPresented = true + } + .fullScreenCover(isPresented: $isImageViewerPresented) { + SwiftUIImageViewer(image: Image(uiImage: uiImage)) + .overlay(alignment: .topTrailing) { + Button { + isImageViewerPresented = false + } label: { + Image(systemName: "xmark") + .font(.headline) + } + .buttonStyle(.bordered) + .clipShape(Circle()) + .tint(.purple) + .padding() + } + } + } + + } + .swipeActions { + + Button(role: .destructive) { + + withAnimation { + modelContext.delete(item) + } + + } label: { + Label("Delete", systemImage: "trash.fill") + } + + Button { + toDoToEdit = item + } label: { + Label("Edit", systemImage: "pencil") + } + .tint(.orange) + + } + } + } + .navigationTitle("My To Do List") + .animation(/*@START_MENU_TOKEN@*/.easeIn/*@END_MENU_TOKEN@*/, value: filteredItems) + .searchable(text: $searchQuery, + prompt: "Search for a to do or a category") + .overlay { + if filteredItems.isEmpty { + ContentUnavailableView.search + } + } + .sheet(item: $toDoToEdit, + onDismiss: { + toDoToEdit = nil + }, + content: { editItem in + NavigationStack { + UpdateToDoView(item: editItem) + .interactiveDismissDisabled() + } + }) + .sheet(isPresented: $showCreateCategory, + content: { + NavigationStack { + CreateCategoryView() + } + }) + .sheet(isPresented: $showCreateToDo, + content: { + NavigationStack { + CreateTodoView() + } + }) + .toolbar { + + ToolbarItemGroup(placement: .primaryAction) { + Button { + showCreateCategory.toggle() + } label: { + Image(systemName: "plus") + } + } + + ToolbarItemGroup(placement: .topBarTrailing) { + + Menu { + Picker("", selection: $selectedSortOption) { + ForEach(SortOption.allCases, + id: \.rawValue) { option in + Label(option.rawValue.capitalized, + systemImage: option.systemImage) + .tag(option) + } + } + .labelsHidden() + + } label: { + Image(systemName: "ellipsis") + .symbolVariant(.circle) + } + + } + + } + .safeAreaInset(edge: .bottom, + alignment: .leading) { + Button(action: { + showCreateToDo.toggle() + }, label: { + Label("New ToDo", systemImage: "plus") + .bold() + .font(.title2) + .padding(8) + .background(.gray.opacity(0.1), + in: Capsule()) + .padding(.leading) + .symbolVariant(.circle.fill) + + }) + + } + } + } + + private func delete(item: Item) { + withAnimation { + modelContext.delete(item) + } + } +} + +private extension [Item] { + + func sort(on option: SortOption) -> [Item] { + switch option { + case .title: + self.sorted(by: { $0.title < $1.title }) + case .date: + self.sorted(by: { $0.timestamp < $1.timestamp }) + case .category: + self.sorted(by: { + guard let firstItemTitle = $0.category?.title, + let secondItemTitle = $1.category?.title else { return false } + return firstItemTitle < secondItemTitle + }) + } + } +} + +// Xcode 15 Beta 2 has a previews bug so this is why we're commenting this out... +// Ref: https://mastodon.social/@denisdepalatis/110561280521551715 +//#Preview { +// ContentView() +// .modelContainer(for: Item.self, inMemory: true) +//} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/CreateCategoryView.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/CreateCategoryView.swift" new file mode 100644 index 0000000..41adde3 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/CreateCategoryView.swift" @@ -0,0 +1,103 @@ +// +// CreateCategoryView.swift +// ToDos +// +// Created by Tunde Adegoroye on 08/06/2023. +// + +import SwiftUI +import SwiftData + +@Model +class Category { + + @Attribute(.unique) + var title: String + + var items: [Item]? + + init(title: String = "") { + self.title = title + } +} + +extension Category { + + static var defaults: [Category] { + [ + .init(title: "🙇🏾‍♂️ Study"), + .init(title: "🤝 Routine"), + .init(title: "🏠 Family") + ] + } +} + +struct CreateCategoryView: View { + + @Environment(\.dismiss) private var dismiss + @Environment(\.modelContext) var modelContext + + @State private var title: String = "" + @Query private var categories: [Category] + + var body: some View { + List { + Section("Category Title") { + TextField("Enter title here", + text: $title) + Button("Add Category") { + withAnimation { + let category = Category(title: title) + modelContext.insert(category) + category.items = [] + title = "" + } + } + .disabled(title.isEmpty) + } + + Section("Categories") { + + if categories.isEmpty { + + ContentUnavailableView("No Categories", + systemImage: "archivebox") + + } else { + + ForEach(categories) { category in + Text(category.title) + .swipeActions { + Button(role: .destructive) { + withAnimation { + modelContext.delete(category) + } + } label: { + Label("Delete", systemImage: "trash.fill") + } + } + } + } + + + } + + } + .navigationTitle("Add Category") + .toolbar { + + ToolbarItem(placement: .cancellationAction) { + Button("Dismiss") { + dismiss() + } + } + } + } +} + +// Xcode 15 Beta 2 has a previews bug so this is why we're commenting this out... +// Ref: https://mastodon.social/@denisdepalatis/110561280521551715//#Preview { +// NavigationStack { +// CreateCategoryView() +// } +//} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/DefaultCategories.json" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/DefaultCategories.json" new file mode 100644 index 0000000..c4d0105 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/DefaultCategories.json" @@ -0,0 +1,11 @@ +[ + { + "title": "🙇🏾‍♂️ Study" + }, + { + "title": "🤝 Routine" + }, + { + "title": "🏠 Family" + } +] diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Item.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Item.swift" new file mode 100644 index 0000000..bf48327 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Item.swift" @@ -0,0 +1,42 @@ +// +// Item.swift +// ToDos +// +// Created by Tunde Adegoroye on 06/06/2023. +// + +import Foundation +import SwiftData + +@Model +final class Item { + var title: String + var timestamp: Date + var isCritical: Bool + var isCompleted: Bool + + @Relationship(deleteRule: .nullify, inverse: \Category.items) + var category: Category? + + @Attribute(.externalStorage) + var image: Data? + + init(title: String = "", + timestamp: Date = .now, + isCritical: Bool = false, + isCompleted: Bool = false) { + self.title = title + self.timestamp = timestamp + self.isCritical = isCritical + self.isCompleted = isCompleted + } +} + +extension Item { + + static var dummy: Item { + .init(title: "Item 1", + timestamp: .now, + isCritical: true) + } +} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ItemsContainer.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ItemsContainer.swift" new file mode 100644 index 0000000..8126e10 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ItemsContainer.swift" @@ -0,0 +1,32 @@ +// +// ItemsContainer.swift +// ToDos +// +// Created by Tunde Adegoroye on 27/06/2023. +// + +import Foundation +import SwiftData + +actor ItemsContainer { + + @MainActor + static func create(shouldCreateDefaults: inout Bool) -> ModelContainer { + let schema = Schema([Item.self]) + let configuration = ModelConfiguration() + let container = try! ModelContainer(for: schema, configurations: [configuration]) + if shouldCreateDefaults { + shouldCreateDefaults = false + let categories = CategoriesJSONDecoder.decode(from: "DefaultCategories") + if !categories.isEmpty { + categories.forEach { item in + let category = Category(title: item.title) + container.mainContext.insert(category) + } + } + } + + return container + } + +} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ModifyTodoView.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ModifyTodoView.swift" new file mode 100644 index 0000000..fb859e6 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ModifyTodoView.swift" @@ -0,0 +1,143 @@ +// +// ModifyTodoView.swift +// ToDos +// +// Created by Tunde Adegoroye on 06/06/2023. +// + +import SwiftUI +import SwiftData +import PhotosUI + +struct CreateTodoView: View { + + @Environment(\.modelContext) var modelContext + @Environment(\.dismiss) var dismiss + + @Query private var categories: [Category] + + @State var item = Item() + @State var selectedCategory: Category? + @State var selectedPhoto: PhotosPickerItem? + + var body: some View { + List { + + Section("To do title") { + TextField("Name", text: $item.title) + } + + Section("General") { + DatePicker("Choose a date", + selection: $item.timestamp) + Toggle("Important?", isOn: $item.isCritical) + } + + Section("Select A Category") { + + + if categories.isEmpty { + + ContentUnavailableView("No Categories", + systemImage: "archivebox") + + } else { + Picker("", selection: $selectedCategory) { + + ForEach(categories) { category in + Text(category.title) + .tag(category as Category?) + } + + Text("None") + .tag(nil as Category?) + } + .labelsHidden() + .pickerStyle(.inline) + } + + + } + + Section { + + if let selectedPhotoData = item.image, + let uiImage = UIImage(data: selectedPhotoData) { + Image(uiImage: uiImage) + .resizable() + .scaledToFill() + .frame(maxWidth: .infinity, maxHeight: 300) + } + + PhotosPicker(selection: $selectedPhoto, + matching: .images, + photoLibrary: .shared()) { + Label("Add Image", systemImage: "photo") + } + + if item.image != nil { + + Button(role: .destructive) { + withAnimation { + selectedPhoto = nil + item.image = nil + } + } label: { + Label("Remove Image", systemImage: "xmark") + .foregroundStyle(.red) + } + } + + } + + + Section { + Button("Create") { + save() + dismiss() + } + } + + } + .navigationTitle("Create ToDo") + .toolbar { + + ToolbarItem(placement: .cancellationAction) { + Button("Dismiss") { + dismiss() + } + } + + ToolbarItem(placement: .primaryAction) { + Button("Done") { + save() + dismiss() + } + .disabled(item.title.isEmpty) + } + } + .task(id: selectedPhoto) { + if let data = try? await selectedPhoto?.loadTransferable(type: Data.self) { + item.image = data + } + } + } +} + +private extension CreateTodoView { + + func save() { + modelContext.insert(item) + item.category = selectedCategory + selectedCategory?.items?.append(item) + } +} +// +// Xcode 15 Beta 2 has a previews bug so this is why we're commenting this out... +// Ref: https://mastodon.social/@denisdepalatis/110561280521551715 +#Preview { + NavigationStack { + CreateTodoView() + .modelContainer(for: Item.self) + } +} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Preview Content/Preview Assets.xcassets/._Contents.json" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Preview Content/Preview Assets.xcassets/._Contents.json" new file mode 100644 index 0000000..c893829 Binary files /dev/null and "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Preview Content/Preview Assets.xcassets/._Contents.json" differ diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Preview Content/Preview Assets.xcassets/Contents.json" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Preview Content/Preview Assets.xcassets/Contents.json" new file mode 100644 index 0000000..73c0059 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Preview Content/Preview Assets.xcassets/Contents.json" @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ToDosApp.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ToDosApp.swift" new file mode 100644 index 0000000..2e6023f --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ToDosApp.swift" @@ -0,0 +1,22 @@ +// +// ToDosApp.swift +// ToDos +// +// Created by Tunde Adegoroye on 06/06/2023. +// + +import SwiftUI +import SwiftData + +@main +struct ToDosApp: App { + + @AppStorage("isFirstTimeLaunch") private var isFirstTimeLaunch: Bool = true + + var body: some Scene { + WindowGroup { + ContentView() + } + .modelContainer(ItemsContainer.create(shouldCreateDefaults: &isFirstTimeLaunch)) + } +} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/TodoView.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/TodoView.swift" new file mode 100644 index 0000000..2fc75dd --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/TodoView.swift" @@ -0,0 +1,40 @@ +// +// TodoView.swift +// ToDos +// +// Created by Tunde Adegoroye on 06/06/2023. +// + +import SwiftUI + +struct TodoView: View { + + let item: Item + + var body: some View { + VStack(alignment: .leading) { + + if item.isCritical { + Image(systemName: "exclamationmark.3") + .symbolVariant(.fill) + .foregroundColor(.red) + .font(.largeTitle) + .bold() + } + + Text(item.title) + .font(.largeTitle) + .bold() + + Text("\(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))") + .font(.callout) + } + + } +} + +// Xcode 15 Beta 2 has a previews bug so this is why we're commenting this out... +// Ref: https://mastodon.social/@denisdepalatis/110561280521551715 +//#Preview { +// TodoView(item: Item.dummy) +//} diff --git "a/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/UpdateToDoView.swift" "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/UpdateToDoView.swift" new file mode 100644 index 0000000..29227e1 --- /dev/null +++ "b/How To Preload Relationships Into SwiftData ModelContainer \342\232\241\357\270\217 | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/UpdateToDoView.swift" @@ -0,0 +1,133 @@ +// +// UpdateToDoView.swift +// ToDos +// +// Created by Tunde Adegoroye on 08/06/2023. +// + +import SwiftUI +import SwiftData +import PhotosUI + +class OriginalToDo { + var title: String + var timestamp: Date + var isCritical: Bool + + init(item: Item) { + self.title = item.title + self.timestamp = item.timestamp + self.isCritical = item.isCritical + } +} + +struct UpdateToDoView: View { + + @Environment(\.dismiss) var dismiss + + @Query private var categories: [Category] + + @State var selectedCategory: Category? + @State var selectedPhoto: PhotosPickerItem? + + @Bindable var item: Item + + var body: some View { + List { + + Section("To do title") { + TextField("Name", text: $item.title) + } + + Section("General") { + DatePicker("Choose a date", + selection: $item.timestamp) + Toggle("Important?", isOn: $item.isCritical) + } + + + + Section("Select A Category") { + + + if categories.isEmpty { + + ContentUnavailableView("No Categories", + systemImage: "archivebox") + + } else { + + Picker("", selection: $selectedCategory) { + + ForEach(categories) { category in + Text(category.title) + .tag(category as Category?) + } + + Text("None") + .tag(nil as Category?) + } + .labelsHidden() + .pickerStyle(.inline) + } + + + } + + Section { + + if let selectedPhotoData = item.image, + let uiImage = UIImage(data: selectedPhotoData) { + Image(uiImage: uiImage) + .resizable() + .scaledToFill() + .frame(maxWidth: .infinity, maxHeight: 300) + } + + PhotosPicker(selection: $selectedPhoto, + matching: .images, + photoLibrary: .shared()) { + Label("Add Image", systemImage: "photo") + } + + if item.image != nil { + + Button(role: .destructive) { + withAnimation { + selectedPhoto = nil + item.image = nil + } + } label: { + Label("Remove Image", systemImage: "xmark") + .foregroundStyle(.red) + } + } + + } + + Section { + Button("Update") { + item.category = selectedCategory + dismiss() + } + } + } + .navigationTitle("Update ToDo") + .onAppear(perform: { + selectedCategory = item.category + }) + .task(id: selectedPhoto) { + if let data = try? await selectedPhoto?.loadTransferable(type: Data.self) { + item.image = data + } + } + } +} + +// Xcode 15 Beta 2 has a previews bug so this is why we're commenting this out... +// Ref: https://mastodon.social/@denisdepalatis/110561280521551715 +//#Preview { +// UpdateToDoView(item: Item.dummy) +// .modelContainer(for: Item.self) +// +//} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/._.DS_Store b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/._.DS_Store new file mode 100644 index 0000000..8e82ed9 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/._.DS_Store differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/._ToDos b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/._ToDos new file mode 100644 index 0000000..904e3be Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/._ToDos differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/._.DS_Store b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/._.DS_Store new file mode 100644 index 0000000..8e82ed9 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/._.DS_Store differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/._ToDos.xcodeproj b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/._ToDos.xcodeproj new file mode 100644 index 0000000..59671bb Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/._ToDos.xcodeproj differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/._project.xcworkspace b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/._project.xcworkspace new file mode 100644 index 0000000..e042c86 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/._project.xcworkspace differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.pbxproj b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.pbxproj new file mode 100644 index 0000000..e2315a1 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.pbxproj @@ -0,0 +1,414 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + E703406A2A9D04E300BD054F /* DefaultToDos.json in Resources */ = {isa = PBXBuildFile; fileRef = E70340692A9D04E300BD054F /* DefaultToDos.json */; }; + E7325DC82A2F839400130DAE /* ToDosApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7325DC72A2F839400130DAE /* ToDosApp.swift */; }; + E7325DCA2A2F839400130DAE /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7325DC92A2F839400130DAE /* ContentView.swift */; }; + E7325DCC2A2F839500130DAE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E7325DCB2A2F839500130DAE /* Assets.xcassets */; }; + E7325DCF2A2F839500130DAE /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E7325DCE2A2F839500130DAE /* Preview Assets.xcassets */; }; + E7325DD12A2F839500130DAE /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7325DD02A2F839500130DAE /* Item.swift */; }; + E7325DD82A2F83B600130DAE /* ModifyTodoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7325DD72A2F83B600130DAE /* ModifyTodoView.swift */; }; + E7325DDA2A2F8A9C00130DAE /* TodoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7325DD92A2F8A9C00130DAE /* TodoView.swift */; }; + E7493F0E2A9E620C0067C946 /* PreviewSampleData.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7493F0D2A9E620C0067C946 /* PreviewSampleData.swift */; }; + E79BE2CB2A3259F5002AFC0C /* UpdateToDoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E79BE2CA2A3259F5002AFC0C /* UpdateToDoView.swift */; }; + E79BE2CD2A325A32002AFC0C /* CreateCategoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E79BE2CC2A325A32002AFC0C /* CreateCategoryView.swift */; }; + E7C2BEDF2A532E7D00197F9A /* DefaultCategories.json in Resources */ = {isa = PBXBuildFile; fileRef = E7C2BEDE2A532E7D00197F9A /* DefaultCategories.json */; }; + E7C2BEE12A5332FA00197F9A /* CategoriesJSONDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7C2BEE02A5332FA00197F9A /* CategoriesJSONDecoder.swift */; }; + E7E15E5D2A84057B0048DFAA /* SwiftUIImageViewer in Frameworks */ = {isa = PBXBuildFile; productRef = E7E15E5C2A84057B0048DFAA /* SwiftUIImageViewer */; }; + E7F0333E2A4B449700B40C93 /* ItemsContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7F0333D2A4B449700B40C93 /* ItemsContainer.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + E70340692A9D04E300BD054F /* DefaultToDos.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = DefaultToDos.json; sourceTree = ""; }; + E7325DC42A2F839400130DAE /* ToDos.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ToDos.app; sourceTree = BUILT_PRODUCTS_DIR; }; + E7325DC72A2F839400130DAE /* ToDosApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToDosApp.swift; sourceTree = ""; }; + E7325DC92A2F839400130DAE /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + E7325DCB2A2F839500130DAE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + E7325DCE2A2F839500130DAE /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + E7325DD02A2F839500130DAE /* Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Item.swift; sourceTree = ""; }; + E7325DD72A2F83B600130DAE /* ModifyTodoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModifyTodoView.swift; sourceTree = ""; }; + E7325DD92A2F8A9C00130DAE /* TodoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodoView.swift; sourceTree = ""; }; + E7493F0D2A9E620C0067C946 /* PreviewSampleData.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PreviewSampleData.swift; sourceTree = ""; }; + E79BE2CA2A3259F5002AFC0C /* UpdateToDoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateToDoView.swift; sourceTree = ""; }; + E79BE2CC2A325A32002AFC0C /* CreateCategoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateCategoryView.swift; sourceTree = ""; }; + E7C2BEDE2A532E7D00197F9A /* DefaultCategories.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = DefaultCategories.json; sourceTree = ""; }; + E7C2BEE02A5332FA00197F9A /* CategoriesJSONDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoriesJSONDecoder.swift; sourceTree = ""; }; + E7F0333D2A4B449700B40C93 /* ItemsContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemsContainer.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + E7325DC12A2F839400130DAE /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E7E15E5D2A84057B0048DFAA /* SwiftUIImageViewer in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + E7325DBB2A2F839400130DAE = { + isa = PBXGroup; + children = ( + E7325DC62A2F839400130DAE /* ToDos */, + E7325DC52A2F839400130DAE /* Products */, + ); + sourceTree = ""; + }; + E7325DC52A2F839400130DAE /* Products */ = { + isa = PBXGroup; + children = ( + E7325DC42A2F839400130DAE /* ToDos.app */, + ); + name = Products; + sourceTree = ""; + }; + E7325DC62A2F839400130DAE /* ToDos */ = { + isa = PBXGroup; + children = ( + E7493F0D2A9E620C0067C946 /* PreviewSampleData.swift */, + E7325DC72A2F839400130DAE /* ToDosApp.swift */, + E7325DC92A2F839400130DAE /* ContentView.swift */, + E7325DD92A2F8A9C00130DAE /* TodoView.swift */, + E7325DCB2A2F839500130DAE /* Assets.xcassets */, + E7325DD02A2F839500130DAE /* Item.swift */, + E7325DCD2A2F839500130DAE /* Preview Content */, + E7325DD72A2F83B600130DAE /* ModifyTodoView.swift */, + E79BE2CA2A3259F5002AFC0C /* UpdateToDoView.swift */, + E79BE2CC2A325A32002AFC0C /* CreateCategoryView.swift */, + E7C2BEE02A5332FA00197F9A /* CategoriesJSONDecoder.swift */, + E7F0333D2A4B449700B40C93 /* ItemsContainer.swift */, + E7C2BEDE2A532E7D00197F9A /* DefaultCategories.json */, + E70340692A9D04E300BD054F /* DefaultToDos.json */, + ); + path = ToDos; + sourceTree = ""; + }; + E7325DCD2A2F839500130DAE /* Preview Content */ = { + isa = PBXGroup; + children = ( + E7325DCE2A2F839500130DAE /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + E7325DC32A2F839400130DAE /* ToDos */ = { + isa = PBXNativeTarget; + buildConfigurationList = E7325DD42A2F839500130DAE /* Build configuration list for PBXNativeTarget "ToDos" */; + buildPhases = ( + E7325DC02A2F839400130DAE /* Sources */, + E7325DC12A2F839400130DAE /* Frameworks */, + E7325DC22A2F839400130DAE /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ToDos; + packageProductDependencies = ( + E7E15E5C2A84057B0048DFAA /* SwiftUIImageViewer */, + ); + productName = ToDos; + productReference = E7325DC42A2F839400130DAE /* ToDos.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + E7325DBC2A2F839400130DAE /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1500; + LastUpgradeCheck = 1500; + TargetAttributes = { + E7325DC32A2F839400130DAE = { + CreatedOnToolsVersion = 15.0; + }; + }; + }; + buildConfigurationList = E7325DBF2A2F839400130DAE /* Build configuration list for PBXProject "ToDos" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = E7325DBB2A2F839400130DAE; + packageReferences = ( + E7E15E5B2A84057B0048DFAA /* XCRemoteSwiftPackageReference "swiftui-image-viewer" */, + ); + productRefGroup = E7325DC52A2F839400130DAE /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + E7325DC32A2F839400130DAE /* ToDos */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + E7325DC22A2F839400130DAE /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E7325DCF2A2F839500130DAE /* Preview Assets.xcassets in Resources */, + E7325DCC2A2F839500130DAE /* Assets.xcassets in Resources */, + E7C2BEDF2A532E7D00197F9A /* DefaultCategories.json in Resources */, + E703406A2A9D04E300BD054F /* DefaultToDos.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + E7325DC02A2F839400130DAE /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E7325DD82A2F83B600130DAE /* ModifyTodoView.swift in Sources */, + E7325DCA2A2F839400130DAE /* ContentView.swift in Sources */, + E79BE2CD2A325A32002AFC0C /* CreateCategoryView.swift in Sources */, + E7C2BEE12A5332FA00197F9A /* CategoriesJSONDecoder.swift in Sources */, + E7325DDA2A2F8A9C00130DAE /* TodoView.swift in Sources */, + E7F0333E2A4B449700B40C93 /* ItemsContainer.swift in Sources */, + E7493F0E2A9E620C0067C946 /* PreviewSampleData.swift in Sources */, + E79BE2CB2A3259F5002AFC0C /* UpdateToDoView.swift in Sources */, + E7325DD12A2F839500130DAE /* Item.swift in Sources */, + E7325DC82A2F839400130DAE /* ToDosApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + E7325DD22A2F839500130DAE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + E7325DD32A2F839500130DAE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + E7325DD52A2F839500130DAE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"ToDos/Preview Content\""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.tundsdev.ToDos; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + E7325DD62A2F839500130DAE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"ToDos/Preview Content\""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.tundsdev.ToDos; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + E7325DBF2A2F839400130DAE /* Build configuration list for PBXProject "ToDos" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E7325DD22A2F839500130DAE /* Debug */, + E7325DD32A2F839500130DAE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E7325DD42A2F839500130DAE /* Build configuration list for PBXNativeTarget "ToDos" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E7325DD52A2F839500130DAE /* Debug */, + E7325DD62A2F839500130DAE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + E7E15E5B2A84057B0048DFAA /* XCRemoteSwiftPackageReference "swiftui-image-viewer" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/fuzzzlove/swiftui-image-viewer.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + E7E15E5C2A84057B0048DFAA /* SwiftUIImageViewer */ = { + isa = XCSwiftPackageProductDependency; + package = E7E15E5B2A84057B0048DFAA /* XCRemoteSwiftPackageReference "swiftui-image-viewer" */; + productName = SwiftUIImageViewer; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = E7325DBC2A2F839400130DAE /* Project object */; +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..c5dd3c9 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "swiftui-image-viewer", + "kind" : "remoteSourceControl", + "location" : "https://github.com/fuzzzlove/swiftui-image-viewer.git", + "state" : { + "revision" : "ca77123064ae046eac2be01654ee756085c2b182", + "version" : "1.0.0" + } + } + ], + "version" : 2 +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/xcuserdata/tunde.adegoroye.xcuserdatad/UserInterfaceState.xcuserstate b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/xcuserdata/tunde.adegoroye.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..e6f380a Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/project.xcworkspace/xcuserdata/tunde.adegoroye.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..2068192 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,40 @@ + + + + + + + + + + + + + diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcschemes/xcschememanagement.plist b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..5f45eb7 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + ToDos.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._CategoriesJSONDecoder.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._CategoriesJSONDecoder.swift new file mode 100644 index 0000000..399fc05 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._CategoriesJSONDecoder.swift differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ContentView.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ContentView.swift new file mode 100644 index 0000000..3f293e0 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ContentView.swift differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._CreateCategoryView.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._CreateCategoryView.swift new file mode 100644 index 0000000..5bcb5c0 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._CreateCategoryView.swift differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._DefaultCategories.json b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._DefaultCategories.json new file mode 100644 index 0000000..0e01ddd Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._DefaultCategories.json differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._DefaultToDos.json b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._DefaultToDos.json new file mode 100644 index 0000000..e97da6d Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._DefaultToDos.json differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._Item.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._Item.swift new file mode 100644 index 0000000..ef88392 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._Item.swift differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ItemsContainer.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ItemsContainer.swift new file mode 100644 index 0000000..279acc8 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ItemsContainer.swift differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ModifyTodoView.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ModifyTodoView.swift new file mode 100644 index 0000000..7596729 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ModifyTodoView.swift differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._PreviewSampleData.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._PreviewSampleData.swift new file mode 100644 index 0000000..8073c5f Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._PreviewSampleData.swift differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ToDosApp.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ToDosApp.swift new file mode 100644 index 0000000..b5c8e0c Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._ToDosApp.swift differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._TodoView.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._TodoView.swift new file mode 100644 index 0000000..5ee1381 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._TodoView.swift differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._UpdateToDoView.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._UpdateToDoView.swift new file mode 100644 index 0000000..3028780 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/._UpdateToDoView.swift differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/._Contents.json b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/._Contents.json new file mode 100644 index 0000000..c893829 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/._Contents.json differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/Contents.json b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/AppIcon.appiconset/Contents.json b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/Contents.json b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/holiday.imageset/Contents.json b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/holiday.imageset/Contents.json new file mode 100644 index 0000000..3cfc538 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/holiday.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "holiday.jpeg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/holiday.imageset/holiday.jpeg b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/holiday.imageset/holiday.jpeg new file mode 100644 index 0000000..5007c69 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Assets.xcassets/holiday.imageset/holiday.jpeg differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/CategoriesJSONDecoder.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/CategoriesJSONDecoder.swift new file mode 100644 index 0000000..5e0adaf --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/CategoriesJSONDecoder.swift @@ -0,0 +1,29 @@ +// +// CategoriesJSONDecoder.swift +// ToDos +// +// Created by Tunde Adegoroye on 03/07/2023. +// + +import Foundation + +struct DefaultsJSON { + + static func decode(from fileName: String, type: T.Type) -> T? { + + do { + guard let url = Bundle.main.url(forResource: fileName, withExtension: "json"), + let data = try? Data(contentsOf: url) else { + return nil + } + + let result = try JSONDecoder().decode(T.self, from: data) + + return result + } catch { + print(error) + return nil + } + + } +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/ContentView.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/ContentView.swift new file mode 100644 index 0000000..75ecf70 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/ContentView.swift @@ -0,0 +1,299 @@ +// +// ContentView.swift +// ToDos +// +// Created by Tunde Adegoroye on 06/06/2023. +// + +import SwiftUI +import SwiftData +import SwiftUIImageViewer + +enum SortOption: String, CaseIterable { + case title + case date + case category +} + +extension SortOption { + + var systemImage: String { + switch self { + case .title: + "textformat.size.larger" + case .date: + "calendar" + case .category: + "folder" + } + } +} + +struct ContentView: View { + + @Environment(\.modelContext) private var modelContext + @Query private var items: [Item] + + @State private var searchQuery = "" + @State private var showCreateCategory = false + @State private var showCreateToDo = false + @State private var toDoToEdit: Item? + + @State private var isImageViewerPresented = false + + @State private var selectedSortOption = SortOption.allCases.first! + + var filteredItems: [Item] { + + if searchQuery.isEmpty { + return items.sort(on: selectedSortOption) + } + + let filteredItems = items.compactMap { item in + + let titleContainsQuery = item.title.range(of: searchQuery, + options: .caseInsensitive) != nil + + let categoryTitleContainsQuery = item.category?.title.range(of: searchQuery, + options: .caseInsensitive) != nil + + return (titleContainsQuery || categoryTitleContainsQuery) ? item : nil + } + + return filteredItems.sort(on: selectedSortOption) + + } + + var body: some View { + NavigationStack { + List { + ForEach(filteredItems) { item in + VStack { + HStack { + VStack(alignment: .leading) { + + if item.isCritical { + Image(systemName: "exclamationmark.3") + .symbolVariant(.fill) + .foregroundColor(.red) + .font(.largeTitle) + .bold() + } + + Text(item.title) + .font(.largeTitle) + .bold() + + Text("\(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .shortened))") + .font(.callout) + + if let category = item.category { + Text(category.title) + .foregroundStyle(Color.blue) + .bold() + .padding(.horizontal) + .padding(.vertical, 8) + .background(Color.blue.opacity(0.1), + in: RoundedRectangle(cornerRadius: 8, + style: .continuous)) + } + + } + + + Spacer() + + Button { + withAnimation { + item.isCompleted.toggle() + } + } label: { + + Image(systemName: "checkmark") + .symbolVariant(.circle.fill) + .foregroundStyle(item.isCompleted ? .green : .gray) + .font(.largeTitle) + } + .buttonStyle(.plain) + } + if let selectedPhotoData = item.image, + let uiImage = UIImage(data: selectedPhotoData) { + Image(uiImage: uiImage) + .resizable() + .scaledToFill() + .frame(maxWidth: .infinity, maxHeight: 120) + .clipShape(RoundedRectangle(cornerRadius: 10, + style: .continuous)) + .onTapGesture { + isImageViewerPresented = true + } + .fullScreenCover(isPresented: $isImageViewerPresented) { + SwiftUIImageViewer(image: Image(uiImage: uiImage)) + .overlay(alignment: .topTrailing) { + Button { + isImageViewerPresented = false + } label: { + Image(systemName: "xmark") + .font(.headline) + } + .buttonStyle(.bordered) + .clipShape(Circle()) + .tint(.purple) + .padding() + } + } + } + + } + .swipeActions { + + Button(role: .destructive) { + + withAnimation { + modelContext.delete(item) + } + + } label: { + Label("Delete", systemImage: "trash.fill") + } + + Button { + toDoToEdit = item + } label: { + Label("Edit", systemImage: "pencil") + } + .tint(.orange) + + } + } + } + .navigationTitle("My To Do List") + .animation(/*@START_MENU_TOKEN@*/.easeIn/*@END_MENU_TOKEN@*/, value: filteredItems) + .searchable(text: $searchQuery, + prompt: "Search for a to do or a category") + .overlay { + if filteredItems.isEmpty { + ContentUnavailableView.search + } + } + .sheet(item: $toDoToEdit, + onDismiss: { + toDoToEdit = nil + }, + content: { editItem in + NavigationStack { + UpdateToDoView(item: editItem) + .interactiveDismissDisabled() + } + }) + .sheet(isPresented: $showCreateCategory, + content: { + NavigationStack { + CreateCategoryView() + } + }) + .sheet(isPresented: $showCreateToDo, + content: { + NavigationStack { + CreateTodoView() + } + }) + .toolbar { + + ToolbarItemGroup(placement: .primaryAction) { + Button { + showCreateCategory.toggle() + } label: { + Image(systemName: "plus") + } + } + + ToolbarItemGroup(placement: .topBarTrailing) { + + Menu { + Picker("", selection: $selectedSortOption) { + ForEach(SortOption.allCases, + id: \.rawValue) { option in + Label(option.rawValue.capitalized, + systemImage: option.systemImage) + .tag(option) + } + } + .labelsHidden() + + } label: { + Image(systemName: "ellipsis") + .symbolVariant(.circle) + } + + } + + } + .safeAreaInset(edge: .bottom, + alignment: .leading) { + Button(action: { + showCreateToDo.toggle() + }, label: { + Label("New ToDo", systemImage: "plus") + .bold() + .font(.title2) + .padding(8) + .background(.gray.opacity(0.1), + in: Capsule()) + .padding(.leading) + .symbolVariant(.circle.fill) + + }) + + } + } + } + + private func delete(item: Item) { + withAnimation { + modelContext.delete(item) + } + } +} + +private extension [Item] { + + func sort(on option: SortOption) -> [Item] { + switch option { + case .title: + self.sorted(by: { $0.title < $1.title }) + case .date: + self.sorted(by: { $0.timestamp < $1.timestamp }) + case .category: + self.sorted(by: { + guard let firstItemTitle = $0.category?.title, + let secondItemTitle = $1.category?.title else { return false } + return firstItemTitle < secondItemTitle + }) + } + } +} + + + + + + + + + + + + +#Preview("To Do List With Data") { + let preview = PreviewContainer([Item.self]) + preview.add(items: Item.sample(4)) + return ContentView() + .modelContainer(preview.container) +} + + + + + + diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/CreateCategoryView.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/CreateCategoryView.swift new file mode 100644 index 0000000..4d6a628 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/CreateCategoryView.swift @@ -0,0 +1,128 @@ +// +// CreateCategoryView.swift +// ToDos +// +// Created by Tunde Adegoroye on 08/06/2023. +// + +import SwiftUI +import SwiftData + +@Model +class Category: Codable { + + @Attribute(.unique) + var title: String + + var items: [Item]? + + init(title: String = "") { + self.title = title + } + + enum CodingKeys: String, CodingKey { + case title + } + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.title = try container.decode(String.self, forKey: .title) + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(title, forKey: .title) + } +} + +extension Category { + + static func sample(_ count: Int) -> [Category] { + var categories: [Category] = [] + for i in 0.. Date? { + let calendar = Calendar.current + let currentDate = Date() + + guard let nextWeekStartDate = calendar.date(byAdding: .day, value: 7, to: currentDate) else { + return nil + } + + let randomTimeInterval = TimeInterval.random(in: 0..<7 * 24 * 60 * 60) // Random time within a week + let randomDate = nextWeekStartDate.addingTimeInterval(randomTimeInterval) + + return randomDate + } +} + +extension Item { + + static var dummy: Item { + .init(title: "Item 1", + timestamp: .now, + isCritical: true) + } + + static func sample(_ count: Int) -> [Item] { + + let tempCategories = Category.sample(3) + + var items: [Item] = [] + for i in 0.. ModelContainer { + let schema = Schema([Item.self]) + let configuration = ModelConfiguration() + let container = try! ModelContainer(for: schema, configurations: [configuration]) + if shouldCreateDefaults { + shouldCreateDefaults = false + + let categories = DefaultsJSON.decode(from: "DefaultCategories", + type: [Category].self) + categories?.forEach { container.mainContext.insert($0) } + + let items = DefaultsJSON.decode(from: "DefaultToDos", + type: [Item].self) + + items?.forEach { item in + + container.mainContext.insert(item) + item.category?.items?.append(item) + } + + } + + return container + } + +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/ModifyTodoView.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/ModifyTodoView.swift new file mode 100644 index 0000000..8da8eb8 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/ModifyTodoView.swift @@ -0,0 +1,144 @@ +// +// ModifyTodoView.swift +// ToDos +// +// Created by Tunde Adegoroye on 06/06/2023. +// + +import SwiftUI +import SwiftData +import PhotosUI + +struct CreateTodoView: View { + + @Environment(\.modelContext) var modelContext + @Environment(\.dismiss) var dismiss + + @Query private var categories: [Category] + + @State var item = Item() + @State var selectedCategory: Category? + @State var selectedPhoto: PhotosPickerItem? + + var body: some View { + List { + + Section("To do title") { + TextField("Name", text: $item.title) + } + + Section("General") { + DatePicker("Choose a date", + selection: $item.timestamp) + Toggle("Important?", isOn: $item.isCritical) + } + + Section("Select A Category") { + + + if categories.isEmpty { + + ContentUnavailableView("No Categories", + systemImage: "archivebox") + + } else { + Picker("", selection: $selectedCategory) { + + ForEach(categories) { category in + Text(category.title) + .tag(category as Category?) + } + + Text("None") + .tag(nil as Category?) + } + .labelsHidden() + .pickerStyle(.inline) + } + + + } + + Section { + + if let selectedPhotoData = item.image, + let uiImage = UIImage(data: selectedPhotoData) { + Image(uiImage: uiImage) + .resizable() + .scaledToFill() + .frame(maxWidth: .infinity, maxHeight: 300) + } + + PhotosPicker(selection: $selectedPhoto, + matching: .images, + photoLibrary: .shared()) { + Label("Add Image", systemImage: "photo") + } + + if item.image != nil { + + Button(role: .destructive) { + withAnimation { + selectedPhoto = nil + item.image = nil + } + } label: { + Label("Remove Image", systemImage: "xmark") + .foregroundStyle(.red) + } + } + + } + + + Section { + Button("Create") { + save() + dismiss() + } + } + + } + .navigationTitle("Create ToDo") + .toolbar { + + ToolbarItem(placement: .cancellationAction) { + Button("Dismiss") { + dismiss() + } + } + + ToolbarItem(placement: .primaryAction) { + Button("Done") { + save() + dismiss() + } + .disabled(item.title.isEmpty) + } + } + .task(id: selectedPhoto) { + if let data = try? await selectedPhoto?.loadTransferable(type: Data.self) { + item.image = data + } + } + } +} + +private extension CreateTodoView { + + func save() { + modelContext.insert(item) + item.category = selectedCategory + selectedCategory?.items?.append(item) + } +} +// +// Xcode 15 Beta 2 has a previews bug so this is why we're commenting this out... +// Ref: https://mastodon.social/@denisdepalatis/110561280521551715 +#Preview { + let preview = PreviewContainer([Item.self]) + return NavigationStack { + CreateTodoView() + .modelContainer(preview.container) + } +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Preview Content/Preview Assets.xcassets/._Contents.json b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Preview Content/Preview Assets.xcassets/._Contents.json new file mode 100644 index 0000000..c893829 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Preview Content/Preview Assets.xcassets/._Contents.json differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Preview Content/Preview Assets.xcassets/Contents.json b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/PreviewSampleData.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/PreviewSampleData.swift new file mode 100644 index 0000000..6d3a165 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/PreviewSampleData.swift @@ -0,0 +1,25 @@ +// +// PreviewSampleData.swift +// ToDos +// +// Created by Tunde Adegoroye on 20/06/2023. +// + +import SwiftData + +struct PreviewContainer { + + let container: ModelContainer! + + init(_ types: [any PersistentModel.Type]) { + let schema = Schema(types) + let configuration = ModelConfiguration(isStoredInMemoryOnly: true) + self.container = try! ModelContainer(for: schema, configurations: [configuration]) + } + + func add(items: [any PersistentModel]) { + Task { @MainActor in + items.forEach { container.mainContext.insert($0) } + } + } +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/ToDosApp.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/ToDosApp.swift new file mode 100644 index 0000000..2e6023f --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/ToDosApp.swift @@ -0,0 +1,22 @@ +// +// ToDosApp.swift +// ToDos +// +// Created by Tunde Adegoroye on 06/06/2023. +// + +import SwiftUI +import SwiftData + +@main +struct ToDosApp: App { + + @AppStorage("isFirstTimeLaunch") private var isFirstTimeLaunch: Bool = true + + var body: some Scene { + WindowGroup { + ContentView() + } + .modelContainer(ItemsContainer.create(shouldCreateDefaults: &isFirstTimeLaunch)) + } +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/TodoView.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/TodoView.swift new file mode 100644 index 0000000..36c50d9 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/TodoView.swift @@ -0,0 +1,43 @@ +// +// TodoView.swift +// ToDos +// +// Created by Tunde Adegoroye on 06/06/2023. +// + +import SwiftUI + +struct TodoView: View { + + let item: Item + + var body: some View { + VStack(alignment: .leading) { + + if item.isCritical { + Image(systemName: "exclamationmark.3") + .symbolVariant(.fill) + .foregroundColor(.red) + .font(.largeTitle) + .bold() + } + + Text(item.title) + .font(.largeTitle) + .bold() + + Text("\(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))") + .font(.callout) + } + + } +} + +// Xcode 15 Beta 2 has a previews bug so this is why we're commenting this out... +// Ref: https://mastodon.social/@denisdepalatis/110561280521551715 +#Preview { + let preview = PreviewContainer([Item.self]) + let toDoItem = Item.sample(1).first! + return TodoView(item: toDoItem) + .modelContainer(preview.container) +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/UpdateToDoView.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/UpdateToDoView.swift new file mode 100644 index 0000000..2306b88 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Final/ToDos/ToDos/UpdateToDoView.swift @@ -0,0 +1,137 @@ +// +// UpdateToDoView.swift +// ToDos +// +// Created by Tunde Adegoroye on 08/06/2023. +// + +import SwiftUI +import SwiftData +import PhotosUI + +class OriginalToDo { + var title: String + var timestamp: Date + var isCritical: Bool + + init(item: Item) { + self.title = item.title + self.timestamp = item.timestamp + self.isCritical = item.isCritical + } +} + +struct UpdateToDoView: View { + + @Environment(\.dismiss) var dismiss + + @Query private var categories: [Category] + + @State var selectedCategory: Category? + @State var selectedPhoto: PhotosPickerItem? + + @Bindable var item: Item + + var body: some View { + List { + + Section("To do title") { + TextField("Name", text: $item.title) + } + + Section("General") { + DatePicker("Choose a date", + selection: $item.timestamp) + Toggle("Important?", isOn: $item.isCritical) + } + + + + Section("Select A Category") { + + + if categories.isEmpty { + + ContentUnavailableView("No Categories", + systemImage: "archivebox") + + } else { + + Picker("", selection: $selectedCategory) { + + ForEach(categories) { category in + Text(category.title) + .tag(category as Category?) + } + + Text("None") + .tag(nil as Category?) + } + .labelsHidden() + .pickerStyle(.inline) + } + + + } + + Section { + + if let selectedPhotoData = item.image, + let uiImage = UIImage(data: selectedPhotoData) { + Image(uiImage: uiImage) + .resizable() + .scaledToFill() + .frame(maxWidth: .infinity, maxHeight: 300) + } + + PhotosPicker(selection: $selectedPhoto, + matching: .images, + photoLibrary: .shared()) { + Label("Add Image", systemImage: "photo") + } + + if item.image != nil { + + Button(role: .destructive) { + withAnimation { + selectedPhoto = nil + item.image = nil + } + } label: { + Label("Remove Image", systemImage: "xmark") + .foregroundStyle(.red) + } + } + + } + + Section { + Button("Update") { + item.category = selectedCategory + dismiss() + } + } + } + .navigationTitle("Update ToDo") + .onAppear(perform: { + selectedCategory = item.category + }) + .task(id: selectedPhoto) { + if let data = try? await selectedPhoto?.loadTransferable(type: Data.self) { + item.image = data + } + } + } +} + +// Xcode 15 Beta 2 has a previews bug so this is why we're commenting this out... +// Ref: https://mastodon.social/@denisdepalatis/110561280521551715 + +#Preview { + let preview = PreviewContainer([Item.self]) + let toDoItem = Item.sample(1).first! + return NavigationStack { + UpdateToDoView(item: Item.dummy) + .modelContainer(preview.container) + } +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/._.DS_Store b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/._.DS_Store new file mode 100644 index 0000000..8e82ed9 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/._.DS_Store differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/._ToDos b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/._ToDos new file mode 100644 index 0000000..904e3be Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/._ToDos differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/._.DS_Store b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/._.DS_Store new file mode 100644 index 0000000..8e82ed9 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/._.DS_Store differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/._ToDos.xcodeproj b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/._ToDos.xcodeproj new file mode 100644 index 0000000..2531395 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/._ToDos.xcodeproj differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/._project.xcworkspace b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/._project.xcworkspace new file mode 100644 index 0000000..d80ef5e Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/._project.xcworkspace differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.pbxproj b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.pbxproj new file mode 100644 index 0000000..d733f00 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.pbxproj @@ -0,0 +1,410 @@ +// !$*UTF8*$! +{ + archiveVersion = 1; + classes = { + }; + objectVersion = 56; + objects = { + +/* Begin PBXBuildFile section */ + E703406A2A9D04E300BD054F /* DefaultToDos.json in Resources */ = {isa = PBXBuildFile; fileRef = E70340692A9D04E300BD054F /* DefaultToDos.json */; }; + E7325DC82A2F839400130DAE /* ToDosApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7325DC72A2F839400130DAE /* ToDosApp.swift */; }; + E7325DCA2A2F839400130DAE /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7325DC92A2F839400130DAE /* ContentView.swift */; }; + E7325DCC2A2F839500130DAE /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E7325DCB2A2F839500130DAE /* Assets.xcassets */; }; + E7325DCF2A2F839500130DAE /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = E7325DCE2A2F839500130DAE /* Preview Assets.xcassets */; }; + E7325DD12A2F839500130DAE /* Item.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7325DD02A2F839500130DAE /* Item.swift */; }; + E7325DD82A2F83B600130DAE /* ModifyTodoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7325DD72A2F83B600130DAE /* ModifyTodoView.swift */; }; + E7325DDA2A2F8A9C00130DAE /* TodoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7325DD92A2F8A9C00130DAE /* TodoView.swift */; }; + E79BE2CB2A3259F5002AFC0C /* UpdateToDoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E79BE2CA2A3259F5002AFC0C /* UpdateToDoView.swift */; }; + E79BE2CD2A325A32002AFC0C /* CreateCategoryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = E79BE2CC2A325A32002AFC0C /* CreateCategoryView.swift */; }; + E7C2BEDF2A532E7D00197F9A /* DefaultCategories.json in Resources */ = {isa = PBXBuildFile; fileRef = E7C2BEDE2A532E7D00197F9A /* DefaultCategories.json */; }; + E7C2BEE12A5332FA00197F9A /* CategoriesJSONDecoder.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7C2BEE02A5332FA00197F9A /* CategoriesJSONDecoder.swift */; }; + E7E15E5D2A84057B0048DFAA /* SwiftUIImageViewer in Frameworks */ = {isa = PBXBuildFile; productRef = E7E15E5C2A84057B0048DFAA /* SwiftUIImageViewer */; }; + E7F0333E2A4B449700B40C93 /* ItemsContainer.swift in Sources */ = {isa = PBXBuildFile; fileRef = E7F0333D2A4B449700B40C93 /* ItemsContainer.swift */; }; +/* End PBXBuildFile section */ + +/* Begin PBXFileReference section */ + E70340692A9D04E300BD054F /* DefaultToDos.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = DefaultToDos.json; sourceTree = ""; }; + E7325DC42A2F839400130DAE /* ToDos.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = ToDos.app; sourceTree = BUILT_PRODUCTS_DIR; }; + E7325DC72A2F839400130DAE /* ToDosApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ToDosApp.swift; sourceTree = ""; }; + E7325DC92A2F839400130DAE /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + E7325DCB2A2F839500130DAE /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + E7325DCE2A2F839500130DAE /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; }; + E7325DD02A2F839500130DAE /* Item.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Item.swift; sourceTree = ""; }; + E7325DD72A2F83B600130DAE /* ModifyTodoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ModifyTodoView.swift; sourceTree = ""; }; + E7325DD92A2F8A9C00130DAE /* TodoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TodoView.swift; sourceTree = ""; }; + E79BE2CA2A3259F5002AFC0C /* UpdateToDoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpdateToDoView.swift; sourceTree = ""; }; + E79BE2CC2A325A32002AFC0C /* CreateCategoryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateCategoryView.swift; sourceTree = ""; }; + E7C2BEDE2A532E7D00197F9A /* DefaultCategories.json */ = {isa = PBXFileReference; lastKnownFileType = text.json; path = DefaultCategories.json; sourceTree = ""; }; + E7C2BEE02A5332FA00197F9A /* CategoriesJSONDecoder.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CategoriesJSONDecoder.swift; sourceTree = ""; }; + E7F0333D2A4B449700B40C93 /* ItemsContainer.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ItemsContainer.swift; sourceTree = ""; }; +/* End PBXFileReference section */ + +/* Begin PBXFrameworksBuildPhase section */ + E7325DC12A2F839400130DAE /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + E7E15E5D2A84057B0048DFAA /* SwiftUIImageViewer in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXFrameworksBuildPhase section */ + +/* Begin PBXGroup section */ + E7325DBB2A2F839400130DAE = { + isa = PBXGroup; + children = ( + E7325DC62A2F839400130DAE /* ToDos */, + E7325DC52A2F839400130DAE /* Products */, + ); + sourceTree = ""; + }; + E7325DC52A2F839400130DAE /* Products */ = { + isa = PBXGroup; + children = ( + E7325DC42A2F839400130DAE /* ToDos.app */, + ); + name = Products; + sourceTree = ""; + }; + E7325DC62A2F839400130DAE /* ToDos */ = { + isa = PBXGroup; + children = ( + E7325DC72A2F839400130DAE /* ToDosApp.swift */, + E7325DC92A2F839400130DAE /* ContentView.swift */, + E7325DD92A2F8A9C00130DAE /* TodoView.swift */, + E7325DCB2A2F839500130DAE /* Assets.xcassets */, + E7325DD02A2F839500130DAE /* Item.swift */, + E7325DCD2A2F839500130DAE /* Preview Content */, + E7325DD72A2F83B600130DAE /* ModifyTodoView.swift */, + E79BE2CA2A3259F5002AFC0C /* UpdateToDoView.swift */, + E79BE2CC2A325A32002AFC0C /* CreateCategoryView.swift */, + E7C2BEE02A5332FA00197F9A /* CategoriesJSONDecoder.swift */, + E7F0333D2A4B449700B40C93 /* ItemsContainer.swift */, + E7C2BEDE2A532E7D00197F9A /* DefaultCategories.json */, + E70340692A9D04E300BD054F /* DefaultToDos.json */, + ); + path = ToDos; + sourceTree = ""; + }; + E7325DCD2A2F839500130DAE /* Preview Content */ = { + isa = PBXGroup; + children = ( + E7325DCE2A2F839500130DAE /* Preview Assets.xcassets */, + ); + path = "Preview Content"; + sourceTree = ""; + }; +/* End PBXGroup section */ + +/* Begin PBXNativeTarget section */ + E7325DC32A2F839400130DAE /* ToDos */ = { + isa = PBXNativeTarget; + buildConfigurationList = E7325DD42A2F839500130DAE /* Build configuration list for PBXNativeTarget "ToDos" */; + buildPhases = ( + E7325DC02A2F839400130DAE /* Sources */, + E7325DC12A2F839400130DAE /* Frameworks */, + E7325DC22A2F839400130DAE /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = ToDos; + packageProductDependencies = ( + E7E15E5C2A84057B0048DFAA /* SwiftUIImageViewer */, + ); + productName = ToDos; + productReference = E7325DC42A2F839400130DAE /* ToDos.app */; + productType = "com.apple.product-type.application"; + }; +/* End PBXNativeTarget section */ + +/* Begin PBXProject section */ + E7325DBC2A2F839400130DAE /* Project object */ = { + isa = PBXProject; + attributes = { + BuildIndependentTargetsInParallel = 1; + LastSwiftUpdateCheck = 1500; + LastUpgradeCheck = 1500; + TargetAttributes = { + E7325DC32A2F839400130DAE = { + CreatedOnToolsVersion = 15.0; + }; + }; + }; + buildConfigurationList = E7325DBF2A2F839400130DAE /* Build configuration list for PBXProject "ToDos" */; + compatibilityVersion = "Xcode 14.0"; + developmentRegion = en; + hasScannedForEncodings = 0; + knownRegions = ( + en, + Base, + ); + mainGroup = E7325DBB2A2F839400130DAE; + packageReferences = ( + E7E15E5B2A84057B0048DFAA /* XCRemoteSwiftPackageReference "swiftui-image-viewer" */, + ); + productRefGroup = E7325DC52A2F839400130DAE /* Products */; + projectDirPath = ""; + projectRoot = ""; + targets = ( + E7325DC32A2F839400130DAE /* ToDos */, + ); + }; +/* End PBXProject section */ + +/* Begin PBXResourcesBuildPhase section */ + E7325DC22A2F839400130DAE /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E7325DCF2A2F839500130DAE /* Preview Assets.xcassets in Resources */, + E7325DCC2A2F839500130DAE /* Assets.xcassets in Resources */, + E7C2BEDF2A532E7D00197F9A /* DefaultCategories.json in Resources */, + E703406A2A9D04E300BD054F /* DefaultToDos.json in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXResourcesBuildPhase section */ + +/* Begin PBXSourcesBuildPhase section */ + E7325DC02A2F839400130DAE /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + E7325DD82A2F83B600130DAE /* ModifyTodoView.swift in Sources */, + E7325DCA2A2F839400130DAE /* ContentView.swift in Sources */, + E79BE2CD2A325A32002AFC0C /* CreateCategoryView.swift in Sources */, + E7C2BEE12A5332FA00197F9A /* CategoriesJSONDecoder.swift in Sources */, + E7325DDA2A2F8A9C00130DAE /* TodoView.swift in Sources */, + E7F0333E2A4B449700B40C93 /* ItemsContainer.swift in Sources */, + E79BE2CB2A3259F5002AFC0C /* UpdateToDoView.swift in Sources */, + E7325DD12A2F839500130DAE /* Item.swift in Sources */, + E7325DC82A2F839400130DAE /* ToDosApp.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXSourcesBuildPhase section */ + +/* Begin XCBuildConfiguration section */ + E7325DD22A2F839500130DAE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = dwarf; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_TESTABILITY = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_DYNAMIC_NO_PIC = NO; + GCC_NO_COMMON_BLOCKS = YES; + GCC_OPTIMIZATION_LEVEL = 0; + GCC_PREPROCESSOR_DEFINITIONS = ( + "DEBUG=1", + "$(inherited)", + ); + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE; + MTL_FAST_MATH = YES; + ONLY_ACTIVE_ARCH = YES; + SDKROOT = iphoneos; + SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)"; + SWIFT_OPTIMIZATION_LEVEL = "-Onone"; + }; + name = Debug; + }; + E7325DD32A2F839500130DAE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ALWAYS_SEARCH_USER_PATHS = NO; + ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES; + CLANG_ANALYZER_NONNULL = YES; + CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE; + CLANG_CXX_LANGUAGE_STANDARD = "gnu++20"; + CLANG_ENABLE_MODULES = YES; + CLANG_ENABLE_OBJC_ARC = YES; + CLANG_ENABLE_OBJC_WEAK = YES; + CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES; + CLANG_WARN_BOOL_CONVERSION = YES; + CLANG_WARN_COMMA = YES; + CLANG_WARN_CONSTANT_CONVERSION = YES; + CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES; + CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR; + CLANG_WARN_DOCUMENTATION_COMMENTS = YES; + CLANG_WARN_EMPTY_BODY = YES; + CLANG_WARN_ENUM_CONVERSION = YES; + CLANG_WARN_INFINITE_RECURSION = YES; + CLANG_WARN_INT_CONVERSION = YES; + CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES; + CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES; + CLANG_WARN_OBJC_LITERAL_CONVERSION = YES; + CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR; + CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES; + CLANG_WARN_RANGE_LOOP_ANALYSIS = YES; + CLANG_WARN_STRICT_PROTOTYPES = YES; + CLANG_WARN_SUSPICIOUS_MOVE = YES; + CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE; + CLANG_WARN_UNREACHABLE_CODE = YES; + CLANG_WARN__DUPLICATE_METHOD_MATCH = YES; + COPY_PHASE_STRIP = NO; + DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym"; + ENABLE_NS_ASSERTIONS = NO; + ENABLE_STRICT_OBJC_MSGSEND = YES; + ENABLE_USER_SCRIPT_SANDBOXING = YES; + GCC_C_LANGUAGE_STANDARD = gnu17; + GCC_NO_COMMON_BLOCKS = YES; + GCC_WARN_64_TO_32_BIT_CONVERSION = YES; + GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR; + GCC_WARN_UNDECLARED_SELECTOR = YES; + GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE; + GCC_WARN_UNUSED_FUNCTION = YES; + GCC_WARN_UNUSED_VARIABLE = YES; + IPHONEOS_DEPLOYMENT_TARGET = 17.0; + LOCALIZATION_PREFERS_STRING_CATALOGS = YES; + MTL_ENABLE_DEBUG_INFO = NO; + MTL_FAST_MATH = YES; + SDKROOT = iphoneos; + SWIFT_COMPILATION_MODE = wholemodule; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; + E7325DD52A2F839500130DAE /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"ToDos/Preview Content\""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.tundsdev.ToDos; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + E7325DD62A2F839500130DAE /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_ASSET_PATHS = "\"ToDos/Preview Content\""; + ENABLE_PREVIEWS = YES; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; + INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; + INFOPLIST_KEY_UILaunchScreen_Generation = YES; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPad = "UIInterfaceOrientationPortrait UIInterfaceOrientationPortraitUpsideDown UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + INFOPLIST_KEY_UISupportedInterfaceOrientations_iPhone = "UIInterfaceOrientationPortrait UIInterfaceOrientationLandscapeLeft UIInterfaceOrientationLandscapeRight"; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.tundsdev.ToDos; + PRODUCT_NAME = "$(TARGET_NAME)"; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Release; + }; +/* End XCBuildConfiguration section */ + +/* Begin XCConfigurationList section */ + E7325DBF2A2F839400130DAE /* Build configuration list for PBXProject "ToDos" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E7325DD22A2F839500130DAE /* Debug */, + E7325DD32A2F839500130DAE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; + E7325DD42A2F839500130DAE /* Build configuration list for PBXNativeTarget "ToDos" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + E7325DD52A2F839500130DAE /* Debug */, + E7325DD62A2F839500130DAE /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; +/* End XCConfigurationList section */ + +/* Begin XCRemoteSwiftPackageReference section */ + E7E15E5B2A84057B0048DFAA /* XCRemoteSwiftPackageReference "swiftui-image-viewer" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/fuzzzlove/swiftui-image-viewer.git"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 1.0.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + E7E15E5C2A84057B0048DFAA /* SwiftUIImageViewer */ = { + isa = XCSwiftPackageProductDependency; + package = E7E15E5B2A84057B0048DFAA /* XCRemoteSwiftPackageReference "swiftui-image-viewer" */; + productName = SwiftUIImageViewer; + }; +/* End XCSwiftPackageProductDependency section */ + }; + rootObject = E7325DBC2A2F839400130DAE /* Project object */; +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/contents.xcworkspacedata new file mode 100644 index 0000000..919434a --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/contents.xcworkspacedata @@ -0,0 +1,7 @@ + + + + + diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist new file mode 100644 index 0000000..18d9810 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist @@ -0,0 +1,8 @@ + + + + + IDEDidComputeMac32BitWarning + + + diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..c5dd3c9 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,14 @@ +{ + "pins" : [ + { + "identity" : "swiftui-image-viewer", + "kind" : "remoteSourceControl", + "location" : "https://github.com/fuzzzlove/swiftui-image-viewer.git", + "state" : { + "revision" : "ca77123064ae046eac2be01654ee756085c2b182", + "version" : "1.0.0" + } + } + ], + "version" : 2 +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/xcuserdata/tunde.adegoroye.xcuserdatad/UserInterfaceState.xcuserstate b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/xcuserdata/tunde.adegoroye.xcuserdatad/UserInterfaceState.xcuserstate new file mode 100644 index 0000000..42420b7 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/project.xcworkspace/xcuserdata/tunde.adegoroye.xcuserdatad/UserInterfaceState.xcuserstate differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist new file mode 100644 index 0000000..2068192 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist @@ -0,0 +1,40 @@ + + + + + + + + + + + + + diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcschemes/xcschememanagement.plist b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcschemes/xcschememanagement.plist new file mode 100644 index 0000000..5f45eb7 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos.xcodeproj/xcuserdata/tunde.adegoroye.xcuserdatad/xcschemes/xcschememanagement.plist @@ -0,0 +1,14 @@ + + + + + SchemeUserState + + ToDos.xcscheme_^#shared#^_ + + orderHint + 0 + + + + diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._CategoriesJSONDecoder.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._CategoriesJSONDecoder.swift new file mode 100644 index 0000000..da3e95d Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._CategoriesJSONDecoder.swift differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ContentView.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ContentView.swift new file mode 100644 index 0000000..01903dd Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ContentView.swift differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._CreateCategoryView.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._CreateCategoryView.swift new file mode 100644 index 0000000..60490de Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._CreateCategoryView.swift differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._DefaultCategories.json b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._DefaultCategories.json new file mode 100644 index 0000000..0e01ddd Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._DefaultCategories.json differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._DefaultToDos.json b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._DefaultToDos.json new file mode 100644 index 0000000..e97da6d Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._DefaultToDos.json differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._Item.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._Item.swift new file mode 100644 index 0000000..c7d684c Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._Item.swift differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ItemsContainer.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ItemsContainer.swift new file mode 100644 index 0000000..f64ae45 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ItemsContainer.swift differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ModifyTodoView.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ModifyTodoView.swift new file mode 100644 index 0000000..39843e3 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ModifyTodoView.swift differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ToDosApp.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ToDosApp.swift new file mode 100644 index 0000000..b94a198 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._ToDosApp.swift differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._TodoView.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._TodoView.swift new file mode 100644 index 0000000..5a591a3 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._TodoView.swift differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._UpdateToDoView.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._UpdateToDoView.swift new file mode 100644 index 0000000..a64f931 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/._UpdateToDoView.swift differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/._Contents.json b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/._Contents.json new file mode 100644 index 0000000..c893829 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/._Contents.json differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/Contents.json b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/AppIcon.appiconset/Contents.json b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..13613e3 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,13 @@ +{ + "images" : [ + { + "idiom" : "universal", + "platform" : "ios", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/Contents.json b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/holiday.imageset/Contents.json b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/holiday.imageset/Contents.json new file mode 100644 index 0000000..3cfc538 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/holiday.imageset/Contents.json @@ -0,0 +1,21 @@ +{ + "images" : [ + { + "filename" : "holiday.jpeg", + "idiom" : "universal", + "scale" : "1x" + }, + { + "idiom" : "universal", + "scale" : "2x" + }, + { + "idiom" : "universal", + "scale" : "3x" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/holiday.imageset/holiday.jpeg b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/holiday.imageset/holiday.jpeg new file mode 100644 index 0000000..5007c69 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Assets.xcassets/holiday.imageset/holiday.jpeg differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/CategoriesJSONDecoder.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/CategoriesJSONDecoder.swift new file mode 100644 index 0000000..5e0adaf --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/CategoriesJSONDecoder.swift @@ -0,0 +1,29 @@ +// +// CategoriesJSONDecoder.swift +// ToDos +// +// Created by Tunde Adegoroye on 03/07/2023. +// + +import Foundation + +struct DefaultsJSON { + + static func decode(from fileName: String, type: T.Type) -> T? { + + do { + guard let url = Bundle.main.url(forResource: fileName, withExtension: "json"), + let data = try? Data(contentsOf: url) else { + return nil + } + + let result = try JSONDecoder().decode(T.self, from: data) + + return result + } catch { + print(error) + return nil + } + + } +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ContentView.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ContentView.swift new file mode 100644 index 0000000..dd31bca --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ContentView.swift @@ -0,0 +1,282 @@ +// +// ContentView.swift +// ToDos +// +// Created by Tunde Adegoroye on 06/06/2023. +// + +import SwiftUI +import SwiftData +import SwiftUIImageViewer + +enum SortOption: String, CaseIterable { + case title + case date + case category +} + +extension SortOption { + + var systemImage: String { + switch self { + case .title: + "textformat.size.larger" + case .date: + "calendar" + case .category: + "folder" + } + } +} + +struct ContentView: View { + + @Environment(\.modelContext) private var modelContext + @Query private var items: [Item] + + @State private var searchQuery = "" + @State private var showCreateCategory = false + @State private var showCreateToDo = false + @State private var toDoToEdit: Item? + + @State private var isImageViewerPresented = false + + @State private var selectedSortOption = SortOption.allCases.first! + + var filteredItems: [Item] { + + if searchQuery.isEmpty { + return items.sort(on: selectedSortOption) + } + + let filteredItems = items.compactMap { item in + + let titleContainsQuery = item.title.range(of: searchQuery, + options: .caseInsensitive) != nil + + let categoryTitleContainsQuery = item.category?.title.range(of: searchQuery, + options: .caseInsensitive) != nil + + return (titleContainsQuery || categoryTitleContainsQuery) ? item : nil + } + + return filteredItems.sort(on: selectedSortOption) + + } + + var body: some View { + NavigationStack { + List { + ForEach(filteredItems) { item in + VStack { + HStack { + VStack(alignment: .leading) { + + if item.isCritical { + Image(systemName: "exclamationmark.3") + .symbolVariant(.fill) + .foregroundColor(.red) + .font(.largeTitle) + .bold() + } + + Text(item.title) + .font(.largeTitle) + .bold() + + Text("\(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .shortened))") + .font(.callout) + + if let category = item.category { + Text(category.title) + .foregroundStyle(Color.blue) + .bold() + .padding(.horizontal) + .padding(.vertical, 8) + .background(Color.blue.opacity(0.1), + in: RoundedRectangle(cornerRadius: 8, + style: .continuous)) + } + + } + + + Spacer() + + Button { + withAnimation { + item.isCompleted.toggle() + } + } label: { + + Image(systemName: "checkmark") + .symbolVariant(.circle.fill) + .foregroundStyle(item.isCompleted ? .green : .gray) + .font(.largeTitle) + } + .buttonStyle(.plain) + } + if let selectedPhotoData = item.image, + let uiImage = UIImage(data: selectedPhotoData) { + Image(uiImage: uiImage) + .resizable() + .scaledToFill() + .frame(maxWidth: .infinity, maxHeight: 120) + .clipShape(RoundedRectangle(cornerRadius: 10, + style: .continuous)) + .onTapGesture { + isImageViewerPresented = true + } + .fullScreenCover(isPresented: $isImageViewerPresented) { + SwiftUIImageViewer(image: Image(uiImage: uiImage)) + .overlay(alignment: .topTrailing) { + Button { + isImageViewerPresented = false + } label: { + Image(systemName: "xmark") + .font(.headline) + } + .buttonStyle(.bordered) + .clipShape(Circle()) + .tint(.purple) + .padding() + } + } + } + + } + .swipeActions { + + Button(role: .destructive) { + + withAnimation { + modelContext.delete(item) + } + + } label: { + Label("Delete", systemImage: "trash.fill") + } + + Button { + toDoToEdit = item + } label: { + Label("Edit", systemImage: "pencil") + } + .tint(.orange) + + } + } + } + .navigationTitle("My To Do List") + .animation(/*@START_MENU_TOKEN@*/.easeIn/*@END_MENU_TOKEN@*/, value: filteredItems) + .searchable(text: $searchQuery, + prompt: "Search for a to do or a category") + .overlay { + if filteredItems.isEmpty { + ContentUnavailableView.search + } + } + .sheet(item: $toDoToEdit, + onDismiss: { + toDoToEdit = nil + }, + content: { editItem in + NavigationStack { + UpdateToDoView(item: editItem) + .interactiveDismissDisabled() + } + }) + .sheet(isPresented: $showCreateCategory, + content: { + NavigationStack { + CreateCategoryView() + } + }) + .sheet(isPresented: $showCreateToDo, + content: { + NavigationStack { + CreateTodoView() + } + }) + .toolbar { + + ToolbarItemGroup(placement: .primaryAction) { + Button { + showCreateCategory.toggle() + } label: { + Image(systemName: "plus") + } + } + + ToolbarItemGroup(placement: .topBarTrailing) { + + Menu { + Picker("", selection: $selectedSortOption) { + ForEach(SortOption.allCases, + id: \.rawValue) { option in + Label(option.rawValue.capitalized, + systemImage: option.systemImage) + .tag(option) + } + } + .labelsHidden() + + } label: { + Image(systemName: "ellipsis") + .symbolVariant(.circle) + } + + } + + } + .safeAreaInset(edge: .bottom, + alignment: .leading) { + Button(action: { + showCreateToDo.toggle() + }, label: { + Label("New ToDo", systemImage: "plus") + .bold() + .font(.title2) + .padding(8) + .background(.gray.opacity(0.1), + in: Capsule()) + .padding(.leading) + .symbolVariant(.circle.fill) + + }) + + } + } + } + + private func delete(item: Item) { + withAnimation { + modelContext.delete(item) + } + } +} + +private extension [Item] { + + func sort(on option: SortOption) -> [Item] { + switch option { + case .title: + self.sorted(by: { $0.title < $1.title }) + case .date: + self.sorted(by: { $0.timestamp < $1.timestamp }) + case .category: + self.sorted(by: { + guard let firstItemTitle = $0.category?.title, + let secondItemTitle = $1.category?.title else { return false } + return firstItemTitle < secondItemTitle + }) + } + } +} + +// Xcode 15 Beta 2 has a previews bug so this is why we're commenting this out... +// Ref: https://mastodon.social/@denisdepalatis/110561280521551715 +//#Preview { +// ContentView() +// .modelContainer(for: Item.self, inMemory: true) +//} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/CreateCategoryView.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/CreateCategoryView.swift new file mode 100644 index 0000000..97e89b2 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/CreateCategoryView.swift @@ -0,0 +1,117 @@ +// +// CreateCategoryView.swift +// ToDos +// +// Created by Tunde Adegoroye on 08/06/2023. +// + +import SwiftUI +import SwiftData + +@Model +class Category: Codable { + + @Attribute(.unique) + var title: String + + var items: [Item]? + + init(title: String = "") { + self.title = title + } + + enum CodingKeys: String, CodingKey { + case title + } + + required init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.title = try container.decode(String.self, forKey: .title) + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(title, forKey: .title) + } +} + +extension Category { + + static var defaults: [Category] { + [ + .init(title: "🙇🏾‍♂️ Study"), + .init(title: "🤝 Routine"), + .init(title: "🏠 Family") + ] + } +} + +struct CreateCategoryView: View { + + @Environment(\.dismiss) private var dismiss + @Environment(\.modelContext) var modelContext + + @State private var title: String = "" + @Query private var categories: [Category] + + var body: some View { + List { + Section("Category Title") { + TextField("Enter title here", + text: $title) + Button("Add Category") { + withAnimation { + let category = Category(title: title) + modelContext.insert(category) + category.items = [] + title = "" + } + } + .disabled(title.isEmpty) + } + + Section("Categories") { + + if categories.isEmpty { + + ContentUnavailableView("No Categories", + systemImage: "archivebox") + + } else { + + ForEach(categories) { category in + Text(category.title) + .swipeActions { + Button(role: .destructive) { + withAnimation { + modelContext.delete(category) + } + } label: { + Label("Delete", systemImage: "trash.fill") + } + } + } + } + + + } + + } + .navigationTitle("Add Category") + .toolbar { + + ToolbarItem(placement: .cancellationAction) { + Button("Dismiss") { + dismiss() + } + } + } + } +} + +// Xcode 15 Beta 2 has a previews bug so this is why we're commenting this out... +// Ref: https://mastodon.social/@denisdepalatis/110561280521551715//#Preview { +// NavigationStack { +// CreateCategoryView() +// } +//} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/DefaultCategories.json b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/DefaultCategories.json new file mode 100644 index 0000000..c4d0105 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/DefaultCategories.json @@ -0,0 +1,11 @@ +[ + { + "title": "🙇🏾‍♂️ Study" + }, + { + "title": "🤝 Routine" + }, + { + "title": "🏠 Family" + } +] diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/DefaultToDos.json b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/DefaultToDos.json new file mode 100644 index 0000000..f32800a --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/DefaultToDos.json @@ -0,0 +1,23 @@ +[ + { + "title": "Sangria Time 🍹", + "isCritical": true, + "isCompleted": false, + "category": null, + "imageName": "holiday" + }, + { + "title": "Go & get wedding suit", + "isCritical": false, + "isCompleted": true, + "category": null + }, + { + "title": "Go visit family", + "isCritical": true, + "isCompleted": false, + "category": { + "title": "🏠 Family" + } + } +] diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Item.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Item.swift new file mode 100644 index 0000000..fae1bca --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Item.swift @@ -0,0 +1,91 @@ +// +// Item.swift +// ToDos +// +// Created by Tunde Adegoroye on 06/06/2023. +// + +import Foundation +import SwiftData +import UIKit + +@Model +final class Item: Codable { + var title: String + var timestamp: Date + var isCritical: Bool + var isCompleted: Bool + + @Relationship(deleteRule: .nullify, inverse: \Category.items) + var category: Category? + + @Attribute(.externalStorage) + var image: Data? + + enum CodingKeys: String, CodingKey { + case title + case timestamp + case isCritical + case isCompleted + case category + case imageName + } + + init(title: String = "", + timestamp: Date = .now, + isCritical: Bool = false, + isCompleted: Bool = false) { + self.title = title + self.timestamp = timestamp + self.isCritical = isCritical + self.isCompleted = isCompleted + } + + init(from decoder: Decoder) throws { + let container = try decoder.container(keyedBy: CodingKeys.self) + self.title = try container.decode(String.self, forKey: .title) + self.timestamp = Date.randomDateNextWeek() ?? .now + self.isCritical = try container.decode(Bool.self, forKey: .isCritical) + self.isCompleted = try container.decode(Bool.self, forKey: .isCompleted) + self.category = try container.decodeIfPresent(Category.self, forKey: .category) + + if let imageName = try container.decodeIfPresent(String.self, forKey: .imageName), + let imageData = UIImage(named: imageName) { + self.image = imageData.jpegData(compressionQuality: 0.8) + } + } + + func encode(to encoder: Encoder) throws { + var container = encoder.container(keyedBy: CodingKeys.self) + try container.encode(title, forKey: .title) + try container.encode(timestamp, forKey: .timestamp) + try container.encode(isCritical, forKey: .isCritical) + try container.encode(isCompleted, forKey: .isCompleted) + try container.encode(category, forKey: .category) + } +} + +extension Date { + static func randomDateNextWeek() -> Date? { + let calendar = Calendar.current + let currentDate = Date() + + guard let nextWeekStartDate = calendar.date(byAdding: .day, value: 7, to: currentDate) else { + return nil + } + + let randomTimeInterval = TimeInterval.random(in: 0..<7 * 24 * 60 * 60) // Random time within a week + let randomDate = nextWeekStartDate.addingTimeInterval(randomTimeInterval) + + return randomDate + } +} + +extension Item { + + static var dummy: Item { + .init(title: "Item 1", + timestamp: .now, + isCritical: true) + } +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ItemsContainer.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ItemsContainer.swift new file mode 100644 index 0000000..2ecb0f8 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ItemsContainer.swift @@ -0,0 +1,39 @@ +// +// ItemsContainer.swift +// ToDos +// +// Created by Tunde Adegoroye on 27/06/2023. +// + +import Foundation +import SwiftData + +actor ItemsContainer { + + @MainActor + static func create(shouldCreateDefaults: inout Bool) -> ModelContainer { + let schema = Schema([Item.self]) + let configuration = ModelConfiguration() + let container = try! ModelContainer(for: schema, configurations: [configuration]) + if shouldCreateDefaults { + shouldCreateDefaults = false + + let categories = DefaultsJSON.decode(from: "DefaultCategories", + type: [Category].self) + categories?.forEach { container.mainContext.insert($0) } + + let items = DefaultsJSON.decode(from: "DefaultToDos", + type: [Item].self) + + items?.forEach { item in + + container.mainContext.insert(item) + item.category?.items?.append(item) + } + + } + + return container + } + +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ModifyTodoView.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ModifyTodoView.swift new file mode 100644 index 0000000..fb859e6 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ModifyTodoView.swift @@ -0,0 +1,143 @@ +// +// ModifyTodoView.swift +// ToDos +// +// Created by Tunde Adegoroye on 06/06/2023. +// + +import SwiftUI +import SwiftData +import PhotosUI + +struct CreateTodoView: View { + + @Environment(\.modelContext) var modelContext + @Environment(\.dismiss) var dismiss + + @Query private var categories: [Category] + + @State var item = Item() + @State var selectedCategory: Category? + @State var selectedPhoto: PhotosPickerItem? + + var body: some View { + List { + + Section("To do title") { + TextField("Name", text: $item.title) + } + + Section("General") { + DatePicker("Choose a date", + selection: $item.timestamp) + Toggle("Important?", isOn: $item.isCritical) + } + + Section("Select A Category") { + + + if categories.isEmpty { + + ContentUnavailableView("No Categories", + systemImage: "archivebox") + + } else { + Picker("", selection: $selectedCategory) { + + ForEach(categories) { category in + Text(category.title) + .tag(category as Category?) + } + + Text("None") + .tag(nil as Category?) + } + .labelsHidden() + .pickerStyle(.inline) + } + + + } + + Section { + + if let selectedPhotoData = item.image, + let uiImage = UIImage(data: selectedPhotoData) { + Image(uiImage: uiImage) + .resizable() + .scaledToFill() + .frame(maxWidth: .infinity, maxHeight: 300) + } + + PhotosPicker(selection: $selectedPhoto, + matching: .images, + photoLibrary: .shared()) { + Label("Add Image", systemImage: "photo") + } + + if item.image != nil { + + Button(role: .destructive) { + withAnimation { + selectedPhoto = nil + item.image = nil + } + } label: { + Label("Remove Image", systemImage: "xmark") + .foregroundStyle(.red) + } + } + + } + + + Section { + Button("Create") { + save() + dismiss() + } + } + + } + .navigationTitle("Create ToDo") + .toolbar { + + ToolbarItem(placement: .cancellationAction) { + Button("Dismiss") { + dismiss() + } + } + + ToolbarItem(placement: .primaryAction) { + Button("Done") { + save() + dismiss() + } + .disabled(item.title.isEmpty) + } + } + .task(id: selectedPhoto) { + if let data = try? await selectedPhoto?.loadTransferable(type: Data.self) { + item.image = data + } + } + } +} + +private extension CreateTodoView { + + func save() { + modelContext.insert(item) + item.category = selectedCategory + selectedCategory?.items?.append(item) + } +} +// +// Xcode 15 Beta 2 has a previews bug so this is why we're commenting this out... +// Ref: https://mastodon.social/@denisdepalatis/110561280521551715 +#Preview { + NavigationStack { + CreateTodoView() + .modelContainer(for: Item.self) + } +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Preview Content/Preview Assets.xcassets/._Contents.json b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Preview Content/Preview Assets.xcassets/._Contents.json new file mode 100644 index 0000000..c893829 Binary files /dev/null and b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Preview Content/Preview Assets.xcassets/._Contents.json differ diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Preview Content/Preview Assets.xcassets/Contents.json b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Preview Content/Preview Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/Preview Content/Preview Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ToDosApp.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ToDosApp.swift new file mode 100644 index 0000000..2e6023f --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/ToDosApp.swift @@ -0,0 +1,22 @@ +// +// ToDosApp.swift +// ToDos +// +// Created by Tunde Adegoroye on 06/06/2023. +// + +import SwiftUI +import SwiftData + +@main +struct ToDosApp: App { + + @AppStorage("isFirstTimeLaunch") private var isFirstTimeLaunch: Bool = true + + var body: some Scene { + WindowGroup { + ContentView() + } + .modelContainer(ItemsContainer.create(shouldCreateDefaults: &isFirstTimeLaunch)) + } +} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/TodoView.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/TodoView.swift new file mode 100644 index 0000000..2fc75dd --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/TodoView.swift @@ -0,0 +1,40 @@ +// +// TodoView.swift +// ToDos +// +// Created by Tunde Adegoroye on 06/06/2023. +// + +import SwiftUI + +struct TodoView: View { + + let item: Item + + var body: some View { + VStack(alignment: .leading) { + + if item.isCritical { + Image(systemName: "exclamationmark.3") + .symbolVariant(.fill) + .foregroundColor(.red) + .font(.largeTitle) + .bold() + } + + Text(item.title) + .font(.largeTitle) + .bold() + + Text("\(item.timestamp, format: Date.FormatStyle(date: .numeric, time: .standard))") + .font(.callout) + } + + } +} + +// Xcode 15 Beta 2 has a previews bug so this is why we're commenting this out... +// Ref: https://mastodon.social/@denisdepalatis/110561280521551715 +//#Preview { +// TodoView(item: Item.dummy) +//} diff --git a/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/UpdateToDoView.swift b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/UpdateToDoView.swift new file mode 100644 index 0000000..29227e1 --- /dev/null +++ b/How To Use SwiftData with Preview | SwiftData Tutorial/Source Code/Starter/ToDos/ToDos/UpdateToDoView.swift @@ -0,0 +1,133 @@ +// +// UpdateToDoView.swift +// ToDos +// +// Created by Tunde Adegoroye on 08/06/2023. +// + +import SwiftUI +import SwiftData +import PhotosUI + +class OriginalToDo { + var title: String + var timestamp: Date + var isCritical: Bool + + init(item: Item) { + self.title = item.title + self.timestamp = item.timestamp + self.isCritical = item.isCritical + } +} + +struct UpdateToDoView: View { + + @Environment(\.dismiss) var dismiss + + @Query private var categories: [Category] + + @State var selectedCategory: Category? + @State var selectedPhoto: PhotosPickerItem? + + @Bindable var item: Item + + var body: some View { + List { + + Section("To do title") { + TextField("Name", text: $item.title) + } + + Section("General") { + DatePicker("Choose a date", + selection: $item.timestamp) + Toggle("Important?", isOn: $item.isCritical) + } + + + + Section("Select A Category") { + + + if categories.isEmpty { + + ContentUnavailableView("No Categories", + systemImage: "archivebox") + + } else { + + Picker("", selection: $selectedCategory) { + + ForEach(categories) { category in + Text(category.title) + .tag(category as Category?) + } + + Text("None") + .tag(nil as Category?) + } + .labelsHidden() + .pickerStyle(.inline) + } + + + } + + Section { + + if let selectedPhotoData = item.image, + let uiImage = UIImage(data: selectedPhotoData) { + Image(uiImage: uiImage) + .resizable() + .scaledToFill() + .frame(maxWidth: .infinity, maxHeight: 300) + } + + PhotosPicker(selection: $selectedPhoto, + matching: .images, + photoLibrary: .shared()) { + Label("Add Image", systemImage: "photo") + } + + if item.image != nil { + + Button(role: .destructive) { + withAnimation { + selectedPhoto = nil + item.image = nil + } + } label: { + Label("Remove Image", systemImage: "xmark") + .foregroundStyle(.red) + } + } + + } + + Section { + Button("Update") { + item.category = selectedCategory + dismiss() + } + } + } + .navigationTitle("Update ToDo") + .onAppear(perform: { + selectedCategory = item.category + }) + .task(id: selectedPhoto) { + if let data = try? await selectedPhoto?.loadTransferable(type: Data.self) { + item.image = data + } + } + } +} + +// Xcode 15 Beta 2 has a previews bug so this is why we're commenting this out... +// Ref: https://mastodon.social/@denisdepalatis/110561280521551715 +//#Preview { +// UpdateToDoView(item: Item.dummy) +// .modelContainer(for: Item.self) +// +//}