diff --git a/myApp/DeepWorkTimer/CLAUDE.md b/myApp/DeepWorkTimer/CLAUDE.md deleted file mode 100644 index 8e1b8e7..0000000 --- a/myApp/DeepWorkTimer/CLAUDE.md +++ /dev/null @@ -1,9 +0,0 @@ -# Project: DeepWork Timer (iOS) -- Frameworks: SwiftUI, SwiftData, Swift Charts, ActivityKit -- Architecture: MVVM (Model-View-ViewModel) - -# Guidelines -- Only use native Apple frameworks. Do not use external APIs, Cocoapods, or third-party libraries. -- Never write the entire project at once. Follow the user's specific file/milestone targets. -- Explain iOS-specific concepts (e.g., @State, @Environment, MainActor) briefly when introducing them. -- Always implement safe SwiftData migrations and error handling. diff --git a/myApp/DeepWorkTimer/DeepWorkTimer.xcodeproj/project.pbxproj b/myApp/DeepWorkTimer/DeepWorkTimer.xcodeproj/project.pbxproj deleted file mode 100644 index 37f6bdc..0000000 --- a/myApp/DeepWorkTimer/DeepWorkTimer.xcodeproj/project.pbxproj +++ /dev/null @@ -1,526 +0,0 @@ -// !$*UTF8*$! -{ - archiveVersion = 1; - classes = { - }; - objectVersion = 77; - objects = { - -/* Begin PBXBuildFile section */ - 72106C082FDC71B400CDE600 /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72106C072FDC71B400CDE600 /* WidgetKit.framework */; }; - 72106C0A2FDC71B400CDE600 /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 72106C092FDC71B400CDE600 /* SwiftUI.framework */; }; - 72106C172FDC71B600CDE600 /* TimeWidgetExtension.appex in Embed Foundation Extensions */ = {isa = PBXBuildFile; fileRef = 72106C052FDC71B400CDE600 /* TimeWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; -/* End PBXBuildFile section */ - -/* Begin PBXContainerItemProxy section */ - 72106C152FDC71B600CDE600 /* PBXContainerItemProxy */ = { - isa = PBXContainerItemProxy; - containerPortal = 72106BE22FDC6B3000CDE600 /* Project object */; - proxyType = 1; - remoteGlobalIDString = 72106C042FDC71B400CDE600; - remoteInfo = TimeWidgetExtension; - }; -/* End PBXContainerItemProxy section */ - -/* Begin PBXCopyFilesBuildPhase section */ - 72106C1C2FDC71B600CDE600 /* Embed Foundation Extensions */ = { - isa = PBXCopyFilesBuildPhase; - buildActionMask = 2147483647; - dstPath = ""; - dstSubfolderSpec = 13; - files = ( - 72106C172FDC71B600CDE600 /* TimeWidgetExtension.appex in Embed Foundation Extensions */, - ); - name = "Embed Foundation Extensions"; - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXCopyFilesBuildPhase section */ - -/* Begin PBXFileReference section */ - 72106BEA2FDC6B3000CDE600 /* DeepWorkTimer.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = DeepWorkTimer.app; sourceTree = BUILT_PRODUCTS_DIR; }; - 72106C052FDC71B400CDE600 /* TimeWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = TimeWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; - 72106C072FDC71B400CDE600 /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; - 72106C092FDC71B400CDE600 /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; -/* End PBXFileReference section */ - -/* Begin PBXFileSystemSynchronizedBuildFileExceptionSet section */ - 72106C182FDC71B600CDE600 /* Exceptions for "TimeWidget" folder in "TimeWidgetExtension" target */ = { - isa = PBXFileSystemSynchronizedBuildFileExceptionSet; - membershipExceptions = ( - Info.plist, - ); - target = 72106C042FDC71B400CDE600 /* TimeWidgetExtension */; - }; -/* End PBXFileSystemSynchronizedBuildFileExceptionSet section */ - -/* Begin PBXFileSystemSynchronizedRootGroup section */ - 72106BEC2FDC6B3000CDE600 /* DeepWorkTimer */ = { - isa = PBXFileSystemSynchronizedRootGroup; - path = DeepWorkTimer; - sourceTree = ""; - }; - 72106C0B2FDC71B400CDE600 /* TimeWidget */ = { - isa = PBXFileSystemSynchronizedRootGroup; - exceptions = ( - 72106C182FDC71B600CDE600 /* Exceptions for "TimeWidget" folder in "TimeWidgetExtension" target */, - ); - path = TimeWidget; - sourceTree = ""; - }; -/* End PBXFileSystemSynchronizedRootGroup section */ - -/* Begin PBXFrameworksBuildPhase section */ - 72106BE72FDC6B3000CDE600 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 72106C022FDC71B400CDE600 /* Frameworks */ = { - isa = PBXFrameworksBuildPhase; - buildActionMask = 2147483647; - files = ( - 72106C0A2FDC71B400CDE600 /* SwiftUI.framework in Frameworks */, - 72106C082FDC71B400CDE600 /* WidgetKit.framework in Frameworks */, - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXFrameworksBuildPhase section */ - -/* Begin PBXGroup section */ - 72106BE12FDC6B3000CDE600 = { - isa = PBXGroup; - children = ( - 72106BEC2FDC6B3000CDE600 /* DeepWorkTimer */, - 72106C0B2FDC71B400CDE600 /* TimeWidget */, - 72106C062FDC71B400CDE600 /* Frameworks */, - 72106BEB2FDC6B3000CDE600 /* Products */, - ); - sourceTree = ""; - }; - 72106BEB2FDC6B3000CDE600 /* Products */ = { - isa = PBXGroup; - children = ( - 72106BEA2FDC6B3000CDE600 /* DeepWorkTimer.app */, - 72106C052FDC71B400CDE600 /* TimeWidgetExtension.appex */, - ); - name = Products; - sourceTree = ""; - }; - 72106C062FDC71B400CDE600 /* Frameworks */ = { - isa = PBXGroup; - children = ( - 72106C072FDC71B400CDE600 /* WidgetKit.framework */, - 72106C092FDC71B400CDE600 /* SwiftUI.framework */, - ); - name = Frameworks; - sourceTree = ""; - }; -/* End PBXGroup section */ - -/* Begin PBXNativeTarget section */ - 72106BE92FDC6B3000CDE600 /* DeepWorkTimer */ = { - isa = PBXNativeTarget; - buildConfigurationList = 72106BF52FDC6B3100CDE600 /* Build configuration list for PBXNativeTarget "DeepWorkTimer" */; - buildPhases = ( - 72106BE62FDC6B3000CDE600 /* Sources */, - 72106BE72FDC6B3000CDE600 /* Frameworks */, - 72106BE82FDC6B3000CDE600 /* Resources */, - 72106C1C2FDC71B600CDE600 /* Embed Foundation Extensions */, - ); - buildRules = ( - ); - dependencies = ( - 72106C162FDC71B600CDE600 /* PBXTargetDependency */, - ); - fileSystemSynchronizedGroups = ( - 72106BEC2FDC6B3000CDE600 /* DeepWorkTimer */, - ); - name = DeepWorkTimer; - packageProductDependencies = ( - ); - productName = DeepWorkTimer; - productReference = 72106BEA2FDC6B3000CDE600 /* DeepWorkTimer.app */; - productType = "com.apple.product-type.application"; - }; - 72106C042FDC71B400CDE600 /* TimeWidgetExtension */ = { - isa = PBXNativeTarget; - buildConfigurationList = 72106C192FDC71B600CDE600 /* Build configuration list for PBXNativeTarget "TimeWidgetExtension" */; - buildPhases = ( - 72106C012FDC71B400CDE600 /* Sources */, - 72106C022FDC71B400CDE600 /* Frameworks */, - 72106C032FDC71B400CDE600 /* Resources */, - ); - buildRules = ( - ); - dependencies = ( - ); - fileSystemSynchronizedGroups = ( - 72106C0B2FDC71B400CDE600 /* TimeWidget */, - ); - name = TimeWidgetExtension; - packageProductDependencies = ( - ); - productName = TimeWidgetExtension; - productReference = 72106C052FDC71B400CDE600 /* TimeWidgetExtension.appex */; - productType = "com.apple.product-type.app-extension"; - }; -/* End PBXNativeTarget section */ - -/* Begin PBXProject section */ - 72106BE22FDC6B3000CDE600 /* Project object */ = { - isa = PBXProject; - attributes = { - BuildIndependentTargetsInParallel = 1; - LastSwiftUpdateCheck = 2650; - LastUpgradeCheck = 2650; - TargetAttributes = { - 72106BE92FDC6B3000CDE600 = { - CreatedOnToolsVersion = 26.5; - }; - 72106C042FDC71B400CDE600 = { - CreatedOnToolsVersion = 26.5; - }; - }; - }; - buildConfigurationList = 72106BE52FDC6B3000CDE600 /* Build configuration list for PBXProject "DeepWorkTimer" */; - developmentRegion = en; - hasScannedForEncodings = 0; - knownRegions = ( - en, - Base, - ); - mainGroup = 72106BE12FDC6B3000CDE600; - minimizedProjectReferenceProxies = 1; - preferredProjectObjectVersion = 77; - productRefGroup = 72106BEB2FDC6B3000CDE600 /* Products */; - projectDirPath = ""; - projectRoot = ""; - targets = ( - 72106BE92FDC6B3000CDE600 /* DeepWorkTimer */, - 72106C042FDC71B400CDE600 /* TimeWidgetExtension */, - ); - }; -/* End PBXProject section */ - -/* Begin PBXResourcesBuildPhase section */ - 72106BE82FDC6B3000CDE600 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 72106C032FDC71B400CDE600 /* Resources */ = { - isa = PBXResourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXResourcesBuildPhase section */ - -/* Begin PBXSourcesBuildPhase section */ - 72106BE62FDC6B3000CDE600 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; - 72106C012FDC71B400CDE600 /* Sources */ = { - isa = PBXSourcesBuildPhase; - buildActionMask = 2147483647; - files = ( - ); - runOnlyForDeploymentPostprocessing = 0; - }; -/* End PBXSourcesBuildPhase section */ - -/* Begin PBXTargetDependency section */ - 72106C162FDC71B600CDE600 /* PBXTargetDependency */ = { - isa = PBXTargetDependency; - target = 72106C042FDC71B400CDE600 /* TimeWidgetExtension */; - targetProxy = 72106C152FDC71B600CDE600 /* PBXContainerItemProxy */; - }; -/* End PBXTargetDependency section */ - -/* Begin XCBuildConfiguration section */ - 72106BF32FDC6B3100CDE600 /* 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 = 26.5; - 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; - }; - 72106BF42FDC6B3100CDE600 /* 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 = 26.5; - 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; - }; - 72106BF62FDC6B3100CDE600 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = "DeepWorkTimer/DeepWorkTimer.entitlements"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSSupportsLiveActivities = 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.yechan.DeepWorkTimer; - PRODUCT_NAME = "$(TARGET_NAME)"; - STRING_CATALOG_GENERATE_SYMBOLS = YES; - SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 72106BF72FDC6B3100CDE600 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - CODE_SIGN_ENTITLEMENTS = "DeepWorkTimer/DeepWorkTimer.entitlements"; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - ENABLE_PREVIEWS = YES; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_KEY_NSSupportsLiveActivities = 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.yechan.DeepWorkTimer; - PRODUCT_NAME = "$(TARGET_NAME)"; - STRING_CATALOG_GENERATE_SYMBOLS = YES; - SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_DEFAULT_ACTOR_ISOLATION = MainActor; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; - 72106C1A2FDC71B600CDE600 /* Debug */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = TimeWidget/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = TimeWidget; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.yechan.DeepWorkTimer.TimeWidget; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - STRING_CATALOG_GENERATE_SYMBOLS = YES; - SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Debug; - }; - 72106C1B2FDC71B600CDE600 /* Release */ = { - isa = XCBuildConfiguration; - buildSettings = { - ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; - ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; - CODE_SIGN_STYLE = Automatic; - CURRENT_PROJECT_VERSION = 1; - GENERATE_INFOPLIST_FILE = YES; - INFOPLIST_FILE = TimeWidget/Info.plist; - INFOPLIST_KEY_CFBundleDisplayName = TimeWidget; - INFOPLIST_KEY_NSHumanReadableCopyright = ""; - LD_RUNPATH_SEARCH_PATHS = ( - "$(inherited)", - "@executable_path/Frameworks", - "@executable_path/../../Frameworks", - ); - MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.yechan.DeepWorkTimer.TimeWidget; - PRODUCT_NAME = "$(TARGET_NAME)"; - SKIP_INSTALL = YES; - STRING_CATALOG_GENERATE_SYMBOLS = YES; - SWIFT_APPROACHABLE_CONCURRENCY = YES; - SWIFT_EMIT_LOC_STRINGS = YES; - SWIFT_UPCOMING_FEATURE_MEMBER_IMPORT_VISIBILITY = YES; - SWIFT_VERSION = 5.0; - TARGETED_DEVICE_FAMILY = "1,2"; - }; - name = Release; - }; -/* End XCBuildConfiguration section */ - -/* Begin XCConfigurationList section */ - 72106BE52FDC6B3000CDE600 /* Build configuration list for PBXProject "DeepWorkTimer" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 72106BF32FDC6B3100CDE600 /* Debug */, - 72106BF42FDC6B3100CDE600 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 72106BF52FDC6B3100CDE600 /* Build configuration list for PBXNativeTarget "DeepWorkTimer" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 72106BF62FDC6B3100CDE600 /* Debug */, - 72106BF72FDC6B3100CDE600 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; - 72106C192FDC71B600CDE600 /* Build configuration list for PBXNativeTarget "TimeWidgetExtension" */ = { - isa = XCConfigurationList; - buildConfigurations = ( - 72106C1A2FDC71B600CDE600 /* Debug */, - 72106C1B2FDC71B600CDE600 /* Release */, - ); - defaultConfigurationIsVisible = 0; - defaultConfigurationName = Release; - }; -/* End XCConfigurationList section */ - }; - rootObject = 72106BE22FDC6B3000CDE600 /* Project object */; -} diff --git a/myApp/DeepWorkTimer/DeepWorkTimer.xcodeproj/project.xcworkspace/contents.xcworkspacedata b/myApp/DeepWorkTimer/DeepWorkTimer.xcodeproj/project.xcworkspace/contents.xcworkspacedata deleted file mode 100644 index 919434a..0000000 --- a/myApp/DeepWorkTimer/DeepWorkTimer.xcodeproj/project.xcworkspace/contents.xcworkspacedata +++ /dev/null @@ -1,7 +0,0 @@ - - - - - diff --git a/myApp/DeepWorkTimer/DeepWorkTimer.xcodeproj/project.xcworkspace/xcuserdata/ceuak.xcuserdatad/UserInterfaceState.xcuserstate b/myApp/DeepWorkTimer/DeepWorkTimer.xcodeproj/project.xcworkspace/xcuserdata/ceuak.xcuserdatad/UserInterfaceState.xcuserstate deleted file mode 100644 index 8553b76..0000000 Binary files a/myApp/DeepWorkTimer/DeepWorkTimer.xcodeproj/project.xcworkspace/xcuserdata/ceuak.xcuserdatad/UserInterfaceState.xcuserstate and /dev/null differ diff --git a/myApp/DeepWorkTimer/DeepWorkTimer.xcodeproj/xcuserdata/ceuak.xcuserdatad/xcschemes/xcschememanagement.plist b/myApp/DeepWorkTimer/DeepWorkTimer.xcodeproj/xcuserdata/ceuak.xcuserdatad/xcschemes/xcschememanagement.plist deleted file mode 100644 index 9055e13..0000000 --- a/myApp/DeepWorkTimer/DeepWorkTimer.xcodeproj/xcuserdata/ceuak.xcuserdatad/xcschemes/xcschememanagement.plist +++ /dev/null @@ -1,19 +0,0 @@ - - - - - SchemeUserState - - DeepWorkTimer.xcscheme_^#shared#^_ - - orderHint - 1 - - TimeWidgetExtension.xcscheme_^#shared#^_ - - orderHint - 2 - - - - diff --git a/myApp/DeepWorkTimer/DeepWorkTimer/Assets.xcassets/AccentColor.colorset/Contents.json b/myApp/DeepWorkTimer/DeepWorkTimer/Assets.xcassets/AccentColor.colorset/Contents.json deleted file mode 100644 index eb87897..0000000 --- a/myApp/DeepWorkTimer/DeepWorkTimer/Assets.xcassets/AccentColor.colorset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "colors" : [ - { - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/myApp/DeepWorkTimer/DeepWorkTimer/Assets.xcassets/AppIcon.appiconset/Contents.json b/myApp/DeepWorkTimer/DeepWorkTimer/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 2305880..0000000 --- a/myApp/DeepWorkTimer/DeepWorkTimer/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "tinted" - } - ], - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/myApp/DeepWorkTimer/DeepWorkTimer/Assets.xcassets/Contents.json b/myApp/DeepWorkTimer/DeepWorkTimer/Assets.xcassets/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/myApp/DeepWorkTimer/DeepWorkTimer/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/myApp/DeepWorkTimer/DeepWorkTimer/ContentView.swift b/myApp/DeepWorkTimer/DeepWorkTimer/ContentView.swift deleted file mode 100644 index 25ccb2e..0000000 --- a/myApp/DeepWorkTimer/DeepWorkTimer/ContentView.swift +++ /dev/null @@ -1,163 +0,0 @@ -// -// ContentView.swift -// DeepWorkTimer -// -// Created by 송예찬 on 6/13/26. -// - -import SwiftUI -import SwiftData - -struct ContentView: View { - // @State owns the ViewModel; @Observable tracks only the properties this view reads. - @State private var viewModel = TimerViewModel() - @Query(sort: \FocusSession.startTime, order: .reverse) private var sessions: [FocusSession] - @Environment(\.modelContext) private var modelContext - // scenePhase tells us when the app moves to/from the background. - @Environment(\.scenePhase) private var scenePhase - - var body: some View { - TabView { - timerTab - .tabItem { - Label("Timer", systemImage: "timer") - } - - DashboardView() - .tabItem { - Label("Dashboard", systemImage: "chart.bar.fill") - } - } - .onChange(of: scenePhase) { _, newPhase in - switch newPhase { - case .background: - viewModel.appWentBackground() - case .active: - viewModel.appReturnedForeground() - default: - break - } - } - } - - private var timerTab: some View { - NavigationStack { - ScrollView { - VStack(spacing: 40) { - timerRing - controlButtons - if !sessions.isEmpty { - sessionList - } - } - .padding(.vertical, 24) - } - .navigationTitle("Deep Work Timer") - } - } - - // MARK: - Timer Ring - - private var timerRing: some View { - ZStack { - Circle() - .stroke(Color(.systemGray5), lineWidth: 18) - Circle() - .trim(from: 0, to: viewModel.progress) - .stroke( - Color.accentColor, - style: StrokeStyle(lineWidth: 18, lineCap: .round) - ) - .rotationEffect(.degrees(-90)) - .animation(.linear(duration: 1), value: viewModel.progress) - VStack(spacing: 6) { - Text(formattedTime) - .font(.system(size: 52, weight: .thin, design: .monospaced)) - Text(statusLabel) - .font(.caption) - .foregroundStyle(.secondary) - .textCase(.uppercase) - .tracking(2) - } - } - .frame(width: 270, height: 270) - .padding() - } - - private var formattedTime: String { - String(format: "%02d:%02d", viewModel.timeRemaining / 60, viewModel.timeRemaining % 60) - } - - private var statusLabel: String { - if viewModel.isRunning { return "Focus" } - if viewModel.sessionStartTime != nil { return "Paused" } - return "Ready" - } - - // MARK: - Control Buttons - - private var controlButtons: some View { - HStack(spacing: 20) { - Button { - viewModel.isRunning ? viewModel.pause() : viewModel.start() - } label: { - Label( - viewModel.isRunning ? "Pause" : "Start", - systemImage: viewModel.isRunning ? "pause.fill" : "play.fill" - ) - .frame(maxWidth: .infinity) - } - .buttonStyle(.borderedProminent) - .controlSize(.large) - - Button(role: .destructive) { - viewModel.stop(context: modelContext) - } label: { - Label("Stop", systemImage: "stop.fill") - .frame(maxWidth: .infinity) - } - .buttonStyle(.bordered) - .controlSize(.large) - .disabled(viewModel.sessionStartTime == nil) - } - .padding(.horizontal) - } - - // MARK: - Session List - - private var sessionList: some View { - VStack(alignment: .leading, spacing: 0) { - Text("Completed Sessions") - .font(.headline) - .padding(.horizontal) - .padding(.bottom, 12) - - ForEach(sessions) { session in - HStack { - VStack(alignment: .leading, spacing: 2) { - Text(session.category) - .font(.subheadline) - .fontWeight(.medium) - Text(session.startTime.formatted(date: .abbreviated, time: .shortened)) - .font(.caption) - .foregroundStyle(.secondary) - } - Spacer() - Text("\(session.durationInMinutes) min") - .font(.subheadline) - .foregroundStyle(.secondary) - } - .padding() - .background(Color(.secondarySystemBackground)) - .clipShape(RoundedRectangle(cornerRadius: 12)) - .padding(.horizontal) - .padding(.vertical, 4) - } - } - } -} - -#Preview { - ContentView() - .modelContainer(for: FocusSession.self, inMemory: true) -} diff --git a/myApp/DeepWorkTimer/DeepWorkTimer/DeepWorkTimer.entitlements b/myApp/DeepWorkTimer/DeepWorkTimer/DeepWorkTimer.entitlements deleted file mode 100644 index 0ba9c39..0000000 --- a/myApp/DeepWorkTimer/DeepWorkTimer/DeepWorkTimer.entitlements +++ /dev/null @@ -1,8 +0,0 @@ - - - - - com.apple.developer.live-activity - - - diff --git a/myApp/DeepWorkTimer/DeepWorkTimer/DeepWorkTimerApp.swift b/myApp/DeepWorkTimer/DeepWorkTimer/DeepWorkTimerApp.swift deleted file mode 100644 index 266cdf6..0000000 --- a/myApp/DeepWorkTimer/DeepWorkTimer/DeepWorkTimerApp.swift +++ /dev/null @@ -1,21 +0,0 @@ -// -// DeepWorkTimerApp.swift -// DeepWorkTimer -// -// Created by 송예찬 on 6/13/26. -// - -import SwiftUI -import SwiftData - -@main -struct DeepWorkTimerApp: App { - // modelContainer registers FocusSession with SwiftData and injects - // the store into the SwiftUI environment for all child views. - var body: some Scene { - WindowGroup { - ContentView() - } - .modelContainer(for: FocusSession.self) - } -} diff --git a/myApp/DeepWorkTimer/DeepWorkTimer/Models/FocusSession.swift b/myApp/DeepWorkTimer/DeepWorkTimer/Models/FocusSession.swift deleted file mode 100644 index efc80c3..0000000 --- a/myApp/DeepWorkTimer/DeepWorkTimer/Models/FocusSession.swift +++ /dev/null @@ -1,25 +0,0 @@ -// -// FocusSession.swift -// DeepWorkTimer -// -// Created by 송예찬 on 6/13/26. -// - -import Foundation -import SwiftData - -// @Model turns this plain Swift class into a SwiftData-managed, persistable entity. -@Model -final class FocusSession { - var id: UUID - var startTime: Date - var durationInMinutes: Int - var category: String - - init(id: UUID = UUID(), startTime: Date = .now, durationInMinutes: Int, category: String) { - self.id = id - self.startTime = startTime - self.durationInMinutes = durationInMinutes - self.category = category - } -} diff --git a/myApp/DeepWorkTimer/DeepWorkTimer/Models/TimerAttributes.swift b/myApp/DeepWorkTimer/DeepWorkTimer/Models/TimerAttributes.swift deleted file mode 100644 index dad149f..0000000 --- a/myApp/DeepWorkTimer/DeepWorkTimer/Models/TimerAttributes.swift +++ /dev/null @@ -1,19 +0,0 @@ -// Shared ActivityAttributes definition. -// NOTE: This file must belong to BOTH the DeepWorkTimer and TimeWidgetExtension targets. -// Because this project uses Xcode 16 file system synchronized groups, a physical copy -// lives in DeepWorkTimer/Models/ (for the app) and in TimeWidget/ (for the extension). -// Keep both files identical. - -import ActivityKit -import Foundation - -struct TimerAttributes: ActivityAttributes { - // ContentState holds data the system can update while the activity is live. - struct ContentState: Codable, Hashable { - // The exact moment the timer expires — drives Text(timerInterval:) real-time countdown. - var endDate: Date - } - - // Fixed for the lifetime of this activity. - var sessionName: String -} diff --git a/myApp/DeepWorkTimer/DeepWorkTimer/ViewModels/TimerViewModel.swift b/myApp/DeepWorkTimer/DeepWorkTimer/ViewModels/TimerViewModel.swift deleted file mode 100644 index 15c8d28..0000000 --- a/myApp/DeepWorkTimer/DeepWorkTimer/ViewModels/TimerViewModel.swift +++ /dev/null @@ -1,133 +0,0 @@ -// -// TimerViewModel.swift -// DeepWorkTimer -// -// Created by 송예찬 on 6/13/26. -// - -import Foundation -import Observation -import SwiftData -import ActivityKit - -// @Observable replaces ObservableObject + @Published. -// SwiftUI views automatically track only the specific properties they read. -@Observable -final class TimerViewModel { - private(set) var timeRemaining: Int - private(set) var isRunning = false - private(set) var sessionStartTime: Date? - - let totalDuration: Int - - private var timerTask: Task? - // Stores the moment the app moved to the background so elapsed time - // can be calculated accurately when the app returns. - private var backgroundEntryDate: Date? - private var currentActivity: Activity? - - var progress: Double { - totalDuration > 0 ? Double(timeRemaining) / Double(totalDuration) : 1.0 - } - - init(duration: Int = 25 * 60) { - totalDuration = duration - timeRemaining = duration - } - - func start() { - guard !isRunning else { return } - if sessionStartTime == nil { - sessionStartTime = .now - } - isRunning = true - scheduleTick() - startLiveActivity() - } - - func pause() { - isRunning = false - cancelTick() - endLiveActivity() - } - - func stop(context: ModelContext) { - let start = sessionStartTime ?? .now - let elapsed = totalDuration - timeRemaining - let minutes = max(1, elapsed / 60) - context.insert(FocusSession(startTime: start, durationInMinutes: minutes, category: "Deep Work")) - endLiveActivity() - reset() - } - - // Call when scenePhase → .background - func appWentBackground() { - guard isRunning else { return } - backgroundEntryDate = .now - cancelTick() - // isRunning stays true so appReturnedForeground knows to resume. - } - - // Call when scenePhase → .active - func appReturnedForeground() { - guard isRunning, let entry = backgroundEntryDate else { return } - let elapsed = Int(Date.now.timeIntervalSince(entry)) - timeRemaining = max(0, timeRemaining - elapsed) - backgroundEntryDate = nil - if timeRemaining > 0 { - scheduleTick() - } else { - isRunning = false - endLiveActivity() - } - } - - private func startLiveActivity() { - guard ActivityAuthorizationInfo().areActivitiesEnabled else { return } - let endDate = Date.now.addingTimeInterval(TimeInterval(timeRemaining)) - let state = TimerAttributes.ContentState(endDate: endDate) - let content = ActivityContent(state: state, staleDate: endDate) - let attributes = TimerAttributes(sessionName: "Deep Work") - do { - currentActivity = try Activity.request(attributes: attributes, content: content) - } catch { - // Live Activities may fail if the device doesn't support them or the limit is reached. - } - } - - private func endLiveActivity() { - guard let activity = currentActivity else { return } - currentActivity = nil - Task { - let finalState = TimerAttributes.ContentState(endDate: .now) - let content = ActivityContent(state: finalState, staleDate: nil) - await activity.end(content, dismissalPolicy: .immediate) - } - } - - private func scheduleTick() { - timerTask = Task { - while !Task.isCancelled && timeRemaining > 0 { - try? await Task.sleep(for: .seconds(1)) - guard !Task.isCancelled else { break } - timeRemaining = max(0, timeRemaining - 1) - } - if timeRemaining == 0 { - isRunning = false - endLiveActivity() - } - } - } - - private func cancelTick() { - timerTask?.cancel() - timerTask = nil - } - - private func reset() { - cancelTick() - isRunning = false - sessionStartTime = nil - timeRemaining = totalDuration - } -} diff --git a/myApp/DeepWorkTimer/DeepWorkTimer/Views/DashboardView.swift b/myApp/DeepWorkTimer/DeepWorkTimer/Views/DashboardView.swift deleted file mode 100644 index 5261534..0000000 --- a/myApp/DeepWorkTimer/DeepWorkTimer/Views/DashboardView.swift +++ /dev/null @@ -1,169 +0,0 @@ -// -// DashboardView.swift -// DeepWorkTimer -// -// Created by 송예찬 on 6/13/26. -// - -import SwiftUI -import SwiftData -import Charts - -// Lightweight struct to hold one day's aggregated focus data for the chart. -struct DayFocusData: Identifiable { - var id: Date { date } - let date: Date - let minutes: Int -} - -struct DashboardView: View { - // @Query fetches every persisted FocusSession from SwiftData automatically. - @Query(sort: \FocusSession.startTime, order: .forward) private var sessions: [FocusSession] - - // MARK: - Computed stats - - private var last7Days: [DayFocusData] { - let calendar = Calendar.current - let today = calendar.startOfDay(for: .now) - - var totals: [Date: Int] = [:] - for session in sessions { - let day = calendar.startOfDay(for: session.startTime) - totals[day, default: 0] += session.durationInMinutes - } - - return (0..<7).reversed().compactMap { offset -> DayFocusData? in - guard let day = calendar.date(byAdding: .day, value: -offset, to: today) else { return nil } - return DayFocusData(date: day, minutes: totals[day, default: 0]) - } - } - - private var totalFocusMinutes: Int { - sessions.reduce(0) { $0 + $1.durationInMinutes } - } - - private var averageDailyMinutes: Int { - let activeDays = last7Days.filter { $0.minutes > 0 } - guard !activeDays.isEmpty else { return 0 } - return activeDays.reduce(0) { $0 + $1.minutes } / activeDays.count - } - - // MARK: - Body - - var body: some View { - NavigationStack { - ScrollView { - VStack(alignment: .leading, spacing: 20) { - statsCards - chartSection - } - .padding(20) - } - .navigationTitle("Dashboard") - .background(Color(.systemGroupedBackground)) - } - } - - // MARK: - Stats Cards - - private var statsCards: some View { - HStack(spacing: 12) { - statCard( - title: "Total Focus Time", - value: formatted(totalFocusMinutes), - systemImage: "clock.fill", - tint: .blue - ) - statCard( - title: "Avg Daily Focus", - value: formatted(averageDailyMinutes), - systemImage: "chart.line.uptrend.xyaxis", - tint: .purple - ) - } - } - - private func statCard(title: String, value: String, systemImage: String, tint: Color) -> some View { - VStack(alignment: .leading, spacing: 8) { - HStack { - Image(systemName: systemImage) - .foregroundStyle(tint) - Spacer() - } - Text(value) - .font(.system(size: 32, weight: .bold, design: .rounded)) - .foregroundStyle(.primary) - .minimumScaleFactor(0.7) - .lineLimit(1) - Text(title) - .font(.caption) - .foregroundStyle(.secondary) - } - .padding(16) - .frame(maxWidth: .infinity, alignment: .leading) - .background(Color(.secondarySystemBackground)) - .clipShape(RoundedRectangle(cornerRadius: 16)) - } - - // MARK: - Chart Section - - private var chartSection: some View { - VStack(alignment: .leading, spacing: 14) { - Text("Last 7 Days") - .font(.headline) - .padding(.horizontal, 4) - - Chart(last7Days) { day in - BarMark( - x: .value("Day", day.date, unit: .day), - y: .value("Minutes", day.minutes) - ) - .foregroundStyle( - LinearGradient( - colors: [Color.blue.opacity(0.5), Color.purple], - startPoint: .bottom, - endPoint: .top - ) - ) - .cornerRadius(8) - } - .chartXAxis { - AxisMarks(values: .stride(by: .day)) { _ in - AxisValueLabel(format: .dateTime.weekday(.abbreviated)) - .font(.caption2) - } - } - .chartYAxis { - AxisMarks(position: .leading) { value in - AxisGridLine(stroke: StrokeStyle(lineWidth: 0.5, dash: [4])) - .foregroundStyle(Color(.systemGray4)) - AxisValueLabel { - if let minutes = value.as(Int.self) { - Text("\(minutes)m") - .font(.caption2) - .foregroundStyle(.secondary) - } - } - } - } - .frame(height: 220) - .padding(16) - .background(Color(.secondarySystemBackground)) - .clipShape(RoundedRectangle(cornerRadius: 16)) - } - } - - // MARK: - Helpers - - private func formatted(_ totalMinutes: Int) -> String { - let hours = totalMinutes / 60 - let mins = totalMinutes % 60 - if hours > 0 { return "\(hours)h \(mins)m" } - return "\(mins)m" - } -} - -#Preview { - DashboardView() - .modelContainer(for: FocusSession.self, inMemory: true) -} diff --git a/myApp/DeepWorkTimer/TimeWidget/Assets.xcassets/AccentColor.colorset/Contents.json b/myApp/DeepWorkTimer/TimeWidget/Assets.xcassets/AccentColor.colorset/Contents.json deleted file mode 100644 index eb87897..0000000 --- a/myApp/DeepWorkTimer/TimeWidget/Assets.xcassets/AccentColor.colorset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "colors" : [ - { - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/myApp/DeepWorkTimer/TimeWidget/Assets.xcassets/AppIcon.appiconset/Contents.json b/myApp/DeepWorkTimer/TimeWidget/Assets.xcassets/AppIcon.appiconset/Contents.json deleted file mode 100644 index 2305880..0000000 --- a/myApp/DeepWorkTimer/TimeWidget/Assets.xcassets/AppIcon.appiconset/Contents.json +++ /dev/null @@ -1,35 +0,0 @@ -{ - "images" : [ - { - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "dark" - } - ], - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - }, - { - "appearances" : [ - { - "appearance" : "luminosity", - "value" : "tinted" - } - ], - "idiom" : "universal", - "platform" : "ios", - "size" : "1024x1024" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/myApp/DeepWorkTimer/TimeWidget/Assets.xcassets/Contents.json b/myApp/DeepWorkTimer/TimeWidget/Assets.xcassets/Contents.json deleted file mode 100644 index 73c0059..0000000 --- a/myApp/DeepWorkTimer/TimeWidget/Assets.xcassets/Contents.json +++ /dev/null @@ -1,6 +0,0 @@ -{ - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/myApp/DeepWorkTimer/TimeWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json b/myApp/DeepWorkTimer/TimeWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json deleted file mode 100644 index eb87897..0000000 --- a/myApp/DeepWorkTimer/TimeWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json +++ /dev/null @@ -1,11 +0,0 @@ -{ - "colors" : [ - { - "idiom" : "universal" - } - ], - "info" : { - "author" : "xcode", - "version" : 1 - } -} diff --git a/myApp/DeepWorkTimer/TimeWidget/Info.plist b/myApp/DeepWorkTimer/TimeWidget/Info.plist deleted file mode 100644 index 0f118fb..0000000 --- a/myApp/DeepWorkTimer/TimeWidget/Info.plist +++ /dev/null @@ -1,11 +0,0 @@ - - - - - NSExtension - - NSExtensionPointIdentifier - com.apple.widgetkit-extension - - - diff --git a/myApp/DeepWorkTimer/TimeWidget/TimeWidget.swift b/myApp/DeepWorkTimer/TimeWidget/TimeWidget.swift deleted file mode 100644 index 2b2ced8..0000000 --- a/myApp/DeepWorkTimer/TimeWidget/TimeWidget.swift +++ /dev/null @@ -1,84 +0,0 @@ -// -// TimeWidget.swift -// TimeWidget -// -// Created by 송예찬 on 6/13/26. -// - -import WidgetKit -import SwiftUI - -struct Provider: TimelineProvider { - func placeholder(in context: Context) -> SimpleEntry { - SimpleEntry(date: Date(), emoji: "😀") - } - - func getSnapshot(in context: Context, completion: @escaping (SimpleEntry) -> ()) { - let entry = SimpleEntry(date: Date(), emoji: "😀") - completion(entry) - } - - func getTimeline(in context: Context, completion: @escaping (Timeline) -> ()) { - var entries: [SimpleEntry] = [] - - // Generate a timeline consisting of five entries an hour apart, starting from the current date. - let currentDate = Date() - for hourOffset in 0 ..< 5 { - let entryDate = Calendar.current.date(byAdding: .hour, value: hourOffset, to: currentDate)! - let entry = SimpleEntry(date: entryDate, emoji: "😀") - entries.append(entry) - } - - let timeline = Timeline(entries: entries, policy: .atEnd) - completion(timeline) - } - -// func relevances() async -> WidgetRelevances { -// // Generate a list containing the contexts this widget is relevant in. -// } -} - -struct SimpleEntry: TimelineEntry { - let date: Date - let emoji: String -} - -struct TimeWidgetEntryView : View { - var entry: Provider.Entry - - var body: some View { - VStack { - Text("Time:") - Text(entry.date, style: .time) - - Text("Emoji:") - Text(entry.emoji) - } - } -} - -struct TimeWidget: Widget { - let kind: String = "TimeWidget" - - var body: some WidgetConfiguration { - StaticConfiguration(kind: kind, provider: Provider()) { entry in - if #available(iOS 17.0, *) { - TimeWidgetEntryView(entry: entry) - .containerBackground(.fill.tertiary, for: .widget) - } else { - TimeWidgetEntryView(entry: entry) - .padding() - .background() - } - } - .configurationDisplayName("My Widget") - .description("This is an example widget.") - } -} - -#Preview(as: .systemSmall) { - TimeWidget() -} timeline: { - SimpleEntry(date: .now, emoji: "😀") - SimpleEntry(date: .now, emoji: "🤩") -} diff --git a/myApp/DeepWorkTimer/TimeWidget/TimeWidgetBundle.swift b/myApp/DeepWorkTimer/TimeWidget/TimeWidgetBundle.swift deleted file mode 100644 index 3303ea7..0000000 --- a/myApp/DeepWorkTimer/TimeWidget/TimeWidgetBundle.swift +++ /dev/null @@ -1,17 +0,0 @@ -// -// TimeWidgetBundle.swift -// TimeWidget -// -// Created by 송예찬 on 6/13/26. -// - -import WidgetKit -import SwiftUI - -@main -struct TimeWidgetBundle: WidgetBundle { - var body: some Widget { - TimeWidget() - TimeWidgetLiveActivity() - } -} diff --git a/myApp/DeepWorkTimer/TimeWidget/TimeWidgetLiveActivity.swift b/myApp/DeepWorkTimer/TimeWidget/TimeWidgetLiveActivity.swift deleted file mode 100644 index 12e0f61..0000000 --- a/myApp/DeepWorkTimer/TimeWidget/TimeWidgetLiveActivity.swift +++ /dev/null @@ -1,104 +0,0 @@ -// -// TimeWidgetLiveActivity.swift -// TimeWidget -// -// Created by 송예찬 on 6/13/26. -// - -import ActivityKit -import WidgetKit -import SwiftUI - -struct TimeWidgetLiveActivity: Widget { - var body: some WidgetConfiguration { - ActivityConfiguration(for: TimerAttributes.self) { context in - // Lock Screen / Notification Banner - // Text(timerInterval:) is a system primitive that counts down in real time - // without waking the app — no Timer or polling needed. - HStack(spacing: 14) { - Image(systemName: "timer") - .font(.title2.weight(.semibold)) - .foregroundStyle(.white) - - VStack(alignment: .leading, spacing: 2) { - Text(context.attributes.sessionName) - .font(.caption) - .foregroundStyle(.white.opacity(0.75)) - - let safeEnd = max(Date.now, context.state.endDate) - Text(timerInterval: Date.now...safeEnd, countsDown: true) - .font(.title.monospacedDigit().bold()) - .foregroundStyle(.white) - } - - Spacer() - } - .padding(.horizontal, 20) - .padding(.vertical, 14) - .activityBackgroundTint(Color(red: 0.07, green: 0.07, blue: 0.18)) - .activitySystemActionForegroundColor(.white) - - } dynamicIsland: { context in - DynamicIsland { - // Expanded — shown when the user long-presses the Dynamic Island. - DynamicIslandExpandedRegion(.leading) { - Label(context.attributes.sessionName, systemImage: "timer") - .font(.caption) - .foregroundStyle(.secondary) - .lineLimit(1) - } - DynamicIslandExpandedRegion(.trailing) { - EmptyView() - } - DynamicIslandExpandedRegion(.center) { - let safeEnd = max(Date.now, context.state.endDate) - Text(timerInterval: Date.now...safeEnd, countsDown: true) - .font(.title2.monospacedDigit().bold()) - .foregroundStyle(.primary) - } - DynamicIslandExpandedRegion(.bottom) { - Text("Stay focused") - .font(.caption2) - .foregroundStyle(.tertiary) - } - } compactLeading: { - Image(systemName: "timer") - .foregroundStyle(.indigo) - } compactTrailing: { - let safeEnd = max(Date.now, context.state.endDate) - Text(timerInterval: Date.now...safeEnd, countsDown: true) - .font(.caption.monospacedDigit().bold()) - .foregroundStyle(.primary) - .frame(width: 52) - } minimal: { - Image(systemName: "timer") - .foregroundStyle(.indigo) - } - .keylineTint(.indigo) - } - } -} - -// MARK: - Previews - -extension TimerAttributes { - fileprivate static var preview: TimerAttributes { - TimerAttributes(sessionName: "Deep Work") - } -} - -extension TimerAttributes.ContentState { - fileprivate static var active: TimerAttributes.ContentState { - TimerAttributes.ContentState(endDate: Date.now.addingTimeInterval(22 * 60)) - } - fileprivate static var nearEnd: TimerAttributes.ContentState { - TimerAttributes.ContentState(endDate: Date.now.addingTimeInterval(2 * 60)) - } -} - -#Preview("Notification", as: .content, using: TimerAttributes.preview) { - TimeWidgetLiveActivity() -} contentStates: { - TimerAttributes.ContentState.active - TimerAttributes.ContentState.nearEnd -} diff --git a/myApp/DeepWorkTimer/TimeWidget/TimerAttributes.swift b/myApp/DeepWorkTimer/TimeWidget/TimerAttributes.swift deleted file mode 100644 index dad149f..0000000 --- a/myApp/DeepWorkTimer/TimeWidget/TimerAttributes.swift +++ /dev/null @@ -1,19 +0,0 @@ -// Shared ActivityAttributes definition. -// NOTE: This file must belong to BOTH the DeepWorkTimer and TimeWidgetExtension targets. -// Because this project uses Xcode 16 file system synchronized groups, a physical copy -// lives in DeepWorkTimer/Models/ (for the app) and in TimeWidget/ (for the extension). -// Keep both files identical. - -import ActivityKit -import Foundation - -struct TimerAttributes: ActivityAttributes { - // ContentState holds data the system can update while the activity is live. - struct ContentState: Codable, Hashable { - // The exact moment the timer expires — drives Text(timerInterval:) real-time countdown. - var endDate: Date - } - - // Fixed for the lifetime of this activity. - var sessionName: String -}