From 58697bf965f4d2887f958b5079147e73d46f94ff Mon Sep 17 00:00:00 2001 From: Trey t Date: Mon, 10 Jan 2022 09:04:38 -0600 Subject: [PATCH] init commit - bring project over from Mood --- Feels (iOS).entitlements | 16 + Feels (iOS)Dev.entitlements | 16 + Feels--iOS--Info.plist | 11 + Feels.xcodeproj/project.pbxproj | 322 +++++++++++++++++- .../xcshareddata/swiftpm/Package.resolved | 16 + .../xcschemes/xcschememanagement.plist | 47 +++ .../AccentColor.colorset/Contents.json | 11 + .../AppIcon.appiconset/Contents.json | 98 ++++++ FeelsWidget/Assets.xcassets/Contents.json | 6 + .../WidgetBackground.colorset/Contents.json | 11 + FeelsWidget/FeelsWidget.intentdefinition | 59 ++++ FeelsWidget/FeelsWidget.swift | 259 ++++++++++++++ FeelsWidget/Info.plist | 11 + FeelsWidgetExtension.entitlements | 16 + FeelsWidgetExtensionDev.entitlements | 16 + Shared/ContentView.swift | 90 ----- .../Shared.xcdatamodel/contents | 8 +- Shared/FeelsApp.swift | 52 ++- Shared/LocalNotification.swift | 143 ++++++++ Shared/Models/Mood.swift | 63 ++++ Shared/Models/MoodEntryExtension.swift | 19 ++ Shared/Mooood.xcdatamodeld/.xccurrentversion | 8 + .../Shared.xcdatamodel/contents | 11 + Shared/Persistence.swift | 161 +++++++-- Shared/Random.swift | 20 ++ Shared/views/AddMoodHeaderView.swift | 79 +++++ Shared/views/ContentView.swift | 152 +++++++++ Shared/views/GraphView.swift | 16 + Shared/views/HeaderStatsView.swift | 82 +++++ Shared/views/SettingsView.swift | 142 ++++++++ 30 files changed, 1840 insertions(+), 121 deletions(-) create mode 100644 Feels (iOS).entitlements create mode 100644 Feels (iOS)Dev.entitlements create mode 100644 Feels--iOS--Info.plist create mode 100644 Feels.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved create mode 100644 FeelsWidget/Assets.xcassets/AccentColor.colorset/Contents.json create mode 100644 FeelsWidget/Assets.xcassets/AppIcon.appiconset/Contents.json create mode 100644 FeelsWidget/Assets.xcassets/Contents.json create mode 100644 FeelsWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json create mode 100644 FeelsWidget/FeelsWidget.intentdefinition create mode 100644 FeelsWidget/FeelsWidget.swift create mode 100644 FeelsWidget/Info.plist create mode 100644 FeelsWidgetExtension.entitlements create mode 100644 FeelsWidgetExtensionDev.entitlements delete mode 100644 Shared/ContentView.swift create mode 100644 Shared/LocalNotification.swift create mode 100644 Shared/Models/Mood.swift create mode 100644 Shared/Models/MoodEntryExtension.swift create mode 100644 Shared/Mooood.xcdatamodeld/.xccurrentversion create mode 100644 Shared/Mooood.xcdatamodeld/Shared.xcdatamodel/contents create mode 100644 Shared/Random.swift create mode 100644 Shared/views/AddMoodHeaderView.swift create mode 100644 Shared/views/ContentView.swift create mode 100644 Shared/views/GraphView.swift create mode 100644 Shared/views/HeaderStatsView.swift create mode 100644 Shared/views/SettingsView.swift diff --git a/Feels (iOS).entitlements b/Feels (iOS).entitlements new file mode 100644 index 0000000..2ead4a3 --- /dev/null +++ b/Feels (iOS).entitlements @@ -0,0 +1,16 @@ + + + + + aps-environment + development + com.apple.developer.icloud-container-identifiers + + iCloud.com.88oak.feels + + com.apple.developer.icloud-services + + CloudKit + + + diff --git a/Feels (iOS)Dev.entitlements b/Feels (iOS)Dev.entitlements new file mode 100644 index 0000000..1662af0 --- /dev/null +++ b/Feels (iOS)Dev.entitlements @@ -0,0 +1,16 @@ + + + + + aps-environment + development + com.apple.developer.icloud-container-identifiers + + iCloud.com.88oak.feelsDev + + com.apple.developer.icloud-services + + CloudKit + + + diff --git a/Feels--iOS--Info.plist b/Feels--iOS--Info.plist new file mode 100644 index 0000000..d1d3f19 --- /dev/null +++ b/Feels--iOS--Info.plist @@ -0,0 +1,11 @@ + + + + + UIBackgroundModes + + processing + remote-notification + + + diff --git a/Feels.xcodeproj/project.pbxproj b/Feels.xcodeproj/project.pbxproj index ef5ff24..d7e09e7 100644 --- a/Feels.xcodeproj/project.pbxproj +++ b/Feels.xcodeproj/project.pbxproj @@ -15,12 +15,44 @@ 1CD90B17278C7DE0001C4FEA /* Feels.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90AEB278C7DDF001C4FEA /* Feels.xcdatamodeld */; }; 1CD90B18278C7DE0001C4FEA /* FeelsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90AED278C7DDF001C4FEA /* FeelsApp.swift */; }; 1CD90B19278C7DE0001C4FEA /* FeelsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90AED278C7DDF001C4FEA /* FeelsApp.swift */; }; - 1CD90B1A278C7DE0001C4FEA /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90AEE278C7DDF001C4FEA /* ContentView.swift */; }; - 1CD90B1B278C7DE0001C4FEA /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90AEE278C7DDF001C4FEA /* ContentView.swift */; }; 1CD90B1C278C7DE0001C4FEA /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90AEF278C7DDF001C4FEA /* Persistence.swift */; }; 1CD90B1D278C7DE0001C4FEA /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90AEF278C7DDF001C4FEA /* Persistence.swift */; }; 1CD90B1E278C7DE0001C4FEA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1CD90AF0278C7DE0001C4FEA /* Assets.xcassets */; }; 1CD90B1F278C7DE0001C4FEA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1CD90AF0278C7DE0001C4FEA /* Assets.xcassets */; }; + 1CD90B37278C7E38001C4FEA /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B32278C7E38001C4FEA /* SettingsView.swift */; }; + 1CD90B38278C7E38001C4FEA /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B32278C7E38001C4FEA /* SettingsView.swift */; }; + 1CD90B39278C7E38001C4FEA /* GraphView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B33278C7E38001C4FEA /* GraphView.swift */; }; + 1CD90B3A278C7E38001C4FEA /* GraphView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B33278C7E38001C4FEA /* GraphView.swift */; }; + 1CD90B3B278C7E38001C4FEA /* AddMoodHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B34278C7E38001C4FEA /* AddMoodHeaderView.swift */; }; + 1CD90B3C278C7E38001C4FEA /* AddMoodHeaderView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B34278C7E38001C4FEA /* AddMoodHeaderView.swift */; }; + 1CD90B3D278C7E38001C4FEA /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B35278C7E38001C4FEA /* ContentView.swift */; }; + 1CD90B3E278C7E38001C4FEA /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B35278C7E38001C4FEA /* ContentView.swift */; }; + 1CD90B3F278C7E38001C4FEA /* HeaderStatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B36278C7E38001C4FEA /* HeaderStatsView.swift */; }; + 1CD90B40278C7E38001C4FEA /* HeaderStatsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B36278C7E38001C4FEA /* HeaderStatsView.swift */; }; + 1CD90B48278C7E7A001C4FEA /* WidgetKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CD90B47278C7E7A001C4FEA /* WidgetKit.framework */; platformFilter = maccatalyst; }; + 1CD90B4A278C7E7A001C4FEA /* SwiftUI.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CD90B49278C7E7A001C4FEA /* SwiftUI.framework */; platformFilter = maccatalyst; }; + 1CD90B4D278C7E7A001C4FEA /* FeelsWidget.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B4C278C7E7A001C4FEA /* FeelsWidget.swift */; }; + 1CD90B50278C7E7A001C4FEA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1CD90B4F278C7E7A001C4FEA /* Assets.xcassets */; }; + 1CD90B52278C7E7A001C4FEA /* FeelsWidget.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B4E278C7E7A001C4FEA /* FeelsWidget.intentdefinition */; }; + 1CD90B53278C7E7A001C4FEA /* FeelsWidget.intentdefinition in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B4E278C7E7A001C4FEA /* FeelsWidget.intentdefinition */; }; + 1CD90B56278C7E7A001C4FEA /* FeelsWidgetExtension.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = 1CD90B45278C7E7A001C4FEA /* FeelsWidgetExtension.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; }; + 1CD90B5B278C7E91001C4FEA /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90AEF278C7DDF001C4FEA /* Persistence.swift */; }; + 1CD90B5D278C7EAD001C4FEA /* Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B5C278C7EAD001C4FEA /* Random.swift */; }; + 1CD90B5E278C7EAD001C4FEA /* Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B5C278C7EAD001C4FEA /* Random.swift */; }; + 1CD90B5F278C7EAD001C4FEA /* Random.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B5C278C7EAD001C4FEA /* Random.swift */; }; + 1CD90B63278C7EBA001C4FEA /* Mood.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B61278C7EBA001C4FEA /* Mood.swift */; }; + 1CD90B64278C7EBA001C4FEA /* Mood.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B61278C7EBA001C4FEA /* Mood.swift */; }; + 1CD90B65278C7EBA001C4FEA /* Mood.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B61278C7EBA001C4FEA /* Mood.swift */; }; + 1CD90B66278C7EBA001C4FEA /* MoodEntryExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B62278C7EBA001C4FEA /* MoodEntryExtension.swift */; }; + 1CD90B67278C7EBA001C4FEA /* MoodEntryExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B62278C7EBA001C4FEA /* MoodEntryExtension.swift */; }; + 1CD90B68278C7EBA001C4FEA /* MoodEntryExtension.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B62278C7EBA001C4FEA /* MoodEntryExtension.swift */; }; + 1CD90B6C278C7F78001C4FEA /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CD90B6B278C7F78001C4FEA /* CloudKit.framework */; }; + 1CD90B6E278C7F8B001C4FEA /* CloudKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 1CD90B6B278C7F78001C4FEA /* CloudKit.framework */; }; + 1CD90B71278C80CA001C4FEA /* Feels.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90AEB278C7DDF001C4FEA /* Feels.xcdatamodeld */; }; + 1CD90B76278C8119001C4FEA /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B75278C8119001C4FEA /* LocalNotification.swift */; }; + 1CD90B77278C8119001C4FEA /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B75278C8119001C4FEA /* LocalNotification.swift */; }; + 1CD90B78278C8119001C4FEA /* LocalNotification.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90B75278C8119001C4FEA /* LocalNotification.swift */; }; + 1CD90B7B278C8146001C4FEA /* Charts in Frameworks */ = {isa = PBXBuildFile; productRef = 1CD90B7A278C8146001C4FEA /* Charts */; }; /* End PBXBuildFile section */ /* Begin PBXContainerItemProxy section */ @@ -38,12 +70,32 @@ remoteGlobalIDString = 1CD90AFA278C7DE0001C4FEA; remoteInfo = "Feels (macOS)"; }; + 1CD90B54278C7E7A001C4FEA /* PBXContainerItemProxy */ = { + isa = PBXContainerItemProxy; + containerPortal = 1CD90AE6278C7DDF001C4FEA /* Project object */; + proxyType = 1; + remoteGlobalIDString = 1CD90B44278C7E7A001C4FEA; + remoteInfo = FeelsWidgetExtension; + }; /* End PBXContainerItemProxy section */ +/* Begin PBXCopyFilesBuildPhase section */ + 1CD90B5A278C7E7A001C4FEA /* Embed App Extensions */ = { + isa = PBXCopyFilesBuildPhase; + buildActionMask = 2147483647; + dstPath = ""; + dstSubfolderSpec = 13; + files = ( + 1CD90B56278C7E7A001C4FEA /* FeelsWidgetExtension.appex in Embed App Extensions */, + ); + name = "Embed App Extensions"; + runOnlyForDeploymentPostprocessing = 0; + }; +/* End PBXCopyFilesBuildPhase section */ + /* Begin PBXFileReference section */ 1CD90AEC278C7DDF001C4FEA /* Shared.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Shared.xcdatamodel; sourceTree = ""; }; 1CD90AED278C7DDF001C4FEA /* FeelsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeelsApp.swift; sourceTree = ""; }; - 1CD90AEE278C7DDF001C4FEA /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; 1CD90AEF278C7DDF001C4FEA /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = ""; }; 1CD90AF0278C7DE0001C4FEA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; 1CD90AF5278C7DE0001C4FEA /* Feels.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Feels.app; sourceTree = BUILT_PRODUCTS_DIR; }; @@ -55,6 +107,28 @@ 1CD90B0E278C7DE0001C4FEA /* Tests macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Tests macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 1CD90B12278C7DE0001C4FEA /* Tests_macOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_macOS.swift; sourceTree = ""; }; 1CD90B14278C7DE0001C4FEA /* Tests_macOSLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_macOSLaunchTests.swift; sourceTree = ""; }; + 1CD90B32278C7E38001C4FEA /* SettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = ""; }; + 1CD90B33278C7E38001C4FEA /* GraphView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphView.swift; sourceTree = ""; }; + 1CD90B34278C7E38001C4FEA /* AddMoodHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddMoodHeaderView.swift; sourceTree = ""; }; + 1CD90B35278C7E38001C4FEA /* ContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; }; + 1CD90B36278C7E38001C4FEA /* HeaderStatsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderStatsView.swift; sourceTree = ""; }; + 1CD90B45278C7E7A001C4FEA /* FeelsWidgetExtension.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = FeelsWidgetExtension.appex; sourceTree = BUILT_PRODUCTS_DIR; }; + 1CD90B47278C7E7A001C4FEA /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; }; + 1CD90B49278C7E7A001C4FEA /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; }; + 1CD90B4C278C7E7A001C4FEA /* FeelsWidget.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeelsWidget.swift; sourceTree = ""; }; + 1CD90B4E278C7E7A001C4FEA /* FeelsWidget.intentdefinition */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; path = FeelsWidget.intentdefinition; sourceTree = ""; }; + 1CD90B4F278C7E7A001C4FEA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; }; + 1CD90B51278C7E7A001C4FEA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 1CD90B5C278C7EAD001C4FEA /* Random.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Random.swift; sourceTree = ""; }; + 1CD90B61278C7EBA001C4FEA /* Mood.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Mood.swift; sourceTree = ""; }; + 1CD90B62278C7EBA001C4FEA /* MoodEntryExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoodEntryExtension.swift; sourceTree = ""; }; + 1CD90B69278C7F65001C4FEA /* Feels--iOS--Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "Feels--iOS--Info.plist"; sourceTree = ""; }; + 1CD90B6A278C7F75001C4FEA /* Feels (iOS).entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Feels (iOS).entitlements"; sourceTree = ""; }; + 1CD90B6B278C7F78001C4FEA /* CloudKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = CloudKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.2.sdk/System/Library/Frameworks/CloudKit.framework; sourceTree = DEVELOPER_DIR; }; + 1CD90B6D278C7F89001C4FEA /* FeelsWidgetExtension.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = FeelsWidgetExtension.entitlements; sourceTree = ""; }; + 1CD90B6F278C8000001C4FEA /* FeelsWidgetExtensionDev.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = FeelsWidgetExtensionDev.entitlements; sourceTree = ""; }; + 1CD90B70278C8000001C4FEA /* Feels (iOS)Dev.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = "Feels (iOS)Dev.entitlements"; sourceTree = ""; }; + 1CD90B75278C8119001C4FEA /* LocalNotification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalNotification.swift; sourceTree = ""; }; /* End PBXFileReference section */ /* Begin PBXFrameworksBuildPhase section */ @@ -62,6 +136,8 @@ isa = PBXFrameworksBuildPhase; buildActionMask = 2147483647; files = ( + 1CD90B6C278C7F78001C4FEA /* CloudKit.framework in Frameworks */, + 1CD90B7B278C8146001C4FEA /* Charts in Frameworks */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -86,16 +162,33 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 1CD90B42278C7E7A001C4FEA /* Frameworks */ = { + isa = PBXFrameworksBuildPhase; + buildActionMask = 2147483647; + files = ( + 1CD90B6E278C7F8B001C4FEA /* CloudKit.framework in Frameworks */, + 1CD90B4A278C7E7A001C4FEA /* SwiftUI.framework in Frameworks */, + 1CD90B48278C7E7A001C4FEA /* WidgetKit.framework in Frameworks */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXFrameworksBuildPhase section */ /* Begin PBXGroup section */ 1CD90AE5278C7DDF001C4FEA = { isa = PBXGroup; children = ( + 1CD90B6A278C7F75001C4FEA /* Feels (iOS).entitlements */, + 1CD90B70278C8000001C4FEA /* Feels (iOS)Dev.entitlements */, + 1CD90B6D278C7F89001C4FEA /* FeelsWidgetExtension.entitlements */, + 1CD90B6F278C8000001C4FEA /* FeelsWidgetExtensionDev.entitlements */, + 1CD90B69278C7F65001C4FEA /* Feels--iOS--Info.plist */, 1CD90AEA278C7DDF001C4FEA /* Shared */, + 1CD90B4B278C7E7A001C4FEA /* FeelsWidget */, 1CD90AFC278C7DE0001C4FEA /* macOS */, 1CD90B05278C7DE0001C4FEA /* Tests iOS */, 1CD90B11278C7DE0001C4FEA /* Tests macOS */, + 1CD90B46278C7E7A001C4FEA /* Frameworks */, 1CD90AF6278C7DE0001C4FEA /* Products */, ); sourceTree = ""; @@ -103,8 +196,11 @@ 1CD90AEA278C7DDF001C4FEA /* Shared */ = { isa = PBXGroup; children = ( + 1CD90B75278C8119001C4FEA /* LocalNotification.swift */, + 1CD90B31278C7E38001C4FEA /* views */, + 1CD90B60278C7EBA001C4FEA /* Models */, + 1CD90B5C278C7EAD001C4FEA /* Random.swift */, 1CD90AED278C7DDF001C4FEA /* FeelsApp.swift */, - 1CD90AEE278C7DDF001C4FEA /* ContentView.swift */, 1CD90AEF278C7DDF001C4FEA /* Persistence.swift */, 1CD90AF0278C7DE0001C4FEA /* Assets.xcassets */, 1CD90AEB278C7DDF001C4FEA /* Feels.xcdatamodeld */, @@ -119,6 +215,7 @@ 1CD90AFB278C7DE0001C4FEA /* Feels.app */, 1CD90B02278C7DE0001C4FEA /* Tests iOS.xctest */, 1CD90B0E278C7DE0001C4FEA /* Tests macOS.xctest */, + 1CD90B45278C7E7A001C4FEA /* FeelsWidgetExtension.appex */, ); name = Products; sourceTree = ""; @@ -149,6 +246,48 @@ path = "Tests macOS"; sourceTree = ""; }; + 1CD90B31278C7E38001C4FEA /* views */ = { + isa = PBXGroup; + children = ( + 1CD90B32278C7E38001C4FEA /* SettingsView.swift */, + 1CD90B33278C7E38001C4FEA /* GraphView.swift */, + 1CD90B34278C7E38001C4FEA /* AddMoodHeaderView.swift */, + 1CD90B35278C7E38001C4FEA /* ContentView.swift */, + 1CD90B36278C7E38001C4FEA /* HeaderStatsView.swift */, + ); + path = views; + sourceTree = ""; + }; + 1CD90B46278C7E7A001C4FEA /* Frameworks */ = { + isa = PBXGroup; + children = ( + 1CD90B6B278C7F78001C4FEA /* CloudKit.framework */, + 1CD90B47278C7E7A001C4FEA /* WidgetKit.framework */, + 1CD90B49278C7E7A001C4FEA /* SwiftUI.framework */, + ); + name = Frameworks; + sourceTree = ""; + }; + 1CD90B4B278C7E7A001C4FEA /* FeelsWidget */ = { + isa = PBXGroup; + children = ( + 1CD90B4C278C7E7A001C4FEA /* FeelsWidget.swift */, + 1CD90B4E278C7E7A001C4FEA /* FeelsWidget.intentdefinition */, + 1CD90B4F278C7E7A001C4FEA /* Assets.xcassets */, + 1CD90B51278C7E7A001C4FEA /* Info.plist */, + ); + path = FeelsWidget; + sourceTree = ""; + }; + 1CD90B60278C7EBA001C4FEA /* Models */ = { + isa = PBXGroup; + children = ( + 1CD90B61278C7EBA001C4FEA /* Mood.swift */, + 1CD90B62278C7EBA001C4FEA /* MoodEntryExtension.swift */, + ); + path = Models; + sourceTree = ""; + }; /* End PBXGroup section */ /* Begin PBXNativeTarget section */ @@ -159,12 +298,17 @@ 1CD90AF1278C7DE0001C4FEA /* Sources */, 1CD90AF2278C7DE0001C4FEA /* Frameworks */, 1CD90AF3278C7DE0001C4FEA /* Resources */, + 1CD90B5A278C7E7A001C4FEA /* Embed App Extensions */, ); buildRules = ( ); dependencies = ( + 1CD90B55278C7E7A001C4FEA /* PBXTargetDependency */, ); name = "Feels (iOS)"; + packageProductDependencies = ( + 1CD90B7A278C8146001C4FEA /* Charts */, + ); productName = "Feels (iOS)"; productReference = 1CD90AF5278C7DE0001C4FEA /* Feels.app */; productType = "com.apple.product-type.application"; @@ -222,6 +366,23 @@ productReference = 1CD90B0E278C7DE0001C4FEA /* Tests macOS.xctest */; productType = "com.apple.product-type.bundle.ui-testing"; }; + 1CD90B44278C7E7A001C4FEA /* FeelsWidgetExtension */ = { + isa = PBXNativeTarget; + buildConfigurationList = 1CD90B57278C7E7A001C4FEA /* Build configuration list for PBXNativeTarget "FeelsWidgetExtension" */; + buildPhases = ( + 1CD90B41278C7E7A001C4FEA /* Sources */, + 1CD90B42278C7E7A001C4FEA /* Frameworks */, + 1CD90B43278C7E7A001C4FEA /* Resources */, + ); + buildRules = ( + ); + dependencies = ( + ); + name = FeelsWidgetExtension; + productName = FeelsWidgetExtension; + productReference = 1CD90B45278C7E7A001C4FEA /* FeelsWidgetExtension.appex */; + productType = "com.apple.product-type.app-extension"; + }; /* End PBXNativeTarget section */ /* Begin PBXProject section */ @@ -246,6 +407,9 @@ CreatedOnToolsVersion = 13.2.1; TestTargetID = 1CD90AFA278C7DE0001C4FEA; }; + 1CD90B44278C7E7A001C4FEA = { + CreatedOnToolsVersion = 13.2.1; + }; }; }; buildConfigurationList = 1CD90AE9278C7DDF001C4FEA /* Build configuration list for PBXProject "Feels" */; @@ -257,6 +421,9 @@ Base, ); mainGroup = 1CD90AE5278C7DDF001C4FEA; + packageReferences = ( + 1CD90B79278C8146001C4FEA /* XCRemoteSwiftPackageReference "Charts" */, + ); productRefGroup = 1CD90AF6278C7DE0001C4FEA /* Products */; projectDirPath = ""; projectRoot = ""; @@ -265,6 +432,7 @@ 1CD90AFA278C7DE0001C4FEA /* Feels (macOS) */, 1CD90B01278C7DE0001C4FEA /* Tests iOS */, 1CD90B0D278C7DE0001C4FEA /* Tests macOS */, + 1CD90B44278C7E7A001C4FEA /* FeelsWidgetExtension */, ); }; /* End PBXProject section */ @@ -300,6 +468,14 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 1CD90B43278C7E7A001C4FEA /* Resources */ = { + isa = PBXResourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1CD90B50278C7E7A001C4FEA /* Assets.xcassets in Resources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXResourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */ @@ -307,10 +483,19 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 1CD90B39278C7E38001C4FEA /* GraphView.swift in Sources */, + 1CD90B76278C8119001C4FEA /* LocalNotification.swift in Sources */, 1CD90B16278C7DE0001C4FEA /* Feels.xcdatamodeld in Sources */, + 1CD90B5D278C7EAD001C4FEA /* Random.swift in Sources */, + 1CD90B63278C7EBA001C4FEA /* Mood.swift in Sources */, + 1CD90B53278C7E7A001C4FEA /* FeelsWidget.intentdefinition in Sources */, + 1CD90B3D278C7E38001C4FEA /* ContentView.swift in Sources */, + 1CD90B3F278C7E38001C4FEA /* HeaderStatsView.swift in Sources */, + 1CD90B3B278C7E38001C4FEA /* AddMoodHeaderView.swift in Sources */, + 1CD90B37278C7E38001C4FEA /* SettingsView.swift in Sources */, + 1CD90B66278C7EBA001C4FEA /* MoodEntryExtension.swift in Sources */, 1CD90B1C278C7DE0001C4FEA /* Persistence.swift in Sources */, 1CD90B18278C7DE0001C4FEA /* FeelsApp.swift in Sources */, - 1CD90B1A278C7DE0001C4FEA /* ContentView.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -318,10 +503,18 @@ isa = PBXSourcesBuildPhase; buildActionMask = 2147483647; files = ( + 1CD90B64278C7EBA001C4FEA /* Mood.swift in Sources */, + 1CD90B3A278C7E38001C4FEA /* GraphView.swift in Sources */, 1CD90B17278C7DE0001C4FEA /* Feels.xcdatamodeld in Sources */, + 1CD90B3E278C7E38001C4FEA /* ContentView.swift in Sources */, + 1CD90B40278C7E38001C4FEA /* HeaderStatsView.swift in Sources */, + 1CD90B3C278C7E38001C4FEA /* AddMoodHeaderView.swift in Sources */, + 1CD90B38278C7E38001C4FEA /* SettingsView.swift in Sources */, + 1CD90B67278C7EBA001C4FEA /* MoodEntryExtension.swift in Sources */, + 1CD90B77278C8119001C4FEA /* LocalNotification.swift in Sources */, 1CD90B1D278C7DE0001C4FEA /* Persistence.swift in Sources */, 1CD90B19278C7DE0001C4FEA /* FeelsApp.swift in Sources */, - 1CD90B1B278C7DE0001C4FEA /* ContentView.swift in Sources */, + 1CD90B5E278C7EAD001C4FEA /* Random.swift in Sources */, ); runOnlyForDeploymentPostprocessing = 0; }; @@ -343,6 +536,21 @@ ); runOnlyForDeploymentPostprocessing = 0; }; + 1CD90B41278C7E7A001C4FEA /* Sources */ = { + isa = PBXSourcesBuildPhase; + buildActionMask = 2147483647; + files = ( + 1CD90B65278C7EBA001C4FEA /* Mood.swift in Sources */, + 1CD90B5F278C7EAD001C4FEA /* Random.swift in Sources */, + 1CD90B5B278C7E91001C4FEA /* Persistence.swift in Sources */, + 1CD90B68278C7EBA001C4FEA /* MoodEntryExtension.swift in Sources */, + 1CD90B78278C8119001C4FEA /* LocalNotification.swift in Sources */, + 1CD90B71278C80CA001C4FEA /* Feels.xcdatamodeld in Sources */, + 1CD90B52278C7E7A001C4FEA /* FeelsWidget.intentdefinition in Sources */, + 1CD90B4D278C7E7A001C4FEA /* FeelsWidget.swift in Sources */, + ); + runOnlyForDeploymentPostprocessing = 0; + }; /* End PBXSourcesBuildPhase section */ /* Begin PBXTargetDependency section */ @@ -356,6 +564,11 @@ target = 1CD90AFA278C7DE0001C4FEA /* Feels (macOS) */; targetProxy = 1CD90B0F278C7DE0001C4FEA /* PBXContainerItemProxy */; }; + 1CD90B55278C7E7A001C4FEA /* PBXTargetDependency */ = { + isa = PBXTargetDependency; + target = 1CD90B44278C7E7A001C4FEA /* FeelsWidgetExtension */; + targetProxy = 1CD90B54278C7E7A001C4FEA /* PBXContainerItemProxy */; + }; /* End PBXTargetDependency section */ /* Begin XCBuildConfiguration section */ @@ -473,13 +686,16 @@ 1CD90B23278C7DE0001C4FEA /* Debug */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = "Feels (iOS)Dev.entitlements"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = V3PF3M6B6U; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "Feels--iOS--Info.plist"; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -491,7 +707,7 @@ "@executable_path/Frameworks", ); MARKETING_VERSION = 1.0; - PRODUCT_BUNDLE_IDENTIFIER = com.88oak.Feels; + PRODUCT_BUNDLE_IDENTIFIER = com.88oak.FeelsDev; PRODUCT_NAME = Feels; SDKROOT = iphoneos; SWIFT_EMIT_LOC_STRINGS = YES; @@ -503,13 +719,16 @@ 1CD90B24278C7DE0001C4FEA /* Release */ = { isa = XCBuildConfiguration; buildSettings = { + ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + CODE_SIGN_ENTITLEMENTS = "Feels (iOS).entitlements"; CODE_SIGN_STYLE = Automatic; CURRENT_PROJECT_VERSION = 1; DEVELOPMENT_TEAM = V3PF3M6B6U; ENABLE_PREVIEWS = YES; GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = "Feels--iOS--Info.plist"; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES; @@ -666,6 +885,67 @@ }; name = Release; }; + 1CD90B58278C7E7A001C4FEA /* Debug */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CODE_SIGN_ENTITLEMENTS = FeelsWidgetExtensionDev.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = V3PF3M6B6U; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = FeelsWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = FeelsWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 15.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.88oak.FeelsDev.FeelsWidgetDev; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + }; + name = Debug; + }; + 1CD90B59278C7E7A001C4FEA /* Release */ = { + isa = XCBuildConfiguration; + buildSettings = { + ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; + ASSETCATALOG_COMPILER_WIDGET_BACKGROUND_COLOR_NAME = WidgetBackground; + CODE_SIGN_ENTITLEMENTS = FeelsWidgetExtension.entitlements; + CODE_SIGN_STYLE = Automatic; + CURRENT_PROJECT_VERSION = 1; + DEVELOPMENT_TEAM = V3PF3M6B6U; + GENERATE_INFOPLIST_FILE = YES; + INFOPLIST_FILE = FeelsWidget/Info.plist; + INFOPLIST_KEY_CFBundleDisplayName = FeelsWidget; + INFOPLIST_KEY_NSHumanReadableCopyright = ""; + IPHONEOS_DEPLOYMENT_TARGET = 15.2; + LD_RUNPATH_SEARCH_PATHS = ( + "$(inherited)", + "@executable_path/Frameworks", + "@executable_path/../../Frameworks", + ); + MARKETING_VERSION = 1.0; + PRODUCT_BUNDLE_IDENTIFIER = com.88oak.Feels.FeelsWidget; + PRODUCT_NAME = "$(TARGET_NAME)"; + SDKROOT = iphoneos; + SKIP_INSTALL = YES; + SWIFT_EMIT_LOC_STRINGS = YES; + SWIFT_VERSION = 5.0; + TARGETED_DEVICE_FAMILY = "1,2"; + VALIDATE_PRODUCT = YES; + }; + name = Release; + }; /* End XCBuildConfiguration section */ /* Begin XCConfigurationList section */ @@ -714,8 +994,36 @@ defaultConfigurationIsVisible = 0; defaultConfigurationName = Release; }; + 1CD90B57278C7E7A001C4FEA /* Build configuration list for PBXNativeTarget "FeelsWidgetExtension" */ = { + isa = XCConfigurationList; + buildConfigurations = ( + 1CD90B58278C7E7A001C4FEA /* Debug */, + 1CD90B59278C7E7A001C4FEA /* Release */, + ); + defaultConfigurationIsVisible = 0; + defaultConfigurationName = Release; + }; /* End XCConfigurationList section */ +/* Begin XCRemoteSwiftPackageReference section */ + 1CD90B79278C8146001C4FEA /* XCRemoteSwiftPackageReference "Charts" */ = { + isa = XCRemoteSwiftPackageReference; + repositoryURL = "https://github.com/danielgindi/Charts"; + requirement = { + kind = upToNextMajorVersion; + minimumVersion = 3.0.0; + }; + }; +/* End XCRemoteSwiftPackageReference section */ + +/* Begin XCSwiftPackageProductDependency section */ + 1CD90B7A278C8146001C4FEA /* Charts */ = { + isa = XCSwiftPackageProductDependency; + package = 1CD90B79278C8146001C4FEA /* XCRemoteSwiftPackageReference "Charts" */; + productName = Charts; + }; +/* End XCSwiftPackageProductDependency section */ + /* Begin XCVersionGroup section */ 1CD90AEB278C7DDF001C4FEA /* Feels.xcdatamodeld */ = { isa = XCVersionGroup; diff --git a/Feels.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved b/Feels.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved new file mode 100644 index 0000000..0878b14 --- /dev/null +++ b/Feels.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved @@ -0,0 +1,16 @@ +{ + "object": { + "pins": [ + { + "package": "Charts", + "repositoryURL": "https://github.com/danielgindi/Charts", + "state": { + "branch": null, + "revision": "66546404a6739173b8e436ab6bc1f2897cd08594", + "version": "3.6.0" + } + } + ] + }, + "version": 1 +} diff --git a/Feels.xcodeproj/xcuserdata/treyt.xcuserdatad/xcschemes/xcschememanagement.plist b/Feels.xcodeproj/xcuserdata/treyt.xcuserdatad/xcschemes/xcschememanagement.plist index b37aaf7..ff9be8c 100644 --- a/Feels.xcodeproj/xcuserdata/treyt.xcuserdatad/xcschemes/xcschememanagement.plist +++ b/Feels.xcodeproj/xcuserdata/treyt.xcuserdatad/xcschemes/xcschememanagement.plist @@ -14,6 +14,53 @@ orderHint 1 + FeelsWidgetExtension.xcscheme_^#shared#^_ + + orderHint + 2 + + PlaygroundChart (Playground) 1.xcscheme + + isShown + + orderHint + 4 + + PlaygroundChart (Playground) 2.xcscheme + + isShown + + orderHint + 5 + + PlaygroundChart (Playground) 3.xcscheme + + isShown + + orderHint + 6 + + PlaygroundChart (Playground) 4.xcscheme + + isShown + + orderHint + 7 + + PlaygroundChart (Playground) 5.xcscheme + + isShown + + orderHint + 8 + + PlaygroundChart (Playground).xcscheme + + isShown + + orderHint + 3 + diff --git a/FeelsWidget/Assets.xcassets/AccentColor.colorset/Contents.json b/FeelsWidget/Assets.xcassets/AccentColor.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/FeelsWidget/Assets.xcassets/AccentColor.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FeelsWidget/Assets.xcassets/AppIcon.appiconset/Contents.json b/FeelsWidget/Assets.xcassets/AppIcon.appiconset/Contents.json new file mode 100644 index 0000000..9221b9b --- /dev/null +++ b/FeelsWidget/Assets.xcassets/AppIcon.appiconset/Contents.json @@ -0,0 +1,98 @@ +{ + "images" : [ + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "20x20" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "29x29" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "40x40" + }, + { + "idiom" : "iphone", + "scale" : "2x", + "size" : "60x60" + }, + { + "idiom" : "iphone", + "scale" : "3x", + "size" : "60x60" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "20x20" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "29x29" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "40x40" + }, + { + "idiom" : "ipad", + "scale" : "1x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "76x76" + }, + { + "idiom" : "ipad", + "scale" : "2x", + "size" : "83.5x83.5" + }, + { + "idiom" : "ios-marketing", + "scale" : "1x", + "size" : "1024x1024" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FeelsWidget/Assets.xcassets/Contents.json b/FeelsWidget/Assets.xcassets/Contents.json new file mode 100644 index 0000000..73c0059 --- /dev/null +++ b/FeelsWidget/Assets.xcassets/Contents.json @@ -0,0 +1,6 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FeelsWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json b/FeelsWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json new file mode 100644 index 0000000..eb87897 --- /dev/null +++ b/FeelsWidget/Assets.xcassets/WidgetBackground.colorset/Contents.json @@ -0,0 +1,11 @@ +{ + "colors" : [ + { + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/FeelsWidget/FeelsWidget.intentdefinition b/FeelsWidget/FeelsWidget.intentdefinition new file mode 100644 index 0000000..bdb4045 --- /dev/null +++ b/FeelsWidget/FeelsWidget.intentdefinition @@ -0,0 +1,59 @@ + + + + + INEnums + + INIntentDefinitionModelVersion + 1.2 + INIntentDefinitionNamespace + 88xZPY + INIntentDefinitionSystemVersion + 20A294 + INIntentDefinitionToolsBuildVersion + 12A6144 + INIntentDefinitionToolsVersion + 12.0 + INIntents + + + INIntentCategory + information + INIntentDescriptionID + tVvJ9c + INIntentEligibleForWidgets + + INIntentIneligibleForSuggestions + + INIntentName + Configuration + INIntentResponse + + INIntentResponseCodes + + + INIntentResponseCodeName + success + INIntentResponseCodeSuccess + + + + INIntentResponseCodeName + failure + + + + INIntentTitle + Configuration + INIntentTitleID + gpCwrM + INIntentType + Custom + INIntentVerb + View + + + INTypes + + + diff --git a/FeelsWidget/FeelsWidget.swift b/FeelsWidget/FeelsWidget.swift new file mode 100644 index 0000000..f972641 --- /dev/null +++ b/FeelsWidget/FeelsWidget.swift @@ -0,0 +1,259 @@ +// +// FeelsWidget.swift +// FeelsWidget +// +// Created by Trey Tartt on 1/7/22. +// + +import WidgetKit +import SwiftUI +import Intents +import CoreData + +struct Provider: IntentTimelineProvider { + /* + placeholder for widget, no data + gets redacted auto + */ + func placeholder(in context: Context) -> SimpleEntry { + let date = Date() + let moodEntry = PersistenceController.shared.moodEntries(forStartDate: date, count: 5) + return SimpleEntry(date: date, configuration: ConfigurationIntent(), mood: moodEntry) + } + + func getSnapshot(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (SimpleEntry) -> ()) { + if context.isPreview { + + } + let date = Date() + let moodEntry = PersistenceController.shared.moodEntries(forStartDate: date, count: 5) + + let entry = SimpleEntry(date: date, configuration: configuration, mood: moodEntry) + completion(entry) + } + + func getTimeline(for configuration: ConfigurationIntent, in context: Context, completion: @escaping (Timeline) -> ()) { + var entries: [SimpleEntry] = [] + + var calendar = Calendar.current + calendar.timeZone = NSTimeZone.local + let todayStart = calendar.startOfDay(for: Date()) + let userEntries = PersistenceController.shared.moodEntries(forStartDate: todayStart, count: 10) + + let entry = SimpleEntry(date: Date(), configuration: configuration, mood: userEntries) + entries.append(entry) + + let timeline = Timeline(entries: entries, policy: .after(Random.widgetUpdateTime)) + completion(timeline) + } +} + +struct SimpleEntry: TimelineEntry { + let date: Date + let configuration: ConfigurationIntent + let mood: [MoodEntry] + let showStats: Bool + + init(date: Date, configuration: ConfigurationIntent, mood: [MoodEntry], showStats: Bool = false) { + self.date = date + self.configuration = configuration + self.mood = mood + self.showStats = showStats + } +} + +struct FeelsWidgetEntryView : View { + @Environment(\.sizeCategory) var sizeCategory + @Environment(\.widgetFamily) var family + + var entry: Provider.Entry + + @ViewBuilder + var body: some View { + ZStack { + Color(UIColor.systemBackground) + + switch family { + case .systemSmall: + SmallWidgetView(entry: entry) + case .systemMedium: + MediumWidgetView(entry: entry) + case .systemLarge: + LargeWidgetView(entry: entry) + @unknown default: + fatalError() + } + } + } +} + +struct SmallWidgetView: View { + var entry: Provider.Entry + + var body: some View { + VStack { + EntryCardCollectionView(moodEntries: Array([entry.mood.first!])) + .padding() + } + } +} + +struct MediumWidgetView: View { + var entry: Provider.Entry + + var formatter: DateFormatter { + let dateFormatter = DateFormatter() + dateFormatter.dateStyle = .medium + return dateFormatter + } + + var firstGroup: [MoodEntry] { + Array(self.entry.mood.prefix(5)) + } + + var body: some View { + VStack { + Spacer() + + HStack { + Text(firstGroup.first?.date ?? Date(), formatter: formatter) + .font(.system(.footnote)) + Text(" - ") + .font(.system(.footnote)) + Text(firstGroup.last?.date ?? Date(), formatter: formatter) + .font(.system(.footnote)) + } + .frame(minWidth: 0, maxWidth: .infinity) + .multilineTextAlignment(.leading) + + EntryCardCollectionView(moodEntries: firstGroup) + .frame(minHeight: 0, maxHeight: 55) + .padding() + + Spacer() + } + } +} + +struct LargeWidgetView: View { + var entry: Provider.Entry + + var formatter: DateFormatter { + let dateFormatter = DateFormatter() + dateFormatter.dateStyle = .medium + return dateFormatter + } + + var firstGroup: [MoodEntry] { + Array(self.entry.mood.prefix(5)) + } + + var lastGroup: [MoodEntry] { + Array(self.entry.mood.suffix(5)) + } + + + var body: some View { + VStack { + Spacer() + + HStack { + Text(firstGroup.first?.date ?? Date(), formatter: formatter) + .font(.system(.footnote)) + Text(" - ") + .font(.system(.footnote)) + Text(firstGroup.last?.date ?? Date(), formatter: formatter) + .font(.system(.footnote)) + } + .frame(minWidth: 0, maxWidth: .infinity) + .multilineTextAlignment(.leading) + + EntryCardCollectionView(moodEntries: firstGroup) + .frame(minHeight: 0, maxHeight: 55) + .padding() + + Spacer() + + HStack { + Text(lastGroup.first?.date ?? Date(), formatter: formatter) + .font(.system(.footnote)) + Text(" - ") + Text(lastGroup.last?.date ?? Date(), formatter: formatter) + .font(.system(.footnote)) + } + .frame(minWidth: 0, maxWidth: .infinity) + .multilineTextAlignment(.leading) + + EntryCardCollectionView(moodEntries: lastGroup) + .frame(minHeight: 0, maxHeight: 55) + .padding() + + Spacer() + } + } +} + +struct EntryCardCollectionView: View { + var moodEntries: [MoodEntry] + + var body: some View { + ZStack { + Color(UIColor.secondarySystemBackground) + HStack { + ForEach(moodEntries) { mood in + EntryCard(moodEntry: mood) + } + } + .padding() + } + .clipShape(RoundedRectangle(cornerRadius: 25, style: .continuous)) + } +} + +struct EntryCard: View { + var moodEntry: MoodEntry + + var body: some View { + moodEntry.mood.icon.font(.system(size: 50)) + } +} + +@main +struct FeelsWidget: Widget { + let kind: String = "FeelsWidget" + + var body: some WidgetConfiguration { + IntentConfiguration(kind: kind, + intent: ConfigurationIntent.self, + provider: Provider()) { entry in + FeelsWidgetEntryView(entry: entry) + } + .configurationDisplayName("Feels") + .description("") + .supportedFamilies([.systemSmall, .systemMedium, .systemLarge]) + } +} + +struct FeelsWidget_Previews: PreviewProvider { + static var previews: some View { + Group { + FeelsWidgetEntryView(entry: SimpleEntry(date: Date(), + configuration: ConfigurationIntent(), + mood: PersistenceController.shared.randomEntries(count: 1))) + .previewContext(WidgetPreviewContext(family: .systemSmall)) + .environment(\.sizeCategory, .small) + + FeelsWidgetEntryView(entry: SimpleEntry(date: Date(), + configuration: ConfigurationIntent(), + mood: PersistenceController.shared.randomEntries(count: 3))) + .previewContext(WidgetPreviewContext(family: .systemMedium)) + .environment(\.sizeCategory, .medium) + + FeelsWidgetEntryView(entry: SimpleEntry(date: Date(), + configuration: ConfigurationIntent(), + mood: PersistenceController.shared.randomEntries(count: 10))) + .previewContext(WidgetPreviewContext(family: .systemLarge)) + .environment(\.sizeCategory, .large) + } + } +} diff --git a/FeelsWidget/Info.plist b/FeelsWidget/Info.plist new file mode 100644 index 0000000..0f118fb --- /dev/null +++ b/FeelsWidget/Info.plist @@ -0,0 +1,11 @@ + + + + + NSExtension + + NSExtensionPointIdentifier + com.apple.widgetkit-extension + + + diff --git a/FeelsWidgetExtension.entitlements b/FeelsWidgetExtension.entitlements new file mode 100644 index 0000000..2ead4a3 --- /dev/null +++ b/FeelsWidgetExtension.entitlements @@ -0,0 +1,16 @@ + + + + + aps-environment + development + com.apple.developer.icloud-container-identifiers + + iCloud.com.88oak.feels + + com.apple.developer.icloud-services + + CloudKit + + + diff --git a/FeelsWidgetExtensionDev.entitlements b/FeelsWidgetExtensionDev.entitlements new file mode 100644 index 0000000..1662af0 --- /dev/null +++ b/FeelsWidgetExtensionDev.entitlements @@ -0,0 +1,16 @@ + + + + + aps-environment + development + com.apple.developer.icloud-container-identifiers + + iCloud.com.88oak.feelsDev + + com.apple.developer.icloud-services + + CloudKit + + + diff --git a/Shared/ContentView.swift b/Shared/ContentView.swift deleted file mode 100644 index 281b0c8..0000000 --- a/Shared/ContentView.swift +++ /dev/null @@ -1,90 +0,0 @@ -// -// ContentView.swift -// Shared -// -// Created by Trey Tartt on 1/10/22. -// - -import SwiftUI -import CoreData - -struct ContentView: View { - @Environment(\.managedObjectContext) private var viewContext - - @FetchRequest( - sortDescriptors: [NSSortDescriptor(keyPath: \Item.timestamp, ascending: true)], - animation: .default) - private var items: FetchedResults - - var body: some View { - NavigationView { - List { - ForEach(items) { item in - NavigationLink { - Text("Item at \(item.timestamp!, formatter: itemFormatter)") - } label: { - Text(item.timestamp!, formatter: itemFormatter) - } - } - .onDelete(perform: deleteItems) - } - .toolbar { -#if os(iOS) - ToolbarItem(placement: .navigationBarTrailing) { - EditButton() - } -#endif - ToolbarItem { - Button(action: addItem) { - Label("Add Item", systemImage: "plus") - } - } - } - Text("Select an item") - } - } - - private func addItem() { - withAnimation { - let newItem = Item(context: viewContext) - newItem.timestamp = Date() - - do { - try viewContext.save() - } catch { - // Replace this implementation with code to handle the error appropriately. - // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. - let nsError = error as NSError - fatalError("Unresolved error \(nsError), \(nsError.userInfo)") - } - } - } - - private func deleteItems(offsets: IndexSet) { - withAnimation { - offsets.map { items[$0] }.forEach(viewContext.delete) - - do { - try viewContext.save() - } catch { - // Replace this implementation with code to handle the error appropriately. - // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. - let nsError = error as NSError - fatalError("Unresolved error \(nsError), \(nsError.userInfo)") - } - } - } -} - -private let itemFormatter: DateFormatter = { - let formatter = DateFormatter() - formatter.dateStyle = .short - formatter.timeStyle = .medium - return formatter -}() - -struct ContentView_Previews: PreviewProvider { - static var previews: some View { - ContentView().environment(\.managedObjectContext, PersistenceController.preview.container.viewContext) - } -} diff --git a/Shared/Feels.xcdatamodeld/Shared.xcdatamodel/contents b/Shared/Feels.xcdatamodeld/Shared.xcdatamodel/contents index e8d6ec8..432945a 100644 --- a/Shared/Feels.xcdatamodeld/Shared.xcdatamodel/contents +++ b/Shared/Feels.xcdatamodeld/Shared.xcdatamodel/contents @@ -1,9 +1,11 @@ - - + + + + - + \ No newline at end of file diff --git a/Shared/FeelsApp.swift b/Shared/FeelsApp.swift index 6d499dc..ccfe0d7 100644 --- a/Shared/FeelsApp.swift +++ b/Shared/FeelsApp.swift @@ -2,19 +2,67 @@ // FeelsApp.swift // Shared // -// Created by Trey Tartt on 1/10/22. +// Created by Trey Tartt on 1/5/22. // import SwiftUI +import BackgroundTasks @main struct FeelsApp: App { + @Environment(\.scenePhase) private var scenePhase let persistenceController = PersistenceController.shared - + + init() { +// persistenceController.fillInMissingDates() + + BGTaskScheduler.shared.cancelAllTaskRequests() + BGTaskScheduler.shared.register(forTaskWithIdentifier: BGTask.updateDBMissingID, using: nil) { (task) in + BGTask.runFillInMissingDatesTask(task: task as! BGProcessingTask) + } + } + var body: some Scene { WindowGroup { ContentView() .environment(\.managedObjectContext, persistenceController.container.viewContext) + }.onChange(of: scenePhase) { phase in + if phase == .background { + BGTask.scheduleBackgroundProcessing() + print("background") + } + } + } +} + +class BGTask { + static let updateDBMissingID = "com.88oak.dbUpdateMissing" + + class func runFillInMissingDatesTask(task: BGProcessingTask) { + BGTask.scheduleBackgroundProcessing() + + task.expirationHandler = { + task.setTaskCompleted(success: false) + } + + PersistenceController.shared.fillInMissingDates() + task.setTaskCompleted(success: true) + } + + @available(iOS 13.0, *) + class func scheduleBackgroundProcessing() { + let request = BGProcessingTaskRequest(identifier: BGTask.updateDBMissingID) + request.requiresNetworkConnectivity = false + request.requiresExternalPower = false + + var runDate = Calendar.current.date(byAdding: .day, value: 1, to: Date()) + runDate = Calendar.current.date(bySettingHour: 0, minute: 1, second: 0, of: runDate!) + request.earliestBeginDate = runDate + + do { + try BGTaskScheduler.shared.submit(request) + } catch { + print("Could not schedule image fetch: (error)") } } } diff --git a/Shared/LocalNotification.swift b/Shared/LocalNotification.swift new file mode 100644 index 0000000..c5a41b2 --- /dev/null +++ b/Shared/LocalNotification.swift @@ -0,0 +1,143 @@ +// +// LocalNotification.swift +// Feels +// +// Created by Trey Tartt on 1/8/22. +// + +import Foundation +import UserNotifications + +class LocalNotification { + public enum ActionType: String { + case horrible = "HORRIBLE_ACTION" + case bad = "BAD_ACTION" + case average = "AVERAGE_ACTION" + case good = "GOOD_ACTION" + case great = "GREAT_ACTION" + } + static let categoryName = "MOOD_UPDATE" + + public class func testIfEnabled(completion: @escaping (Result) -> Void) { + UNUserNotificationCenter.current().requestAuthorization(options: [.alert, .badge, .sound]) { success, error in + if success { + completion(.success(true)) + } else if let error = error { + completion(.failure(error)) + } + } + } + + public class func scheduleReminder(atTime time: Date) { + let _ = LocalNotification.createNotificationCategory() + + let notificationContent = UNMutableNotificationContent() + notificationContent.title = "How was your day?" + notificationContent.badge = NSNumber(value: 1) + notificationContent.sound = .default + notificationContent.categoryIdentifier = LocalNotification.categoryName + + let calendar = Calendar.current + let time = calendar.dateComponents([.hour,.minute], from: time) + + var datComp = DateComponents() + datComp.hour = time.hour + datComp.minute = time.minute + + let trigger = UNCalendarNotificationTrigger(dateMatching: datComp, repeats: true) + let request = UNNotificationRequest(identifier: UUID().uuidString, content: notificationContent, trigger: trigger) + UNUserNotificationCenter.current().add(request) { (error : Error?) in + if let theError = error { + print(theError.localizedDescription) + } + } + } + + private class func createNotificationCategory() -> UNNotificationCategory { + let moodCategory = + UNNotificationCategory(identifier: LocalNotification.categoryName, + actions: [horribleAction, badAction, averageAction, goodAction, greatAction], + intentIdentifiers: [], + hiddenPreviewsBodyPlaceholder: "", + options: .customDismissAction) + // Register the notification type. + let notificationCenter = UNUserNotificationCenter.current() + notificationCenter.setNotificationCategories([moodCategory]) + + return moodCategory + } + + private class var horribleAction: UNNotificationAction { + let acceptAction = UNNotificationAction(identifier: ActionType.horrible.rawValue, + title: "Horrible", + options: []) + return acceptAction + } + + private class var badAction: UNNotificationAction { + let acceptAction = UNNotificationAction(identifier: ActionType.bad.rawValue, + title: "Bad", + options: []) + return acceptAction + } + + private class var averageAction: UNNotificationAction { + let acceptAction = UNNotificationAction(identifier: ActionType.average.rawValue, + title: "Average", + options: []) + return acceptAction + } + + private class var goodAction: UNNotificationAction { + let acceptAction = UNNotificationAction(identifier: ActionType.good.rawValue, + title: "Good", + options: []) + return acceptAction + } + + private class var greatAction: UNNotificationAction { + let acceptAction = UNNotificationAction(identifier: ActionType.great.rawValue, + title: "Great", + options: []) + return acceptAction + } + + public class func removeNotificaiton() { + UNUserNotificationCenter.current().removeAllPendingNotificationRequests() + } +} + +class NotificationDelegate: NSObject, ObservableObject, UNUserNotificationCenterDelegate { + @Published var notificationCounter = 0 + + override init() { + super.init() + UNUserNotificationCenter.current().delegate = self + } + + func requestAuthorization() { + + } + + func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) { + completionHandler([.badge, .banner, .sound]) + } + + func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) { + if let action = LocalNotification.ActionType(rawValue: response.actionIdentifier) { + switch action { + case .horrible: + PersistenceController.shared.add(mood: .horrible, forDate: Date()) + case .bad: + PersistenceController.shared.add(mood: .bad, forDate: Date()) + case .average: + PersistenceController.shared.add(mood: .average, forDate: Date()) + case .good: + PersistenceController.shared.add(mood: .good, forDate: Date()) + case .great: + PersistenceController.shared.add(mood: .great, forDate: Date()) + } + } + completionHandler() + } +} diff --git a/Shared/Models/Mood.swift b/Shared/Models/Mood.swift new file mode 100644 index 0000000..1c407d8 --- /dev/null +++ b/Shared/Models/Mood.swift @@ -0,0 +1,63 @@ +// +// Mood.swift +// Feels +// +// Created by Trey Tartt on 1/5/22. +// + +import Foundation +import SwiftUI + +enum Mood: Int { + case horrible + case bad + case average + case good + case great + case missing + + var strValue: String { + switch self { + case .horrible: + return "Horrible" + case .bad: + return "Bad" + case .average: + return "Average" + case .good: + return "Good" + case .great: + return "Great" + case .missing: + return "Missing" + } + } + + static var allValues: [Mood] { + return [Mood.horrible, Mood.bad, Mood.average, Mood.good, Mood.great] + } + + var icon: Text { + switch self { + + case .horrible: + return Text("😫") + case .bad: + return Text("🙁") + case .average: + return Text("😐") + case .good: + return Text("🙂") + case .great: + return Text("😆") + case .missing: + return Text("🚫") + } + } +} + +extension Mood: Identifiable { + var id: Int { + rawValue + } +} diff --git a/Shared/Models/MoodEntryExtension.swift b/Shared/Models/MoodEntryExtension.swift new file mode 100644 index 0000000..b22725a --- /dev/null +++ b/Shared/Models/MoodEntryExtension.swift @@ -0,0 +1,19 @@ +// +// MoodEntryExtension.swift +// Feels +// +// Created by Trey Tartt on 1/5/22. +// + +import Foundation + + +extension MoodEntry { + var moodString: String { + return Mood.init(rawValue: Int(self.moodValue))?.strValue ?? "NA" + } + + var mood: Mood { + return Mood.init(rawValue: Int(self.moodValue))! + } +} diff --git a/Shared/Mooood.xcdatamodeld/.xccurrentversion b/Shared/Mooood.xcdatamodeld/.xccurrentversion new file mode 100644 index 0000000..775cb51 --- /dev/null +++ b/Shared/Mooood.xcdatamodeld/.xccurrentversion @@ -0,0 +1,8 @@ + + + + + _XCCurrentVersionName + Shared.xcdatamodel + + diff --git a/Shared/Mooood.xcdatamodeld/Shared.xcdatamodel/contents b/Shared/Mooood.xcdatamodeld/Shared.xcdatamodel/contents new file mode 100644 index 0000000..432945a --- /dev/null +++ b/Shared/Mooood.xcdatamodeld/Shared.xcdatamodel/contents @@ -0,0 +1,11 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/Shared/Persistence.swift b/Shared/Persistence.swift index 20ac4b0..70b8df5 100644 --- a/Shared/Persistence.swift +++ b/Shared/Persistence.swift @@ -2,20 +2,71 @@ // Persistence.swift // Shared // -// Created by Trey Tartt on 1/10/22. +// Created by Trey Tartt on 1/5/22. // import CoreData struct PersistenceController { - static let shared = PersistenceController() + static let shared = PersistenceController.persistenceController + + private static var persistenceController: PersistenceController { +#if targetEnvironment(simulator) + return PersistenceController(inMemory: false) +#else + return PersistenceController(inMemory: false) +#endif - static var preview: PersistenceController = { - let result = PersistenceController(inMemory: true) - let viewContext = result.container.viewContext - for _ in 0..<10 { - let newItem = Item(context: viewContext) + } + + public var viewContext: NSManagedObjectContext { + return PersistenceController.shared.container.viewContext + } + + public func add(mood: Mood, forDate date: Date) { + let newItem = MoodEntry(context: viewContext) + newItem.timestamp = Date() + newItem.moodValue = Int16(mood.rawValue) + newItem.date = date + + do { + try viewContext.save() + } catch { + let nsError = error as NSError + fatalError("Unresolved error \(nsError), \(nsError.userInfo)") + } + } + + public func moodEntries(forStartDate date: Date, count: Int) -> [MoodEntry] { + let fetchRequest = NSFetchRequest(entityName: "MoodEntry") + + fetchRequest.fetchLimit = count + fetchRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false)] + + var calendar = Calendar.current + calendar.timeZone = NSTimeZone.local + + let dateFrom = calendar.startOfDay(for: Date()) + + // Set predicate as date being today's date + let fromPredicate = NSPredicate(format: "date <= %@", dateFrom as NSDate) + let datePredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [fromPredicate]) + fetchRequest.predicate = datePredicate + let entries = try! viewContext.fetch(fetchRequest) + + if entries.count >= count { + return Array(entries) + } else { + return entries + } + } + + func populateTestData() { + for idx in 1..<25 { + let newItem = MoodEntry(context: viewContext) newItem.timestamp = Date() + newItem.moodValue = Int16(Mood.allValues.randomElement()!.rawValue) + newItem.date = Calendar.current.date(byAdding: .day, value: -idx, to: Date()) } do { try viewContext.save() @@ -25,11 +76,69 @@ struct PersistenceController { let nsError = error as NSError fatalError("Unresolved error \(nsError), \(nsError.userInfo)") } - return result - }() - + } + + func populateMemory() { + for idx in 1..<25 { + let newItem = MoodEntry(context: viewContext) + newItem.timestamp = Calendar.current.date(byAdding: .day, value: -idx, to: Date()) + newItem.moodValue = Int16(Mood.allValues.randomElement()!.rawValue) + newItem.date = Calendar.current.date(byAdding: .day, value: -idx, to: Date()) + } + do { + try viewContext.save() + } catch { + // Replace this implementation with code to handle the error appropriately. + // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. + let nsError = error as NSError + fatalError("Unresolved error \(nsError), \(nsError.userInfo)") + } + } + + func fillInMissingDates() { + let fetchRequest = NSFetchRequest(entityName: "MoodEntry") + fetchRequest.sortDescriptors = [NSSortDescriptor(key: "date", ascending: false)] + let entries = try! viewContext.fetch(fetchRequest) + + if let earliestDate = entries.last?.date { + let diffInDays = Calendar.current.dateComponents([.day], from: earliestDate, to: Date()).day + + for idx in 1.. = NSFetchRequest(entityName: "MoodEntry") + let deleteRequest = NSBatchDeleteRequest(fetchRequest: fetchRequest) + + do { + try viewContext.executeAndMergeChanges(using: deleteRequest) + try viewContext.save() + } catch let error as NSError { + fatalError("Unresolved error \(error), \(error.userInfo)") + } + } + + public func randomEntries(count: Int) -> [MoodEntry] { + var entries = [MoodEntry]() + + for idx in 0.. + + var body: some View { + TabView { + mainView + .tabItem { + Label("Main", systemImage: "list.dash") + } + + GraphView() + .tabItem { + Label("Graph", systemImage: "chart.line.uptrend.xyaxis") + } + } + } + + private var settingsButtonView: some View { + HStack { + Spacer() + Button(action: { + showingSheet.toggle() + }, label: { + Image(systemName: "gear") + .foregroundColor(Color(UIColor.systemGray)) + .font(.system(size: 20)) + }).sheet(isPresented: $showingSheet) { + SettingsView() + }.padding(.trailing) + } + } + + private var listView: some View { + List { + ForEach(items) { item in + HStack { + item.mood.icon + .font(.system(size: 50)) + VStack { + Text("\(item.moodString)") + .font(.title) + .foregroundColor(Color(UIColor.systemGray)) + .frame(maxWidth: .infinity, alignment: .leading) + Text(item.date!, style: .date) + .font(.body) + .foregroundColor(Color(UIColor.label)) + .frame(maxWidth: .infinity, alignment: .leading) + } + .padding(.leading) + } + } + .onDelete(perform: deleteItems) + } + } + + private var mainView: some View { + VStack{ + settingsButtonView + if shouldShowTodayInput() { + AddMoodHeaderView() + .frame(minHeight: 85, maxHeight: 180) + .frame(minWidth: 0, maxWidth: .infinity) + } else { + HeaderStatsView(entries: [ + //x - position of a bar, y - height of a bar + BarChartDataEntry(x: 1, y: 1), + BarChartDataEntry(x: 2, y: 5), + BarChartDataEntry(x: 3, y: 2), + BarChartDataEntry(x: 4, y: 4), + BarChartDataEntry(x: 5, y: 1) + ]) + .frame(minHeight: 85, maxHeight: 180) + } + listView + } + } + + private func deleteItems(offsets: IndexSet) { + withAnimation { + offsets.map { items[$0] }.forEach(viewContext.delete) + + do { + try viewContext.save() + } catch { + // Replace this implementation with code to handle the error appropriately. + // fatalError() causes the application to generate a crash log and terminate. You should not use this function in a shipping application, although it may be useful during development. + let nsError = error as NSError + fatalError("Unresolved error \(nsError), \(nsError.userInfo)") + } + } + } + + private func shouldShowTodayInput() -> Bool { + let fetchRequest = NSFetchRequest(entityName: "MoodEntry") + + var calendar = Calendar.current + calendar.timeZone = NSTimeZone.local + + // Get today's beginning & end + let dateFrom = calendar.startOfDay(for: Date()) // eg. 2016-10-10 00:00:00 + let dateTo = calendar.date(byAdding: .day, value: 1, to: dateFrom)! + // Note: Times are printed in UTC. Depending on where you live it won't print 00:00:00 but it will work with UTC times which can be converted to local time + + // Set predicate as date being today's date + let fromPredicate = NSPredicate(format: "%@ <= %K", dateFrom as NSDate, #keyPath(MoodEntry.date)) + let toPredicate = NSPredicate(format: "%K < %@", #keyPath(MoodEntry.date), dateTo as NSDate) + let datePredicate = NSCompoundPredicate(andPredicateWithSubpredicates: [fromPredicate, toPredicate]) + fetchRequest.predicate = datePredicate + let entries = try! self.viewContext.count(for: fetchRequest) + + return entries == 0 + } +} + +private let itemFormatter: DateFormatter = { + let formatter = DateFormatter() + formatter.dateStyle = .short + formatter.timeStyle = .medium + return formatter +}() + +struct ContentView_Previews: PreviewProvider { + static var previews: some View { + ContentView().environment(\.managedObjectContext, PersistenceController.shared.container.viewContext) + .onAppear(perform: { + PersistenceController.shared.populateMemory() + }) + + ContentView() + .preferredColorScheme(.dark) + .environment(\.managedObjectContext, PersistenceController.shared.container.viewContext) + } +} diff --git a/Shared/views/GraphView.swift b/Shared/views/GraphView.swift new file mode 100644 index 0000000..894ef0e --- /dev/null +++ b/Shared/views/GraphView.swift @@ -0,0 +1,16 @@ +// +// GraphView.swift +// Feels +// +// Created by Trey Tartt on 1/8/22. +// + +import Foundation +import SwiftUI +import CoreData + +struct GraphView: View { + var body: some View { + Text("this is a graph") + } +} diff --git a/Shared/views/HeaderStatsView.swift b/Shared/views/HeaderStatsView.swift new file mode 100644 index 0000000..e449b0f --- /dev/null +++ b/Shared/views/HeaderStatsView.swift @@ -0,0 +1,82 @@ +// +// HeaderStatsView.swift +// Feels +// +// Created by Trey Tartt on 1/8/22. +// + +import SwiftUI +import Charts + +struct HeaderStatsView : UIViewRepresentable { + //Bar chart accepts data as array of BarChartDataEntry objects + var entries : [BarChartDataEntry] + // this func is required to conform to UIViewRepresentable protocol + func makeUIView(context: Context) -> BarChartView { + //crate new chart + let chart = BarChartView() + chart.drawGridBackgroundEnabled = false + chart.drawValueAboveBarEnabled = false + + chart.xAxis.drawAxisLineEnabled = false + chart.xAxis.labelTextColor = .clear + + chart.rightAxis.drawAxisLineEnabled = false + chart.rightAxis.labelTextColor = .clear + + chart.leftAxis.drawAxisLineEnabled = false + chart.leftAxis.labelTextColor = .clear + + chart.xAxis.drawGridLinesEnabled = false + chart.leftAxis.drawGridLinesEnabled = false + chart.leftAxis.axisLineColor = .clear + chart.rightAxis.axisLineColor = .clear + + chart.legend.textColor = .clear + chart.legend.enabled = false + + chart.drawBordersEnabled = false + chart.drawMarkers = false +// chart.yAxis.drawGridLinesEnabled = false + chart.rightAxis.drawGridLinesEnabled = false + chart.borderColor = .clear + //it is convenient to form chart data in a separate func + chart.data = addData() + return chart + } + + // this func is required to conform to UIViewRepresentable protocol + func updateUIView(_ uiView: BarChartView, context: Context) { + //when data changes chartd.data update is required + uiView.data = addData() + } + + func addData() -> BarChartData{ + let data = BarChartData() + //BarChartDataSet is an object that contains information about your data, styling and more + let dataSet = BarChartDataSet(entries: entries) + // change bars color to green + dataSet.colors = [NSUIColor.green] + //change data label + data.addDataSet(dataSet) + return data + } + + typealias UIViewType = BarChartView + +} + + + +struct HeaderStatsView_Previews: PreviewProvider { + static var previews: some View { + HeaderStatsView(entries: [ + //x - position of a bar, y - height of a bar + BarChartDataEntry(x: 1, y: 1), + BarChartDataEntry(x: 2, y: 4), + BarChartDataEntry(x: 3, y: 3), + BarChartDataEntry(x: 4, y: 2), + BarChartDataEntry(x: 5, y: 1) + ]).frame(minHeight: 85, maxHeight: 90) + } +} diff --git a/Shared/views/SettingsView.swift b/Shared/views/SettingsView.swift new file mode 100644 index 0000000..cf23c91 --- /dev/null +++ b/Shared/views/SettingsView.swift @@ -0,0 +1,142 @@ +// +// SettingsView.swift +// Feels +// +// Created by Trey Tartt on 1/8/22. +// + +import SwiftUI + +struct SettingsView: View { + @Environment(\.dismiss) var dismiss + + @State private var currentDate = Date() { + didSet { + if self.showReminder { + LocalNotification.scheduleReminder(atTime: self.currentDate) + } + } + } + + @State private var showReminder: Bool = false { + didSet { + if self.showReminder { + LocalNotification.testIfEnabled(completion: { result in + switch result{ + case .success(_): + LocalNotification.scheduleReminder(atTime: self.currentDate) + case .failure(_): + // show error + break + } + }) + } else { + LocalNotification.removeNotificaiton() + } + } + } + + var body: some View { ZStack { + Color(UIColor.secondarySystemBackground) + + VStack { + closeButtonView + .padding() + notificationCell + randomShitCell + addTestDataCell + clearDB + whyBackgroundMode + Spacer() + } + .padding() + } + } + + private var closeButtonView: some View { + HStack{ + Spacer() + Button(action: { + dismiss() + }, label: { + Text("Exit") + .font(.body) + .foregroundColor(Color(UIColor.systemBlue)) + }) + } + } + + private var notificationCell: some View { + ZStack { + Color(UIColor.systemBackground) + VStack { + Toggle("Would you like to be reminded?", isOn: $showReminder) + .padding() + DatePicker("", selection: $currentDate, displayedComponents: .hourAndMinute) + .disabled(showReminder == false) + .padding() + } + } + .fixedSize(horizontal: false, vertical: true) + .clipShape(RoundedRectangle(cornerRadius: 25, style: .continuous)) + } + + private var randomShitCell: some View { + ZStack { + Color(UIColor.systemBackground) + VStack { + Text("random shit") + .padding() + } + } + .fixedSize(horizontal: false, vertical: true) + .clipShape(RoundedRectangle(cornerRadius: 25, style: .continuous)) + } + + private var addTestDataCell: some View { + ZStack { + Color(UIColor.systemBackground) + Button(action: { + PersistenceController.shared.populateTestData() + }, label: { + Text("Add test data") + }) + .padding() + } + .fixedSize(horizontal: false, vertical: true) + .clipShape(RoundedRectangle(cornerRadius: 25, style: .continuous)) + } + + private var clearDB: some View { + ZStack { + Color(UIColor.systemBackground) + Button(action: { + PersistenceController.shared.clearDB() + }, label: { + Text("Clear DB") + }) + .padding() + } + .fixedSize(horizontal: false, vertical: true) + .clipShape(RoundedRectangle(cornerRadius: 25, style: .continuous)) + } + + private var whyBackgroundMode: some View { + ZStack { + Color(UIColor.systemBackground) + Text("we do bg mode b/c we can") + .padding() + } + .fixedSize(horizontal: false, vertical: true) + .clipShape(RoundedRectangle(cornerRadius: 25, style: .continuous)) + } +} + +struct SettingsView_Previews: PreviewProvider { + static var previews: some View { + SettingsView() + + SettingsView() + .preferredColorScheme(.dark) + } +}