init commit - bring project over from Mood
This commit is contained in:
16
Feels (iOS).entitlements
Normal file
16
Feels (iOS).entitlements
Normal 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>
|
||||
16
Feels (iOS)Dev.entitlements
Normal file
16
Feels (iOS)Dev.entitlements
Normal 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
11
Feels--iOS--Info.plist
Normal 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>
|
||||
@@ -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 = "<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>"; };
|
||||
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; };
|
||||
@@ -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 = "<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 */
|
||||
|
||||
/* 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 = "<group>";
|
||||
@@ -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 = "<group>";
|
||||
@@ -149,6 +246,48 @@
|
||||
path = "Tests macOS";
|
||||
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 */
|
||||
|
||||
/* 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;
|
||||
|
||||
@@ -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
|
||||
}
|
||||
@@ -14,6 +14,53 @@
|
||||
<key>orderHint</key>
|
||||
<integer>1</integer>
|
||||
</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>
|
||||
</plist>
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
98
FeelsWidget/Assets.xcassets/AppIcon.appiconset/Contents.json
Normal file
98
FeelsWidget/Assets.xcassets/AppIcon.appiconset/Contents.json
Normal 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
|
||||
}
|
||||
}
|
||||
6
FeelsWidget/Assets.xcassets/Contents.json
Normal file
6
FeelsWidget/Assets.xcassets/Contents.json
Normal file
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
59
FeelsWidget/FeelsWidget.intentdefinition
Normal file
59
FeelsWidget/FeelsWidget.intentdefinition
Normal 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>
|
||||
259
FeelsWidget/FeelsWidget.swift
Normal file
259
FeelsWidget/FeelsWidget.swift
Normal 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
11
FeelsWidget/Info.plist
Normal 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>
|
||||
16
FeelsWidgetExtension.entitlements
Normal file
16
FeelsWidgetExtension.entitlements
Normal 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>
|
||||
16
FeelsWidgetExtensionDev.entitlements
Normal file
16
FeelsWidgetExtensionDev.entitlements
Normal 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>
|
||||
@@ -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)
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,11 @@
|
||||
<?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="">
|
||||
<entity name="Item" representedClassName="Item" syncable="YES" codeGenerationType="class">
|
||||
<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="Item" positionX="-63" positionY="-18" width="128" height="44"/>
|
||||
<element name="MoodEntry" positionX="-63" positionY="-18" width="128" height="74"/>
|
||||
</elements>
|
||||
</model>
|
||||
@@ -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)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
143
Shared/LocalNotification.swift
Normal file
143
Shared/LocalNotification.swift
Normal 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
63
Shared/Models/Mood.swift
Normal 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
|
||||
}
|
||||
}
|
||||
19
Shared/Models/MoodEntryExtension.swift
Normal file
19
Shared/Models/MoodEntryExtension.swift
Normal 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))!
|
||||
}
|
||||
}
|
||||
8
Shared/Mooood.xcdatamodeld/.xccurrentversion
Normal file
8
Shared/Mooood.xcdatamodeld/.xccurrentversion
Normal 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>
|
||||
11
Shared/Mooood.xcdatamodeld/Shared.xcdatamodel/contents
Normal file
11
Shared/Mooood.xcdatamodeld/Shared.xcdatamodel/contents
Normal 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>
|
||||
@@ -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<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.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<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
|
||||
|
||||
|
||||
init(inMemory: Bool = false) {
|
||||
container = NSPersistentCloudKitContainer(name: "Feels")
|
||||
if inMemory {
|
||||
@@ -40,17 +149,31 @@ struct PersistenceController {
|
||||
if let error = error as NSError? {
|
||||
// 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.
|
||||
|
||||
|
||||
/*
|
||||
Typical reasons for an error here include:
|
||||
* 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 device is out of space.
|
||||
* The store could not be migrated to the current model version.
|
||||
Check the error message to determine what the actual problem was.
|
||||
*/
|
||||
Typical reasons for an error here include:
|
||||
* 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 device is out of space.
|
||||
* The store could not be migrated to the current model version.
|
||||
Check the error message to determine what the actual problem was.
|
||||
*/
|
||||
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
20
Shared/Random.swift
Normal 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
|
||||
}
|
||||
}
|
||||
79
Shared/views/AddMoodHeaderView.swift
Normal file
79
Shared/views/AddMoodHeaderView.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
}
|
||||
152
Shared/views/ContentView.swift
Normal file
152
Shared/views/ContentView.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
16
Shared/views/GraphView.swift
Normal file
16
Shared/views/GraphView.swift
Normal 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")
|
||||
}
|
||||
}
|
||||
82
Shared/views/HeaderStatsView.swift
Normal file
82
Shared/views/HeaderStatsView.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
142
Shared/views/SettingsView.swift
Normal file
142
Shared/views/SettingsView.swift
Normal 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)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user