init commit - bring project over from Mood

This commit is contained in:
Trey t
2022-01-10 09:04:38 -06:00
parent ff5d9bea18
commit 58697bf965
30 changed files with 1840 additions and 121 deletions

16
Feels (iOS).entitlements Normal file
View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.icloud-container-identifiers</key>
<array>
<string>iCloud.com.88oak.feels</string>
</array>
<key>com.apple.developer.icloud-services</key>
<array>
<string>CloudKit</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.icloud-container-identifiers</key>
<array>
<string>iCloud.com.88oak.feelsDev</string>
</array>
<key>com.apple.developer.icloud-services</key>
<array>
<string>CloudKit</string>
</array>
</dict>
</plist>

11
Feels--iOS--Info.plist Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>UIBackgroundModes</key>
<array>
<string>processing</string>
<string>remote-notification</string>
</array>
</dict>
</plist>

View File

@@ -15,12 +15,44 @@
1CD90B17278C7DE0001C4FEA /* Feels.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90AEB278C7DDF001C4FEA /* Feels.xcdatamodeld */; }; 1CD90B17278C7DE0001C4FEA /* Feels.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90AEB278C7DDF001C4FEA /* Feels.xcdatamodeld */; };
1CD90B18278C7DE0001C4FEA /* FeelsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90AED278C7DDF001C4FEA /* FeelsApp.swift */; }; 1CD90B18278C7DE0001C4FEA /* FeelsApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90AED278C7DDF001C4FEA /* FeelsApp.swift */; };
1CD90B19278C7DE0001C4FEA /* 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 */; }; 1CD90B1C278C7DE0001C4FEA /* Persistence.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1CD90AEF278C7DDF001C4FEA /* Persistence.swift */; };
1CD90B1D278C7DE0001C4FEA /* 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 */; }; 1CD90B1E278C7DE0001C4FEA /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 1CD90AF0278C7DE0001C4FEA /* Assets.xcassets */; };
1CD90B1F278C7DE0001C4FEA /* 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 */ /* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */ /* Begin PBXContainerItemProxy section */
@@ -38,12 +70,32 @@
remoteGlobalIDString = 1CD90AFA278C7DE0001C4FEA; remoteGlobalIDString = 1CD90AFA278C7DE0001C4FEA;
remoteInfo = "Feels (macOS)"; remoteInfo = "Feels (macOS)";
}; };
1CD90B54278C7E7A001C4FEA /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 1CD90AE6278C7DDF001C4FEA /* Project object */;
proxyType = 1;
remoteGlobalIDString = 1CD90B44278C7E7A001C4FEA;
remoteInfo = FeelsWidgetExtension;
};
/* End PBXContainerItemProxy section */ /* 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 */ /* Begin PBXFileReference section */
1CD90AEC278C7DDF001C4FEA /* Shared.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Shared.xcdatamodel; sourceTree = "<group>"; }; 1CD90AEC278C7DDF001C4FEA /* Shared.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Shared.xcdatamodel; sourceTree = "<group>"; };
1CD90AED278C7DDF001C4FEA /* FeelsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeelsApp.swift; sourceTree = "<group>"; }; 1CD90AED278C7DDF001C4FEA /* FeelsApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FeelsApp.swift; sourceTree = "<group>"; };
1CD90AEE278C7DDF001C4FEA /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
1CD90AEF278C7DDF001C4FEA /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; }; 1CD90AEF278C7DDF001C4FEA /* Persistence.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Persistence.swift; sourceTree = "<group>"; };
1CD90AF0278C7DE0001C4FEA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; }; 1CD90AF0278C7DE0001C4FEA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
1CD90AF5278C7DE0001C4FEA /* Feels.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = Feels.app; sourceTree = BUILT_PRODUCTS_DIR; }; 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; }; 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 = "<group>"; }; 1CD90B12278C7DE0001C4FEA /* Tests_macOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_macOS.swift; sourceTree = "<group>"; };
1CD90B14278C7DE0001C4FEA /* Tests_macOSLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_macOSLaunchTests.swift; sourceTree = "<group>"; }; 1CD90B14278C7DE0001C4FEA /* Tests_macOSLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_macOSLaunchTests.swift; sourceTree = "<group>"; };
1CD90B32278C7E38001C4FEA /* SettingsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
1CD90B33278C7E38001C4FEA /* GraphView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = GraphView.swift; sourceTree = "<group>"; };
1CD90B34278C7E38001C4FEA /* AddMoodHeaderView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = AddMoodHeaderView.swift; sourceTree = "<group>"; };
1CD90B35278C7E38001C4FEA /* ContentView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
1CD90B36278C7E38001C4FEA /* HeaderStatsView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = HeaderStatsView.swift; sourceTree = "<group>"; };
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 = "<group>"; };
1CD90B4E278C7E7A001C4FEA /* FeelsWidget.intentdefinition */ = {isa = PBXFileReference; lastKnownFileType = file.intentdefinition; path = FeelsWidget.intentdefinition; sourceTree = "<group>"; };
1CD90B4F278C7E7A001C4FEA /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
1CD90B51278C7E7A001C4FEA /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
1CD90B5C278C7EAD001C4FEA /* Random.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Random.swift; sourceTree = "<group>"; };
1CD90B61278C7EBA001C4FEA /* Mood.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = Mood.swift; sourceTree = "<group>"; };
1CD90B62278C7EBA001C4FEA /* MoodEntryExtension.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = MoodEntryExtension.swift; sourceTree = "<group>"; };
1CD90B69278C7F65001C4FEA /* Feels--iOS--Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist; path = "Feels--iOS--Info.plist"; sourceTree = "<group>"; };
1CD90B6A278C7F75001C4FEA /* Feels (iOS).entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "Feels (iOS).entitlements"; sourceTree = "<group>"; };
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 = "<group>"; };
1CD90B6F278C8000001C4FEA /* FeelsWidgetExtensionDev.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = FeelsWidgetExtensionDev.entitlements; sourceTree = "<group>"; };
1CD90B70278C8000001C4FEA /* Feels (iOS)Dev.entitlements */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.plist.entitlements; path = "Feels (iOS)Dev.entitlements"; sourceTree = "<group>"; };
1CD90B75278C8119001C4FEA /* LocalNotification.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LocalNotification.swift; sourceTree = "<group>"; };
/* End PBXFileReference section */ /* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */ /* Begin PBXFrameworksBuildPhase section */
@@ -62,6 +136,8 @@
isa = PBXFrameworksBuildPhase; isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
1CD90B6C278C7F78001C4FEA /* CloudKit.framework in Frameworks */,
1CD90B7B278C8146001C4FEA /* Charts in Frameworks */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -86,16 +162,33 @@
); );
runOnlyForDeploymentPostprocessing = 0; 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 */ /* End PBXFrameworksBuildPhase section */
/* Begin PBXGroup section */ /* Begin PBXGroup section */
1CD90AE5278C7DDF001C4FEA = { 1CD90AE5278C7DDF001C4FEA = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
1CD90B6A278C7F75001C4FEA /* Feels (iOS).entitlements */,
1CD90B70278C8000001C4FEA /* Feels (iOS)Dev.entitlements */,
1CD90B6D278C7F89001C4FEA /* FeelsWidgetExtension.entitlements */,
1CD90B6F278C8000001C4FEA /* FeelsWidgetExtensionDev.entitlements */,
1CD90B69278C7F65001C4FEA /* Feels--iOS--Info.plist */,
1CD90AEA278C7DDF001C4FEA /* Shared */, 1CD90AEA278C7DDF001C4FEA /* Shared */,
1CD90B4B278C7E7A001C4FEA /* FeelsWidget */,
1CD90AFC278C7DE0001C4FEA /* macOS */, 1CD90AFC278C7DE0001C4FEA /* macOS */,
1CD90B05278C7DE0001C4FEA /* Tests iOS */, 1CD90B05278C7DE0001C4FEA /* Tests iOS */,
1CD90B11278C7DE0001C4FEA /* Tests macOS */, 1CD90B11278C7DE0001C4FEA /* Tests macOS */,
1CD90B46278C7E7A001C4FEA /* Frameworks */,
1CD90AF6278C7DE0001C4FEA /* Products */, 1CD90AF6278C7DE0001C4FEA /* Products */,
); );
sourceTree = "<group>"; sourceTree = "<group>";
@@ -103,8 +196,11 @@
1CD90AEA278C7DDF001C4FEA /* Shared */ = { 1CD90AEA278C7DDF001C4FEA /* Shared */ = {
isa = PBXGroup; isa = PBXGroup;
children = ( children = (
1CD90B75278C8119001C4FEA /* LocalNotification.swift */,
1CD90B31278C7E38001C4FEA /* views */,
1CD90B60278C7EBA001C4FEA /* Models */,
1CD90B5C278C7EAD001C4FEA /* Random.swift */,
1CD90AED278C7DDF001C4FEA /* FeelsApp.swift */, 1CD90AED278C7DDF001C4FEA /* FeelsApp.swift */,
1CD90AEE278C7DDF001C4FEA /* ContentView.swift */,
1CD90AEF278C7DDF001C4FEA /* Persistence.swift */, 1CD90AEF278C7DDF001C4FEA /* Persistence.swift */,
1CD90AF0278C7DE0001C4FEA /* Assets.xcassets */, 1CD90AF0278C7DE0001C4FEA /* Assets.xcassets */,
1CD90AEB278C7DDF001C4FEA /* Feels.xcdatamodeld */, 1CD90AEB278C7DDF001C4FEA /* Feels.xcdatamodeld */,
@@ -119,6 +215,7 @@
1CD90AFB278C7DE0001C4FEA /* Feels.app */, 1CD90AFB278C7DE0001C4FEA /* Feels.app */,
1CD90B02278C7DE0001C4FEA /* Tests iOS.xctest */, 1CD90B02278C7DE0001C4FEA /* Tests iOS.xctest */,
1CD90B0E278C7DE0001C4FEA /* Tests macOS.xctest */, 1CD90B0E278C7DE0001C4FEA /* Tests macOS.xctest */,
1CD90B45278C7E7A001C4FEA /* FeelsWidgetExtension.appex */,
); );
name = Products; name = Products;
sourceTree = "<group>"; sourceTree = "<group>";
@@ -149,6 +246,48 @@
path = "Tests macOS"; path = "Tests macOS";
sourceTree = "<group>"; sourceTree = "<group>";
}; };
1CD90B31278C7E38001C4FEA /* views */ = {
isa = PBXGroup;
children = (
1CD90B32278C7E38001C4FEA /* SettingsView.swift */,
1CD90B33278C7E38001C4FEA /* GraphView.swift */,
1CD90B34278C7E38001C4FEA /* AddMoodHeaderView.swift */,
1CD90B35278C7E38001C4FEA /* ContentView.swift */,
1CD90B36278C7E38001C4FEA /* HeaderStatsView.swift */,
);
path = views;
sourceTree = "<group>";
};
1CD90B46278C7E7A001C4FEA /* Frameworks */ = {
isa = PBXGroup;
children = (
1CD90B6B278C7F78001C4FEA /* CloudKit.framework */,
1CD90B47278C7E7A001C4FEA /* WidgetKit.framework */,
1CD90B49278C7E7A001C4FEA /* SwiftUI.framework */,
);
name = Frameworks;
sourceTree = "<group>";
};
1CD90B4B278C7E7A001C4FEA /* FeelsWidget */ = {
isa = PBXGroup;
children = (
1CD90B4C278C7E7A001C4FEA /* FeelsWidget.swift */,
1CD90B4E278C7E7A001C4FEA /* FeelsWidget.intentdefinition */,
1CD90B4F278C7E7A001C4FEA /* Assets.xcassets */,
1CD90B51278C7E7A001C4FEA /* Info.plist */,
);
path = FeelsWidget;
sourceTree = "<group>";
};
1CD90B60278C7EBA001C4FEA /* Models */ = {
isa = PBXGroup;
children = (
1CD90B61278C7EBA001C4FEA /* Mood.swift */,
1CD90B62278C7EBA001C4FEA /* MoodEntryExtension.swift */,
);
path = Models;
sourceTree = "<group>";
};
/* End PBXGroup section */ /* End PBXGroup section */
/* Begin PBXNativeTarget section */ /* Begin PBXNativeTarget section */
@@ -159,12 +298,17 @@
1CD90AF1278C7DE0001C4FEA /* Sources */, 1CD90AF1278C7DE0001C4FEA /* Sources */,
1CD90AF2278C7DE0001C4FEA /* Frameworks */, 1CD90AF2278C7DE0001C4FEA /* Frameworks */,
1CD90AF3278C7DE0001C4FEA /* Resources */, 1CD90AF3278C7DE0001C4FEA /* Resources */,
1CD90B5A278C7E7A001C4FEA /* Embed App Extensions */,
); );
buildRules = ( buildRules = (
); );
dependencies = ( dependencies = (
1CD90B55278C7E7A001C4FEA /* PBXTargetDependency */,
); );
name = "Feels (iOS)"; name = "Feels (iOS)";
packageProductDependencies = (
1CD90B7A278C8146001C4FEA /* Charts */,
);
productName = "Feels (iOS)"; productName = "Feels (iOS)";
productReference = 1CD90AF5278C7DE0001C4FEA /* Feels.app */; productReference = 1CD90AF5278C7DE0001C4FEA /* Feels.app */;
productType = "com.apple.product-type.application"; productType = "com.apple.product-type.application";
@@ -222,6 +366,23 @@
productReference = 1CD90B0E278C7DE0001C4FEA /* Tests macOS.xctest */; productReference = 1CD90B0E278C7DE0001C4FEA /* Tests macOS.xctest */;
productType = "com.apple.product-type.bundle.ui-testing"; 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 */ /* End PBXNativeTarget section */
/* Begin PBXProject section */ /* Begin PBXProject section */
@@ -246,6 +407,9 @@
CreatedOnToolsVersion = 13.2.1; CreatedOnToolsVersion = 13.2.1;
TestTargetID = 1CD90AFA278C7DE0001C4FEA; TestTargetID = 1CD90AFA278C7DE0001C4FEA;
}; };
1CD90B44278C7E7A001C4FEA = {
CreatedOnToolsVersion = 13.2.1;
};
}; };
}; };
buildConfigurationList = 1CD90AE9278C7DDF001C4FEA /* Build configuration list for PBXProject "Feels" */; buildConfigurationList = 1CD90AE9278C7DDF001C4FEA /* Build configuration list for PBXProject "Feels" */;
@@ -257,6 +421,9 @@
Base, Base,
); );
mainGroup = 1CD90AE5278C7DDF001C4FEA; mainGroup = 1CD90AE5278C7DDF001C4FEA;
packageReferences = (
1CD90B79278C8146001C4FEA /* XCRemoteSwiftPackageReference "Charts" */,
);
productRefGroup = 1CD90AF6278C7DE0001C4FEA /* Products */; productRefGroup = 1CD90AF6278C7DE0001C4FEA /* Products */;
projectDirPath = ""; projectDirPath = "";
projectRoot = ""; projectRoot = "";
@@ -265,6 +432,7 @@
1CD90AFA278C7DE0001C4FEA /* Feels (macOS) */, 1CD90AFA278C7DE0001C4FEA /* Feels (macOS) */,
1CD90B01278C7DE0001C4FEA /* Tests iOS */, 1CD90B01278C7DE0001C4FEA /* Tests iOS */,
1CD90B0D278C7DE0001C4FEA /* Tests macOS */, 1CD90B0D278C7DE0001C4FEA /* Tests macOS */,
1CD90B44278C7E7A001C4FEA /* FeelsWidgetExtension */,
); );
}; };
/* End PBXProject section */ /* End PBXProject section */
@@ -300,6 +468,14 @@
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
1CD90B43278C7E7A001C4FEA /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
1CD90B50278C7E7A001C4FEA /* Assets.xcassets in Resources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */ /* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */ /* Begin PBXSourcesBuildPhase section */
@@ -307,10 +483,19 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
1CD90B39278C7E38001C4FEA /* GraphView.swift in Sources */,
1CD90B76278C8119001C4FEA /* LocalNotification.swift in Sources */,
1CD90B16278C7DE0001C4FEA /* Feels.xcdatamodeld 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 */, 1CD90B1C278C7DE0001C4FEA /* Persistence.swift in Sources */,
1CD90B18278C7DE0001C4FEA /* FeelsApp.swift in Sources */, 1CD90B18278C7DE0001C4FEA /* FeelsApp.swift in Sources */,
1CD90B1A278C7DE0001C4FEA /* ContentView.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -318,10 +503,18 @@
isa = PBXSourcesBuildPhase; isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647; buildActionMask = 2147483647;
files = ( files = (
1CD90B64278C7EBA001C4FEA /* Mood.swift in Sources */,
1CD90B3A278C7E38001C4FEA /* GraphView.swift in Sources */,
1CD90B17278C7DE0001C4FEA /* Feels.xcdatamodeld 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 */, 1CD90B1D278C7DE0001C4FEA /* Persistence.swift in Sources */,
1CD90B19278C7DE0001C4FEA /* FeelsApp.swift in Sources */, 1CD90B19278C7DE0001C4FEA /* FeelsApp.swift in Sources */,
1CD90B1B278C7DE0001C4FEA /* ContentView.swift in Sources */, 1CD90B5E278C7EAD001C4FEA /* Random.swift in Sources */,
); );
runOnlyForDeploymentPostprocessing = 0; runOnlyForDeploymentPostprocessing = 0;
}; };
@@ -343,6 +536,21 @@
); );
runOnlyForDeploymentPostprocessing = 0; 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 */ /* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */ /* Begin PBXTargetDependency section */
@@ -356,6 +564,11 @@
target = 1CD90AFA278C7DE0001C4FEA /* Feels (macOS) */; target = 1CD90AFA278C7DE0001C4FEA /* Feels (macOS) */;
targetProxy = 1CD90B0F278C7DE0001C4FEA /* PBXContainerItemProxy */; targetProxy = 1CD90B0F278C7DE0001C4FEA /* PBXContainerItemProxy */;
}; };
1CD90B55278C7E7A001C4FEA /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 1CD90B44278C7E7A001C4FEA /* FeelsWidgetExtension */;
targetProxy = 1CD90B54278C7E7A001C4FEA /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */ /* End PBXTargetDependency section */
/* Begin XCBuildConfiguration section */ /* Begin XCBuildConfiguration section */
@@ -473,13 +686,16 @@
1CD90B23278C7DE0001C4FEA /* Debug */ = { 1CD90B23278C7DE0001C4FEA /* Debug */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "Feels (iOS)Dev.entitlements";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = V3PF3M6B6U; DEVELOPMENT_TEAM = V3PF3M6B6U;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "Feels--iOS--Info.plist";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES;
@@ -491,7 +707,7 @@
"@executable_path/Frameworks", "@executable_path/Frameworks",
); );
MARKETING_VERSION = 1.0; MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = com.88oak.Feels; PRODUCT_BUNDLE_IDENTIFIER = com.88oak.FeelsDev;
PRODUCT_NAME = Feels; PRODUCT_NAME = Feels;
SDKROOT = iphoneos; SDKROOT = iphoneos;
SWIFT_EMIT_LOC_STRINGS = YES; SWIFT_EMIT_LOC_STRINGS = YES;
@@ -503,13 +719,16 @@
1CD90B24278C7DE0001C4FEA /* Release */ = { 1CD90B24278C7DE0001C4FEA /* Release */ = {
isa = XCBuildConfiguration; isa = XCBuildConfiguration;
buildSettings = { buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon; ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor; ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
CODE_SIGN_ENTITLEMENTS = "Feels (iOS).entitlements";
CODE_SIGN_STYLE = Automatic; CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1; CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = V3PF3M6B6U; DEVELOPMENT_TEAM = V3PF3M6B6U;
ENABLE_PREVIEWS = YES; ENABLE_PREVIEWS = YES;
GENERATE_INFOPLIST_FILE = YES; GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "Feels--iOS--Info.plist";
INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES; INFOPLIST_KEY_UIApplicationSceneManifest_Generation = YES;
INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES; INFOPLIST_KEY_UIApplicationSupportsIndirectInputEvents = YES;
INFOPLIST_KEY_UILaunchScreen_Generation = YES; INFOPLIST_KEY_UILaunchScreen_Generation = YES;
@@ -666,6 +885,67 @@
}; };
name = Release; 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 */ /* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */ /* Begin XCConfigurationList section */
@@ -714,8 +994,36 @@
defaultConfigurationIsVisible = 0; defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release; defaultConfigurationName = Release;
}; };
1CD90B57278C7E7A001C4FEA /* Build configuration list for PBXNativeTarget "FeelsWidgetExtension" */ = {
isa = XCConfigurationList;
buildConfigurations = (
1CD90B58278C7E7A001C4FEA /* Debug */,
1CD90B59278C7E7A001C4FEA /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */ /* 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 */ /* Begin XCVersionGroup section */
1CD90AEB278C7DDF001C4FEA /* Feels.xcdatamodeld */ = { 1CD90AEB278C7DDF001C4FEA /* Feels.xcdatamodeld */ = {
isa = XCVersionGroup; isa = XCVersionGroup;

View File

@@ -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
}

View File

@@ -14,6 +14,53 @@
<key>orderHint</key> <key>orderHint</key>
<integer>1</integer> <integer>1</integer>
</dict> </dict>
<key>FeelsWidgetExtension.xcscheme_^#shared#^_</key>
<dict>
<key>orderHint</key>
<integer>2</integer>
</dict>
<key>PlaygroundChart (Playground) 1.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>4</integer>
</dict>
<key>PlaygroundChart (Playground) 2.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>5</integer>
</dict>
<key>PlaygroundChart (Playground) 3.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>6</integer>
</dict>
<key>PlaygroundChart (Playground) 4.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>7</integer>
</dict>
<key>PlaygroundChart (Playground) 5.xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>8</integer>
</dict>
<key>PlaygroundChart (Playground).xcscheme</key>
<dict>
<key>isShown</key>
<false/>
<key>orderHint</key>
<integer>3</integer>
</dict>
</dict> </dict>
</dict> </dict>
</plist> </plist>

View File

@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -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
}
}

View File

@@ -0,0 +1,6 @@
{
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,11 @@
{
"colors" : [
{
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@@ -0,0 +1,59 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>INEnums</key>
<array/>
<key>INIntentDefinitionModelVersion</key>
<string>1.2</string>
<key>INIntentDefinitionNamespace</key>
<string>88xZPY</string>
<key>INIntentDefinitionSystemVersion</key>
<string>20A294</string>
<key>INIntentDefinitionToolsBuildVersion</key>
<string>12A6144</string>
<key>INIntentDefinitionToolsVersion</key>
<string>12.0</string>
<key>INIntents</key>
<array>
<dict>
<key>INIntentCategory</key>
<string>information</string>
<key>INIntentDescriptionID</key>
<string>tVvJ9c</string>
<key>INIntentEligibleForWidgets</key>
<true/>
<key>INIntentIneligibleForSuggestions</key>
<true/>
<key>INIntentName</key>
<string>Configuration</string>
<key>INIntentResponse</key>
<dict>
<key>INIntentResponseCodes</key>
<array>
<dict>
<key>INIntentResponseCodeName</key>
<string>success</string>
<key>INIntentResponseCodeSuccess</key>
<true/>
</dict>
<dict>
<key>INIntentResponseCodeName</key>
<string>failure</string>
</dict>
</array>
</dict>
<key>INIntentTitle</key>
<string>Configuration</string>
<key>INIntentTitleID</key>
<string>gpCwrM</string>
<key>INIntentType</key>
<string>Custom</string>
<key>INIntentVerb</key>
<string>View</string>
</dict>
</array>
<key>INTypes</key>
<array/>
</dict>
</plist>

View File

@@ -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<Entry>) -> ()) {
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)
}
}
}

11
FeelsWidget/Info.plist Normal file
View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.widgetkit-extension</string>
</dict>
</dict>
</plist>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.icloud-container-identifiers</key>
<array>
<string>iCloud.com.88oak.feels</string>
</array>
<key>com.apple.developer.icloud-services</key>
<array>
<string>CloudKit</string>
</array>
</dict>
</plist>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>aps-environment</key>
<string>development</string>
<key>com.apple.developer.icloud-container-identifiers</key>
<array>
<string>iCloud.com.88oak.feelsDev</string>
</array>
<key>com.apple.developer.icloud-services</key>
<array>
<string>CloudKit</string>
</array>
</dict>
</plist>

View File

@@ -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<Item>
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)
}
}

View File

@@ -1,9 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?> <?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="1" systemVersion="11A491" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="true" userDefinedModelVersionIdentifier=""> <model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="21C52" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier="">
<entity name="Item" representedClassName="Item" syncable="YES" codeGenerationType="class"> <entity name="MoodEntry" representedClassName="MoodEntry" syncable="YES" codeGenerationType="class">
<attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="moodValue" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="timestamp" optional="YES" attributeType="Date" usesScalarValueType="NO"/> <attribute name="timestamp" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
</entity> </entity>
<elements> <elements>
<element name="Item" positionX="-63" positionY="-18" width="128" height="44"/> <element name="MoodEntry" positionX="-63" positionY="-18" width="128" height="74"/>
</elements> </elements>
</model> </model>

View File

@@ -2,19 +2,67 @@
// FeelsApp.swift // FeelsApp.swift
// Shared // Shared
// //
// Created by Trey Tartt on 1/10/22. // Created by Trey Tartt on 1/5/22.
// //
import SwiftUI import SwiftUI
import BackgroundTasks
@main @main
struct FeelsApp: App { struct FeelsApp: App {
@Environment(\.scenePhase) private var scenePhase
let persistenceController = PersistenceController.shared 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 { var body: some Scene {
WindowGroup { WindowGroup {
ContentView() ContentView()
.environment(\.managedObjectContext, persistenceController.container.viewContext) .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)")
} }
} }
} }

View File

@@ -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<Bool, Error>) -> 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()
}
}

63
Shared/Models/Mood.swift Normal file
View File

@@ -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
}
}

View File

@@ -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))!
}
}

View File

@@ -0,0 +1,8 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>_XCCurrentVersionName</key>
<string>Shared.xcdatamodel</string>
</dict>
</plist>

View File

@@ -0,0 +1,11 @@
<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
<model type="com.apple.IDECoreDataModeler.DataModel" documentVersion="1.0" lastSavedToolsVersion="19574" systemVersion="21C52" minimumToolsVersion="Automatic" sourceLanguage="Swift" usedWithCloudKit="YES" userDefinedModelVersionIdentifier="">
<entity name="MoodEntry" representedClassName="MoodEntry" syncable="YES" codeGenerationType="class">
<attribute name="date" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
<attribute name="moodValue" optional="YES" attributeType="Integer 16" defaultValueString="0" usesScalarValueType="YES"/>
<attribute name="timestamp" optional="YES" attributeType="Date" usesScalarValueType="NO"/>
</entity>
<elements>
<element name="MoodEntry" positionX="-63" positionY="-18" width="128" height="74"/>
</elements>
</model>

View File

@@ -2,20 +2,71 @@
// Persistence.swift // Persistence.swift
// Shared // Shared
// //
// Created by Trey Tartt on 1/10/22. // Created by Trey Tartt on 1/5/22.
// //
import CoreData import CoreData
struct PersistenceController { struct PersistenceController {
static let shared = PersistenceController() static let shared = PersistenceController.persistenceController
static var preview: PersistenceController = { private static var persistenceController: PersistenceController {
let result = PersistenceController(inMemory: true) #if targetEnvironment(simulator)
let viewContext = result.container.viewContext return PersistenceController(inMemory: false)
for _ in 0..<10 { #else
let newItem = Item(context: viewContext) return PersistenceController(inMemory: false)
#endif
}
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<MoodEntry>(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.timestamp = Date()
newItem.moodValue = Int16(Mood.allValues.randomElement()!.rawValue)
newItem.date = Calendar.current.date(byAdding: .day, value: -idx, to: Date())
} }
do { do {
try viewContext.save() try viewContext.save()
@@ -25,8 +76,66 @@ struct PersistenceController {
let nsError = error as NSError let nsError = error as NSError
fatalError("Unresolved error \(nsError), \(nsError.userInfo)") 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<MoodEntry>(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..<diffInDays! {
let searchDay = Calendar.current.date(byAdding: .day, value: -idx, to: Date())
if entries.filter({ Calendar.current.isDate($0.date!, inSameDayAs:searchDay!) }).isEmpty {
self.add(mood: .missing, forDate: searchDay!)
}
}
}
}
func clearDB() {
let fetchRequest: NSFetchRequest<NSFetchRequestResult> = 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..<count {
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())
entries.append(newItem)
}
return entries
}
let container: NSPersistentCloudKitContainer let container: NSPersistentCloudKitContainer
@@ -42,15 +151,29 @@ struct PersistenceController {
// 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. // 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.
/* /*
Typical reasons for an error here include: Typical reasons for an error here include:
* The parent directory does not exist, cannot be created, or disallows writing. * The parent directory does not exist, cannot be created, or disallows writing.
* The persistent store is not accessible, due to permissions or data protection when the device is locked. * The persistent store is not accessible, due to permissions or data protection when the device is locked.
* The device is out of space. * The device is out of space.
* The store could not be migrated to the current model version. * The store could not be migrated to the current model version.
Check the error message to determine what the actual problem was. Check the error message to determine what the actual problem was.
*/ */
fatalError("Unresolved error \(error), \(error.userInfo)") fatalError("Unresolved error \(error), \(error.userInfo)")
} }
}) })
} }
} }
extension NSManagedObjectContext {
/// Executes the given `NSBatchDeleteRequest` and directly merges the changes to bring the given managed object context up to date.
///
/// - Parameter batchDeleteRequest: The `NSBatchDeleteRequest` to execute.
/// - Throws: An error if anything went wrong executing the batch deletion.
public func executeAndMergeChanges(using batchDeleteRequest: NSBatchDeleteRequest) throws {
batchDeleteRequest.resultType = .resultTypeObjectIDs
let result = try execute(batchDeleteRequest) as? NSBatchDeleteResult
let changes: [AnyHashable: Any] = [NSDeletedObjectsKey: result?.result as? [NSManagedObjectID] ?? []]
NSManagedObjectContext.mergeChanges(fromRemoteContextSave: changes, into: [self])
}
}

20
Shared/Random.swift Normal file
View File

@@ -0,0 +1,20 @@
//
// Random.swift
// Feels
//
// Created by Trey Tartt on 1/9/22.
//
import Foundation
class Random {
static var widgetUpdateTime: Date {
let components = DateComponents(hour: 0, minute: 30, second: 0)
var updateTime = Date()
if let tomorrow = Calendar.current.date(byAdding: .day, value: 1, to: Date()),
let tomorrowMorning = Calendar.current.date(byAdding: components, to: tomorrow) {
updateTime = tomorrowMorning
}
return updateTime
}
}

View File

@@ -0,0 +1,79 @@
//
// AddMoodHeaderView.swift
// Feels
//
// Created by Trey Tartt on 1/5/22.
//
import Foundation
import SwiftUI
import CoreData
struct AddMoodHeaderView: View {
@Environment(\.managedObjectContext) private var viewContext
var body: some View {
ZStack {
Color(UIColor.secondarySystemBackground)
VStack {
Text("How are you feeling today?")
.font(.title)
.foregroundColor(Color(UIColor.label))
.padding()
HStack{
ForEach(Mood.allValues) { mood in
VStack {
Button(action: {
addItem(withMoodValue: mood.rawValue)
}, label: {
mood.icon
.font(.system(size: 50))
})
//Text(mood.strValue)
}.frame(minWidth: 0, maxWidth: .infinity)
}
}
.padding([.leading, .trailing, .bottom])
}
}
.clipShape(RoundedRectangle(cornerRadius: 25, style: .continuous))
.frame(minHeight: 85, maxHeight: 140)
.frame(minWidth: 0, maxWidth: .infinity)
.padding()
}
private func addItem(withMoodValue moodValue: Int) {
withAnimation {
let newItem = MoodEntry(context: viewContext)
newItem.timestamp = Date()
newItem.moodValue = Int16(moodValue)
newItem.date = 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)")
}
}
}
}
struct AddMoodHeaderView_Previews: PreviewProvider {
static var previews: some View {
Group {
AddMoodHeaderView().environment(\.managedObjectContext, PersistenceController.shared.container.viewContext)
AddMoodHeaderView().preferredColorScheme(.dark).environment(\.managedObjectContext, PersistenceController.shared.container.viewContext)
AddMoodHeaderView().environment(\.managedObjectContext, PersistenceController.shared.container.viewContext)
AddMoodHeaderView().preferredColorScheme(.dark).environment(\.managedObjectContext, PersistenceController.shared.container.viewContext)
}
}
}

View File

@@ -0,0 +1,152 @@
//
// ContentView.swift
// Shared
//
// Created by Trey Tartt on 1/5/22.
//
import SwiftUI
import CoreData
import Charts
struct ContentView: View {
@Environment(\.managedObjectContext) private var viewContext
@State private var showingSheet = false
@State private var showTodayInput = true
@FetchRequest(
sortDescriptors: [NSSortDescriptor(keyPath: \MoodEntry.date, ascending: false)],
animation: .spring())
private var items: FetchedResults<MoodEntry>
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<MoodEntry>(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)
}
}

View File

@@ -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")
}
}

View File

@@ -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)
}
}

View File

@@ -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)
}
}