├── Screenshots
├── Icon.png
├── Delete-Dark.png
├── AddCourse-Dark.png
├── ColorCode-Dark.png
├── GridView-Dark.png
├── GridView-Light.png
├── AddCourse-Light.png
├── HomeScreen-Dark.png
├── HomeScreen-Light.png
├── ScrollView-Dark.png
├── ScrollView-Light.png
├── AppleCalendar-Dark.png
├── AppleCalendar-Light.png
├── Notifications-Dark.png
└── Notifications-Light.png
├── TimeMan
├── Assets.xcassets
│ ├── Contents.json
│ ├── ColorCodes
│ │ ├── Contents.json
│ │ ├── Beaming Blue
│ │ │ ├── Contents.json
│ │ │ ├── BeamingBluePrimary.colorset
│ │ │ │ └── Contents.json
│ │ │ └── BeamingBlueSecondary.colorset
│ │ │ │ └── Contents.json
│ │ ├── Gleamy Pink
│ │ │ ├── Contents.json
│ │ │ ├── GleamyPinkPrimary.colorset
│ │ │ │ └── Contents.json
│ │ │ └── GleamyPinkSecondary.colorset
│ │ │ │ └── Contents.json
│ │ ├── Placid Green
│ │ │ ├── Contents.json
│ │ │ ├── PlacidGreenPrimary.colorset
│ │ │ │ └── Contents.json
│ │ │ └── PlacidGreenSecondary.colorset
│ │ │ │ └── Contents.json
│ │ ├── Purple Punch
│ │ │ ├── Contents.json
│ │ │ ├── PurplePunchPrimary.colorset
│ │ │ │ └── Contents.json
│ │ │ └── PurplePunchSecondary.colorset
│ │ │ │ └── Contents.json
│ │ ├── Tangy Orange
│ │ │ ├── Contents.json
│ │ │ ├── TangyOrangePrimary.colorset
│ │ │ │ └── Contents.json
│ │ │ └── TangyOrangeSecondary.colorset
│ │ │ │ └── Contents.json
│ │ ├── Trendy Teal
│ │ │ ├── Contents.json
│ │ │ ├── TrendyTealPrimary.colorset
│ │ │ │ └── Contents.json
│ │ │ └── TrendyTealSecondary.colorset
│ │ │ │ └── Contents.json
│ │ ├── Vanilla Ice
│ │ │ ├── Contents.json
│ │ │ ├── VanillaIcePrimary.colorset
│ │ │ │ └── Contents.json
│ │ │ └── VanillaIceSecondary.colorset
│ │ │ │ └── Contents.json
│ │ └── Bright Yellow
│ │ │ ├── Contents.json
│ │ │ ├── BrightYellowPrimary.colorset
│ │ │ └── Contents.json
│ │ │ └── BrightYellowSecondary.colorset
│ │ │ └── Contents.json
│ ├── Logo.imageset
│ │ ├── Icon.png
│ │ └── Contents.json
│ ├── splash.imageset
│ │ ├── 256.png
│ │ └── Contents.json
│ ├── AppIcon.appiconset
│ │ ├── 114.png
│ │ ├── 57.png
│ │ ├── appstore.png
│ │ ├── ItunesArtwork@2x.png
│ │ ├── Icon-App-20x20@2x.png
│ │ ├── Icon-App-20x20@3x.png
│ │ ├── Icon-App-29x29@1x.png
│ │ ├── Icon-App-29x29@2x.png
│ │ ├── Icon-App-29x29@3x.png
│ │ ├── Icon-App-40x40@2x.png
│ │ ├── Icon-App-40x40@3x.png
│ │ ├── Icon-App-60x60@2x.png
│ │ ├── Icon-App-60x60@3x.png
│ │ └── Contents.json
│ ├── CalItem.colorset
│ │ └── Contents.json
│ ├── TabButton.colorset
│ │ └── Contents.json
│ ├── Primary.colorset
│ │ └── Contents.json
│ ├── CourseCardPrimaryAccent.colorset
│ │ └── Contents.json
│ ├── CalendarItemBackground.colorset
│ │ └── Contents.json
│ ├── PrimaryBackground.colorset
│ │ └── Contents.json
│ ├── CourseCardSecondaryAccent.colorset
│ │ └── Contents.json
│ └── SecondaryBackground.colorset
│ │ └── Contents.json
├── Preview Content
│ └── Preview Assets.xcassets
│ │ └── Contents.json
├── Core
│ ├── TimeMan.xcdatamodeld
│ │ ├── .xccurrentversion
│ │ └── TimeMan.xcdatamodel
│ │ │ └── contents
│ ├── Course+CoreDataClass.swift
│ └── Course+CoreDataProperties.swift
├── Utilities
│ ├── Shapes
│ │ ├── CustomRoundedEdgesShape.swift
│ │ └── CustomShapeTriangle.swift
│ ├── DateTimeUtilities.swift
│ ├── ColorCodes.swift
│ └── WeekDaysUtilities.swift
├── Views
│ ├── Components
│ │ ├── CalendarItem.swift
│ │ ├── Header.swift
│ │ ├── FloatingActionButton.swift
│ │ ├── MultiSelector
│ │ │ ├── MultiSelector.swift
│ │ │ └── MultiSelectionView.swift
│ │ ├── CourseCardComponents
│ │ │ └── CourseCardComponents.swift
│ │ └── GridComponents
│ │ │ └── GridComponents.swift
│ ├── ContentView.swift
│ ├── Widgets
│ │ ├── UpcomingClasses.swift
│ │ ├── CoursesList.swift
│ │ ├── WeekScroll.swift
│ │ ├── TabBar
│ │ │ └── TabBar.swift
│ │ └── Cards
│ │ │ ├── UpcomingCourseCard.swift
│ │ │ └── CourseCard.swift
│ └── Screens
│ │ ├── HomeScreen.swift
│ │ ├── ScrollScreen.swift
│ │ ├── GridScreen.swift
│ │ └── CourseInput.swift
├── ViewModels
│ ├── UpcomingCourseCardViewModel.swift
│ ├── CourseCardViewModel.swift
│ ├── CourseListViewModel.swift
│ ├── CourseViewModel.swift
│ ├── GridViewModel.swift
│ └── UpcomingClassViewModel.swift
├── Base.lproj
│ └── LaunchScreen.storyboard
├── .swiftlint.yml
├── Info.plist
├── Services
│ ├── LocalNotificationManager.swift
│ └── AppleEvents.swift
├── SceneDelegate.swift
└── AppDelegate.swift
├── TimeMan.xcodeproj
├── project.xcworkspace
│ ├── contents.xcworkspacedata
│ ├── xcuserdata
│ │ └── SaiAnkit.xcuserdatad
│ │ │ └── UserInterfaceState.xcuserstate
│ └── xcshareddata
│ │ └── IDEWorkspaceChecks.plist
├── xcuserdata
│ └── SaiAnkit.xcuserdatad
│ │ ├── xcdebugger
│ │ └── Breakpoints_v2.xcbkptlist
│ │ └── xcschemes
│ │ └── xcschememanagement.plist
└── project.pbxproj
└── README.md
/Screenshots/Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/Screenshots/Icon.png
--------------------------------------------------------------------------------
/Screenshots/Delete-Dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/Screenshots/Delete-Dark.png
--------------------------------------------------------------------------------
/Screenshots/AddCourse-Dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/Screenshots/AddCourse-Dark.png
--------------------------------------------------------------------------------
/Screenshots/ColorCode-Dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/Screenshots/ColorCode-Dark.png
--------------------------------------------------------------------------------
/Screenshots/GridView-Dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/Screenshots/GridView-Dark.png
--------------------------------------------------------------------------------
/Screenshots/GridView-Light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/Screenshots/GridView-Light.png
--------------------------------------------------------------------------------
/Screenshots/AddCourse-Light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/Screenshots/AddCourse-Light.png
--------------------------------------------------------------------------------
/Screenshots/HomeScreen-Dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/Screenshots/HomeScreen-Dark.png
--------------------------------------------------------------------------------
/Screenshots/HomeScreen-Light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/Screenshots/HomeScreen-Light.png
--------------------------------------------------------------------------------
/Screenshots/ScrollView-Dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/Screenshots/ScrollView-Dark.png
--------------------------------------------------------------------------------
/Screenshots/ScrollView-Light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/Screenshots/ScrollView-Light.png
--------------------------------------------------------------------------------
/Screenshots/AppleCalendar-Dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/Screenshots/AppleCalendar-Dark.png
--------------------------------------------------------------------------------
/Screenshots/AppleCalendar-Light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/Screenshots/AppleCalendar-Light.png
--------------------------------------------------------------------------------
/Screenshots/Notifications-Dark.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/Screenshots/Notifications-Dark.png
--------------------------------------------------------------------------------
/Screenshots/Notifications-Light.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/Screenshots/Notifications-Light.png
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Beaming Blue/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Gleamy Pink/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Placid Green/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Purple Punch/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Tangy Orange/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Trendy Teal/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Vanilla Ice/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/Logo.imageset/Icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/TimeMan/Assets.xcassets/Logo.imageset/Icon.png
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/splash.imageset/256.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/TimeMan/Assets.xcassets/splash.imageset/256.png
--------------------------------------------------------------------------------
/TimeMan/Preview Content/Preview Assets.xcassets/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/AppIcon.appiconset/114.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/TimeMan/Assets.xcassets/AppIcon.appiconset/114.png
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/AppIcon.appiconset/57.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/TimeMan/Assets.xcassets/AppIcon.appiconset/57.png
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Bright Yellow/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "info" : {
3 | "author" : "xcode",
4 | "version" : 1
5 | }
6 | }
7 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/AppIcon.appiconset/appstore.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/TimeMan/Assets.xcassets/AppIcon.appiconset/appstore.png
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/TimeMan/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/TimeMan/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/TimeMan/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/TimeMan/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/TimeMan/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/TimeMan/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/TimeMan/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/TimeMan/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/TimeMan/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/TimeMan/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png
--------------------------------------------------------------------------------
/TimeMan.xcodeproj/project.xcworkspace/contents.xcworkspacedata:
--------------------------------------------------------------------------------
1 |
2 |
4 |
6 |
7 |
8 |
--------------------------------------------------------------------------------
/TimeMan.xcodeproj/project.xcworkspace/xcuserdata/SaiAnkit.xcuserdatad/UserInterfaceState.xcuserstate:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/saiankit/TimeMan/HEAD/TimeMan.xcodeproj/project.xcworkspace/xcuserdata/SaiAnkit.xcuserdatad/UserInterfaceState.xcuserstate
--------------------------------------------------------------------------------
/TimeMan.xcodeproj/xcuserdata/SaiAnkit.xcuserdatad/xcdebugger/Breakpoints_v2.xcbkptlist:
--------------------------------------------------------------------------------
1 |
2 |
6 |
7 |
--------------------------------------------------------------------------------
/TimeMan/Core/TimeMan.xcdatamodeld/.xccurrentversion:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | _XCCurrentVersionName
6 | TimeMan.xcdatamodel
7 |
8 |
9 |
--------------------------------------------------------------------------------
/TimeMan.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | IDEDidComputeMac32BitWarning
6 |
7 |
8 |
9 |
--------------------------------------------------------------------------------
/TimeMan/Core/Course+CoreDataClass.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Course+CoreDataClass.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 10/1/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 | //
9 |
10 | import CoreData
11 | import Foundation
12 |
13 | @objc(Course)
14 | public class Course: NSManagedObject {
15 | }
16 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/CalItem.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.820",
9 | "green" : "1.000",
10 | "red" : "0.792"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/TabButton.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.000",
9 | "green" : "0.600",
10 | "red" : "1.000"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Gleamy Pink/GleamyPinkPrimary.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.894",
9 | "green" : "0.486",
10 | "red" : "0.937"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Trendy Teal/TrendyTealPrimary.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.729",
9 | "green" : "0.937",
10 | "red" : "0.443"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Vanilla Ice/VanillaIcePrimary.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.878",
9 | "green" : "0.878",
10 | "red" : "0.961"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Beaming Blue/BeamingBluePrimary.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.933",
9 | "green" : "0.859",
10 | "red" : "0.518"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Beaming Blue/BeamingBlueSecondary.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "1.000",
9 | "green" : "0.933",
10 | "red" : "0.624"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Bright Yellow/BrightYellowPrimary.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.255",
9 | "green" : "0.871",
10 | "red" : "1.000"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Bright Yellow/BrightYellowSecondary.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.522",
9 | "green" : "0.918",
10 | "red" : "1.000"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Gleamy Pink/GleamyPinkSecondary.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.957",
9 | "green" : "0.580",
10 | "red" : "1.000"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Placid Green/PlacidGreenPrimary.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.486",
9 | "green" : "0.882",
10 | "red" : "0.647"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Placid Green/PlacidGreenSecondary.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.553",
9 | "green" : "0.957",
10 | "red" : "0.718"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Purple Punch/PurplePunchPrimary.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.992",
9 | "green" : "0.514",
10 | "red" : "0.490"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Purple Punch/PurplePunchSecondary.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.961",
9 | "green" : "0.690",
10 | "red" : "0.631"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Tangy Orange/TangyOrangePrimary.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.431",
9 | "green" : "0.557",
10 | "red" : "1.000"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Tangy Orange/TangyOrangeSecondary.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.553",
9 | "green" : "0.651",
10 | "red" : "1.000"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Trendy Teal/TrendyTealSecondary.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.796",
9 | "green" : "1.000",
10 | "red" : "0.518"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/ColorCodes/Vanilla Ice/VanillaIceSecondary.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.922",
9 | "green" : "0.922",
10 | "red" : "0.973"
11 | }
12 | },
13 | "idiom" : "universal"
14 | }
15 | ],
16 | "info" : {
17 | "author" : "xcode",
18 | "version" : 1
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/Logo.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Icon.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | },
21 | "properties" : {
22 | "template-rendering-intent" : "original"
23 | }
24 | }
25 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/splash.imageset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "256.png",
5 | "idiom" : "universal",
6 | "scale" : "1x"
7 | },
8 | {
9 | "idiom" : "universal",
10 | "scale" : "2x"
11 | },
12 | {
13 | "idiom" : "universal",
14 | "scale" : "3x"
15 | }
16 | ],
17 | "info" : {
18 | "author" : "xcode",
19 | "version" : 1
20 | },
21 | "properties" : {
22 | "auto-scaling" : "auto",
23 | "template-rendering-intent" : "original"
24 | }
25 | }
26 |
--------------------------------------------------------------------------------
/TimeMan.xcodeproj/xcuserdata/SaiAnkit.xcuserdatad/xcschemes/xcschememanagement.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | SchemeUserState
6 |
7 | TimeMan.xcscheme_^#shared#^_
8 |
9 | orderHint
10 | 0
11 |
12 | TimeManWidgetExtension.xcscheme_^#shared#^_
13 |
14 | orderHint
15 | 0
16 |
17 |
18 |
19 |
20 |
--------------------------------------------------------------------------------
/TimeMan/Utilities/Shapes/CustomRoundedEdgesShape.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CustomRoundedEdgesShape.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 10/3/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct CustomCorner: Shape {
12 | var corners: UIRectCorner
13 | var size: CGFloat
14 | func path(in rect: CGRect) -> Path {
15 | let path = UIBezierPath(
16 | roundedRect: rect,
17 | byRoundingCorners: corners,
18 | cornerRadii: CGSize(width: size, height: size)
19 | )
20 |
21 | return Path(path.cgPath)
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/TimeMan/Views/Components/CalendarItem.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CalendarItem.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 9/22/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct CalendarItem: View {
12 | var isSelected: Bool
13 | var weekDay: String
14 | var body: some View {
15 | VStack {
16 | Text(self.weekDay)
17 | .font(.headline)
18 | .fontWeight(.semibold)
19 | .foregroundColor(Color.black)
20 | }
21 | .frame(width: 30, height: 30)
22 | .padding(6)
23 | .background(isSelected ? Color("CalItem") : Color("CalendarItemBackground"))
24 | .cornerRadius(15)
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/TimeMan/Views/Components/Header.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Header.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 9/25/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct Header: View {
12 | var body: some View {
13 | HStack {
14 | VStack {
15 | HStack {
16 | Text("TimeMan")
17 | .font(.largeTitle)
18 | .fontWeight(.bold)
19 | }
20 | HStack {
21 | Text("Track your Timetable")
22 | .font(.callout)
23 | .fontWeight(.thin)
24 | }
25 | }
26 | .padding(.leading)
27 | Spacer()
28 | }
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/TimeMan/Views/ContentView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ContentView.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 9/21/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct ContentView: View {
12 | @State var isPresented = false
13 | @State var selectedIndex = "Home"
14 | var body: some View {
15 | VStack {
16 | if selectedIndex == "Scroll" {
17 | ScrollScreen(isPresented: $isPresented)
18 | } else if selectedIndex == "Home" {
19 | HomeScreen()
20 | } else {
21 | GridScreen(isPresented: $isPresented)
22 | }
23 | TabBar(selectedTab: $selectedIndex, isPresented: $isPresented)
24 | }
25 | .edgesIgnoringSafeArea(.bottom)
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/Primary.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "display-p3",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.000",
9 | "green" : "0.000",
10 | "red" : "0.000"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "1.000",
27 | "green" : "1.000",
28 | "red" : "1.000"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/CourseCardPrimaryAccent.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.608",
9 | "green" : "0.294",
10 | "red" : "0.271"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0.961",
27 | "green" : "0.553",
28 | "red" : "0.475"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/CalendarItemBackground.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "display-p3",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.957",
9 | "green" : "0.898",
10 | "red" : "0.859"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "srgb",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "1.000",
27 | "green" : "1.000",
28 | "red" : "1.000"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/PrimaryBackground.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "display-p3",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "1.000",
9 | "green" : "1.000",
10 | "red" : "1.000"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "display-p3",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0.000",
27 | "green" : "0.000",
28 | "red" : "0.000"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/TimeMan/ViewModels/UpcomingCourseCardViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UpcomingCourseCardViewModel.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 10/11/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | class UpcomingCourseCardViewModel {
12 | private var courseCardViewModel = CourseCardViewModel()
13 |
14 | func classTime(course: Course) -> String {
15 | return courseCardViewModel.classTime(course: course)
16 | }
17 |
18 | func getClassType(course: Course) -> String {
19 | return courseCardViewModel.getClassType(course: course)
20 | }
21 |
22 | func secondaryBackground(course: Course) -> Color {
23 | return courseCardViewModel.secondaryBackground(course: course)
24 | }
25 |
26 | func primaryBackground(course: Course) -> Color {
27 | return courseCardViewModel.primaryBackground(course: course)
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/CourseCardSecondaryAccent.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "srgb",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.804",
9 | "green" : "0.380",
10 | "red" : "0.353"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "display-p3",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0.933",
27 | "green" : "0.624",
28 | "red" : "0.576"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/SecondaryBackground.colorset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "colors" : [
3 | {
4 | "color" : {
5 | "color-space" : "display-p3",
6 | "components" : {
7 | "alpha" : "1.000",
8 | "blue" : "0.957",
9 | "green" : "0.898",
10 | "red" : "0.859"
11 | }
12 | },
13 | "idiom" : "universal"
14 | },
15 | {
16 | "appearances" : [
17 | {
18 | "appearance" : "luminosity",
19 | "value" : "dark"
20 | }
21 | ],
22 | "color" : {
23 | "color-space" : "display-p3",
24 | "components" : {
25 | "alpha" : "1.000",
26 | "blue" : "0.141",
27 | "green" : "0.106",
28 | "red" : "0.122"
29 | }
30 | },
31 | "idiom" : "universal"
32 | }
33 | ],
34 | "info" : {
35 | "author" : "xcode",
36 | "version" : 1
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/TimeMan/Utilities/Shapes/CustomShapeTriangle.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CustomShapeTriangle.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 10/3/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct CustomShapeHome: Shape {
12 | func path(in rect: CGRect) -> Path {
13 | return Path {path in
14 | let pt1 = CGPoint(x: 0, y: 0)
15 | let pt2 = CGPoint(x: 0, y: rect.height)
16 | let pt3 = CGPoint(x: rect.width, y: rect.height)
17 | let pt4 = CGPoint(x: rect.width, y: 150)
18 |
19 | path.move(to: pt4)
20 |
21 | path.addArc(tangent1End: pt1, tangent2End: pt2, radius: 45)
22 | path.addArc(tangent1End: pt2, tangent2End: pt3, radius: 45)
23 | path.addArc(tangent1End: pt3, tangent2End: pt4, radius: 45)
24 | path.addArc(tangent1End: pt4, tangent2End: pt1, radius: 45)
25 | }
26 | }
27 | }
28 |
--------------------------------------------------------------------------------
/TimeMan/Utilities/DateTimeUtilities.swift:
--------------------------------------------------------------------------------
1 | //
2 | // DateTimeUtilities.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 10/10/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import Foundation
10 |
11 | class DateTimeUtilities {
12 | private var calendar = Calendar.current
13 |
14 | // MARK: - Stringify Time
15 | // Method Used to convert a Date Component into Stringified Time
16 | func getClassTime(time: Date) -> String {
17 | var timeType: String = "AM"
18 | var hour: Int = (calendar.component(.hour, from: time))
19 | let minute: Int = (calendar.component(.minute, from: time))
20 | var aminute = (minute == 0) ? "00" : String(minute)
21 | aminute = ((minute < 10) && (minute >= 1)) ? "0" + String(minute) : String(aminute)
22 | timeType = hour >= 12 ? "PM" : "AM"
23 | hour = hour > 12 ? hour - 12 : hour
24 | return String(hour) + ":" + String(aminute) + " " + timeType
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/TimeMan/Views/Components/FloatingActionButton.swift:
--------------------------------------------------------------------------------
1 | //
2 | // FloatingActionButton.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 10/4/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct FloatingActionButton: View {
12 | @Binding var isPresented: Bool
13 | var body: some View {
14 | VStack {
15 | Spacer()
16 | HStack {
17 | Spacer()
18 | Button {
19 | self.isPresented.toggle()
20 | } label: {
21 | Text("+")
22 | .font(.system(.largeTitle))
23 | .frame(width: 50, height: 45)
24 | .foregroundColor(Color.white)
25 | .padding(.bottom, 7)
26 | }
27 | .background(Color("CourseCardSecondaryAccent"))
28 | .cornerRadius(25)
29 | .padding()
30 | .shadow(color: Color.black.opacity(0.3), radius: 3, x: 3, y: 3)
31 | }
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/TimeMan/Views/Widgets/UpcomingClasses.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UpcomingClasses.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 10/2/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct UpcomingClasses: View {
12 | var viewModel = UpcomingClassViewModel()
13 | @FetchRequest(
14 | entity: Course.entity(),
15 | sortDescriptors: [NSSortDescriptor(key: "time", ascending: true)]
16 | ) var coursesList: FetchedResults
17 | var body: some View {
18 | VStack {
19 | if !viewModel.areUpcomingClassesAvailable(list: coursesList) {
20 | NoUpcomingClasses()
21 | } else {
22 | UpcomingCourseCard(course: viewModel.getUpcomingClass(list: coursesList))
23 | }
24 | }
25 | .padding(20)
26 | }
27 | }
28 |
29 | struct NoUpcomingClasses: View {
30 | var body: some View {
31 | VStack {
32 | VStack {
33 | Text("No Upcoming Classes")
34 | .font(.system(size: 22, weight: .bold, design: .rounded))
35 | .padding(.bottom, 5)
36 | }
37 | }
38 | .padding(20)
39 | }
40 | }
41 |
--------------------------------------------------------------------------------
/TimeMan/Utilities/ColorCodes.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ColorCodes.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 10/10/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | class ColorCodes {
12 | let colorNumbers: [Color] = [
13 | Color("PurplePunchPrimary"),
14 | Color("TangyOrangePrimary"),
15 | Color("PlacidGreenPrimary"),
16 | Color("BrightYellowPrimary"),
17 | Color("TrendyTealPrimary"),
18 | Color("GleamyPinkPrimary"),
19 | Color("BeamingBluePrimary"),
20 | Color("VanillaIcePrimary")
21 | ]
22 |
23 | let colorNumbersLight: [Color] = [
24 | Color("PurplePunchSecondary"),
25 | Color("TangyOrangeSecondary"),
26 | Color("PlacidGreenSecondary"),
27 | Color("BrightYellowSecondary"),
28 | Color("TrendyTealSecondary"),
29 | Color("GleamyPinkSecondary"),
30 | Color("BeamingBlueSecondary"),
31 | Color("VanillaIceSecondary")
32 | ]
33 |
34 | let colorNames: [String] = [
35 | "Purple Punch",
36 | "Tangy Orange",
37 | "Placid Green",
38 | "Bright Yellow",
39 | "Trendy Teal",
40 | "Gleamy Pink",
41 | "Beaming Blue",
42 | "Vanilla Ice"
43 | ]
44 | }
45 |
--------------------------------------------------------------------------------
/TimeMan/Views/Components/MultiSelector/MultiSelector.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MultiSelector.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 9/24/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct MultiSelector: View {
12 | let label: LabelView
13 | let options: [Selectable]
14 | let optionToString: (Selectable) -> String
15 |
16 | var selected: Binding>
17 |
18 | private var formattedSelectedListString: String {
19 | ListFormatter.localizedString(byJoining: selected.wrappedValue.map { optionToString($0) })
20 | }
21 |
22 | var body: some View {
23 | NavigationLink(destination: multiSelectionView()) {
24 | HStack {
25 | label
26 | Spacer()
27 | Text(formattedSelectedListString)
28 | .foregroundColor(.gray)
29 | .multilineTextAlignment(.trailing)
30 | }
31 | }
32 | }
33 |
34 | private func multiSelectionView() -> some View {
35 | MultiSelectionView(
36 | options: options,
37 | optionToString: optionToString,
38 | selected: selected
39 | )
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/TimeMan/Core/Course+CoreDataProperties.swift:
--------------------------------------------------------------------------------
1 | //
2 | // Course+CoreDataProperties.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 10/1/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 | //
9 |
10 | import CoreData
11 | import Foundation
12 |
13 | extension Course {
14 | @nonobjc public class func fetchRequest() -> NSFetchRequest {
15 | return NSFetchRequest(entityName: "Course")
16 | }
17 |
18 | @NSManaged public var courseCode: String?
19 | @NSManaged public var courseID: String?
20 | @NSManaged public var courseTitle: String?
21 | @NSManaged public var id: UUID?
22 | @NSManaged public var instructorName: String?
23 | @NSManaged public var isLecture: Bool
24 | @NSManaged public var isPractical: Bool
25 | @NSManaged public var isTutorial: Bool
26 | @NSManaged public var lectureExists: Bool
27 | @NSManaged public var lectureNumber: String?
28 | @NSManaged public var meetLink: String?
29 | @NSManaged public var practicalExists: Bool
30 | @NSManaged public var practicalNumber: String?
31 | @NSManaged public var time: Date?
32 | @NSManaged public var tutorialExists: Bool
33 | @NSManaged public var tutorialNumber: String?
34 | @NSManaged public var weekDayRepeat: Set?
35 | @NSManaged public var colorNum: Int16
36 | }
37 |
38 | extension Course: Identifiable {
39 | }
40 |
--------------------------------------------------------------------------------
/TimeMan/Base.lproj/LaunchScreen.storyboard:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
--------------------------------------------------------------------------------
/TimeMan/ViewModels/CourseCardViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CourseCardViewModel.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 10/11/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | class CourseCardViewModel {
12 | @Environment(\.managedObjectContext) var managedObjectContext
13 | private var colorCodes = ColorCodes()
14 | private var dateTimeUtilities = DateTimeUtilities()
15 |
16 | func deleteItem(course: Course) {
17 | managedObjectContext.delete(course)
18 | do {
19 | try self.managedObjectContext.save()
20 | } catch {
21 | print(error)
22 | }
23 | }
24 |
25 | func getClassType(course: Course) -> String {
26 | if course.isLecture {
27 | return course.lectureNumber!
28 | } else if course.isTutorial {
29 | return course.tutorialNumber!
30 | } else if course.isPractical {
31 | return course.practicalNumber!
32 | }
33 | return ""
34 | }
35 |
36 | func secondaryBackground(course: Course) -> Color {
37 | return colorCodes.colorNumbersLight[Int(course.colorNum)]
38 | }
39 |
40 | func primaryBackground(course: Course) -> Color {
41 | return colorCodes.colorNumbers[Int(course.colorNum)]
42 | }
43 |
44 | func classTime(course: Course) -> String {
45 | return dateTimeUtilities.getClassTime(time: course.time ?? Date())
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/TimeMan/Views/Screens/HomeScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // HomeScreen.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 10/3/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct HomeScreen: View {
12 | var body: some View {
13 | NavigationView {
14 | if #available(iOS 14.0, *) {
15 | ZStack {
16 | Color("PrimaryBackground")
17 | VStack(alignment: .leading) {
18 | Spacer()
19 | Image("Logo")
20 | .renderingMode(.original)
21 | .resizable()
22 | .frame(width: 100, height: 100)
23 | .padding(.leading)
24 | Spacer()
25 | Text("Upcoming Class")
26 | .font(
27 | .system(size: 22, weight: .bold, design: .rounded)
28 | )
29 | .padding(.bottom, 5)
30 | .padding(.leading, 30)
31 | UpcomingClasses()
32 | Spacer()
33 | }
34 | .frame(width: 320.0, height: 500.0)
35 | .background(
36 | Color("SecondaryBackground")
37 | .clipShape(CustomShapeHome())
38 | )
39 | }
40 | .navigationTitle("TimeMan")
41 | }
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/TimeMan/Views/Components/MultiSelector/MultiSelectionView.swift:
--------------------------------------------------------------------------------
1 | //
2 | // MultiSelectionView.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 9/24/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct MultiSelectionView: View {
12 | let options: [Selectable]
13 | let optionToString: (Selectable) -> String
14 |
15 | @Binding var selected: Set
16 |
17 | var body: some View {
18 | List {
19 | ForEach(options) { selectable in
20 | Button {
21 | toggleSelection(selectable: selectable)
22 | } label: {
23 | HStack {
24 | Text(optionToString(selectable))
25 | .foregroundColor(Color("Primary"))
26 | Spacer()
27 | if selected.contains { $0.id == selectable.id } {
28 | Image(systemName: "checkmark.circle")
29 | .foregroundColor(.accentColor)
30 | }
31 | }
32 | }
33 | .tag(selectable.id)
34 | }
35 | }.listStyle(GroupedListStyle())
36 | }
37 |
38 | private func toggleSelection(selectable: Selectable) {
39 | if let existingIndex = selected.firstIndex(where: { $0.id == selectable.id }) {
40 | selected.remove(at: existingIndex)
41 | } else {
42 | selected.insert(selectable)
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/TimeMan/Views/Widgets/CoursesList.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CoursesList.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 9/27/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct CoursesList: View {
12 | @Binding var calendarIndex: Int
13 | @FetchRequest(
14 | entity: Course.entity(),
15 | sortDescriptors: [NSSortDescriptor(key: "time", ascending: true)]
16 | ) var coursesList: FetchedResults
17 | var viewModel = CourseListViewModel()
18 |
19 | var body: some View {
20 | if #available(iOS 14.0, *) {
21 | if !viewModel.areClassesAvailableToday(coursesList: coursesList, index: calendarIndex) {
22 | NoClassesToday()
23 | } else {
24 | LazyVStack(alignment: .leading) {
25 | ForEach(self.coursesList, id: \.self) {
26 | if viewModel.shouldCourseBeIncluded(course: $0, index: self.calendarIndex) {
27 | CourseCard(course: $0)
28 | }
29 | }
30 | }
31 | }
32 | }
33 | }
34 | }
35 |
36 | struct NoClassesToday: View {
37 | var body: some View {
38 | VStack {
39 | Spacer()
40 | HStack {
41 | Spacer()
42 | Text("No Classes Today")
43 | .font(.system(size: 22, weight: .bold, design: .rounded ))
44 | .padding(.bottom, 5)
45 | .padding(.leading, 20)
46 | Spacer()
47 | }
48 | Spacer()
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/TimeMan/ViewModels/CourseListViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CourseListViewModel.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 10/4/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | class CourseListViewModel {
12 | func shouldCourseBeIncluded(course: Course, index: Int) -> Bool {
13 | // To find if current course is scheduled on the given day of the week
14 | // We check if the given day of the week is present in the weekDayRepeat of current Course
15 | // If the course is scheduled on that day of week "true" is returned
16 | // Else "false" is returned
17 |
18 | let weekDayName = longWeekDaySymbols[index]
19 | let converted = course.weekDayRepeat
20 | if converted!.contains(weekDayName) {
21 | return true
22 | }
23 | return false
24 | }
25 |
26 | func areClassesAvailableToday(coursesList: FetchedResults, index: Int) -> Bool {
27 | // To find if any classes are scheduled on that day
28 | // We loop across all the courses and
29 | // check if the given day of the week is present in the weekDayRepeat of the iterable Course
30 |
31 | var areClassesAvailable = false
32 | let weekDayName = longWeekDaySymbols[index]
33 | for course in coursesList {
34 | let courseWeekDayRepeat = course.weekDayRepeat
35 | if courseWeekDayRepeat!.contains(weekDayName) {
36 | areClassesAvailable = true
37 | }
38 | }
39 | if !areClassesAvailable {
40 | return false
41 | }
42 | return true
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/TimeMan/Views/Widgets/WeekScroll.swift:
--------------------------------------------------------------------------------
1 | //
2 | // WeekScroll.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 9/22/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct WeekScroll: View {
12 | @Binding var index: Int
13 | var body: some View {
14 | HStack {
15 | CalendarItem(isSelected: self.index == 1 ? true : false, weekDay: "Mo")
16 | .onTapGesture {
17 | self.index = 1
18 | }
19 | CalendarItem(isSelected: self.index == 2 ? true : false, weekDay: "Tu")
20 | .onTapGesture {
21 | self.index = 2
22 | }
23 | CalendarItem(isSelected: self.index == 3 ? true : false, weekDay: "We")
24 | .onTapGesture {
25 | self.index = 3
26 | }
27 | CalendarItem(isSelected: self.index == 4 ? true : false, weekDay: "Th")
28 | .onTapGesture {
29 | self.index = 4
30 | }
31 | CalendarItem(isSelected: self.index == 5 ? true : false, weekDay: "Fr")
32 | .onTapGesture {
33 | self.index = 5
34 | }
35 | CalendarItem(isSelected: self.index == 6 ? true : false, weekDay: "Sa")
36 | .onTapGesture {
37 | self.index = 6
38 | }
39 | CalendarItem(isSelected: self.index == 0 ? true : false, weekDay: "Su")
40 | .onTapGesture {
41 | self.index = 0
42 | }
43 | }
44 | .padding(.bottom, 20)
45 | .padding(.horizontal)
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/TimeMan/Views/Widgets/TabBar/TabBar.swift:
--------------------------------------------------------------------------------
1 | //
2 | // TabBar.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 10/3/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct TabBar: View {
12 | @Binding var selectedTab: String
13 | @Binding var isPresented: Bool
14 | var body: some View {
15 | HStack {
16 | TabButton(title: "Scroll", image: "lineweight", selectedTab: $selectedTab)
17 | Spacer(minLength: 0)
18 | TabButton(title: "Home", image: "house", selectedTab: $selectedTab)
19 | Spacer(minLength: 0)
20 | TabButton(title: "Grid", image: "calendar", selectedTab: $selectedTab)
21 | }
22 | .padding(.vertical)
23 | .padding(.horizontal, 20)
24 | .background(Color("SecondaryBackground"))
25 | .shadow(color: Color.black.opacity(0.05), radius: 5, x: 0, y: -5)
26 | }
27 | }
28 |
29 | struct TabButton: View {
30 | var title: String
31 | var image: String
32 | @Binding var selectedTab: String
33 | var body: some View {
34 | Button {
35 | selectedTab = title
36 | } label: {
37 | HStack(spacing: 10) {
38 | Image(systemName: image)
39 | .renderingMode(.template)
40 | if title != "Home" {
41 | Text(title)
42 | .fontWeight(.semibold)
43 | }
44 | }
45 | .foregroundColor(selectedTab == title ? Color("TabButton") : .gray)
46 | .padding(.vertical)
47 | .padding(.horizontal, 15)
48 | .background(Color.yellow.opacity(selectedTab == title ? 0.10 : 0))
49 | .clipShape(Capsule())
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | TimeMan ⏰
5 |
6 |
7 |
8 | ------
9 |
10 |
11 | Native iOS App
12 | to enable the people of BPHC to track of their Timetables
13 |
14 |
15 |
16 |
17 | ## Salient Features of TimeMan
18 |
19 | ###### 1. The feature set includes viewing the timetable, adding a course ( along with it's Google Meet Link ) to the Timetable, and to view a course in the Timetable.
20 | ###### 2. The Timetable can be seen in two types of views : Scroll View and Grid View
21 | ###### 3. The user can add these course events to the Apple Calendar.
22 | ###### 4. The app offers local notifications 10 minutes before each event.
23 | ###### 5. The home screen shows the Upcoming class - Glanceable Screen
24 |
25 | ------
26 |
27 | |  |  |  |
28 | |---|---|---|
29 | |  |  |  |
30 | |  |  |  |
31 | |  |  |  |
32 | |  | |  |
33 |
34 | ------
35 |
36 | #### How to use the app locally on your machine ?
37 |
38 | ### Requirements 📋
39 |
40 | ###### 1. macOS 10.15.7 +
41 | ###### 2. Xcode 12
42 |
43 | **Step #1** : Clone this repository using
44 |
45 | ``` git clone https://github.com/saiankit/TimeMan.git ```
46 |
47 | **Step #2** : Open in Xcode
48 |
49 | ``` open TimeMan.xcworkspace```
50 |
51 | **Step #3**: Open a simulator and run the application
52 |
53 |
54 | - - - -
55 | ### Created and Maintained by
56 | #### Sai Ankit
57 | ###### This application is being built as a part of CRUx Round II Inductions.
--------------------------------------------------------------------------------
/TimeMan/Assets.xcassets/AppIcon.appiconset/Contents.json:
--------------------------------------------------------------------------------
1 | {
2 | "images" : [
3 | {
4 | "filename" : "Icon-App-20x20@2x.png",
5 | "idiom" : "iphone",
6 | "scale" : "2x",
7 | "size" : "20x20"
8 | },
9 | {
10 | "filename" : "Icon-App-20x20@3x.png",
11 | "idiom" : "iphone",
12 | "scale" : "3x",
13 | "size" : "20x20"
14 | },
15 | {
16 | "filename" : "Icon-App-29x29@1x.png",
17 | "idiom" : "iphone",
18 | "scale" : "1x",
19 | "size" : "29x29"
20 | },
21 | {
22 | "filename" : "Icon-App-29x29@2x.png",
23 | "idiom" : "iphone",
24 | "scale" : "2x",
25 | "size" : "29x29"
26 | },
27 | {
28 | "filename" : "Icon-App-29x29@3x.png",
29 | "idiom" : "iphone",
30 | "scale" : "3x",
31 | "size" : "29x29"
32 | },
33 | {
34 | "filename" : "Icon-App-40x40@2x.png",
35 | "idiom" : "iphone",
36 | "scale" : "2x",
37 | "size" : "40x40"
38 | },
39 | {
40 | "filename" : "Icon-App-40x40@3x.png",
41 | "idiom" : "iphone",
42 | "scale" : "3x",
43 | "size" : "40x40"
44 | },
45 | {
46 | "filename" : "57.png",
47 | "idiom" : "iphone",
48 | "scale" : "1x",
49 | "size" : "57x57"
50 | },
51 | {
52 | "filename" : "114.png",
53 | "idiom" : "iphone",
54 | "scale" : "2x",
55 | "size" : "57x57"
56 | },
57 | {
58 | "filename" : "Icon-App-60x60@2x.png",
59 | "idiom" : "iphone",
60 | "scale" : "2x",
61 | "size" : "60x60"
62 | },
63 | {
64 | "filename" : "Icon-App-60x60@3x.png",
65 | "idiom" : "iphone",
66 | "scale" : "3x",
67 | "size" : "60x60"
68 | },
69 | {
70 | "filename" : "appstore.png",
71 | "idiom" : "ios-marketing",
72 | "scale" : "1x",
73 | "size" : "1024x1024"
74 | },
75 | {
76 | "filename" : "ItunesArtwork@2x.png",
77 | "idiom" : "mac",
78 | "scale" : "2x",
79 | "size" : "512x512"
80 | }
81 | ],
82 | "info" : {
83 | "author" : "xcode",
84 | "version" : 1
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/TimeMan/.swiftlint.yml:
--------------------------------------------------------------------------------
1 | # SwiftLint Configuration File
2 | excluded:
3 | - Carthage
4 | - Pods
5 | - SwiftLint/Common/3rdPartyLib
6 | disabled_rules:
7 | - trailing_whitespace
8 | - force_cast
9 | - discarded_notification_center_observer
10 | - notification_center_detachment
11 | - orphaned_doc_comment
12 | - todo
13 | - unused_capture_list
14 |
15 | - identifier_name
16 | - attributes
17 | - unused_closure_parameter
18 | - type_body_length
19 | opt_in_rules:
20 | - array_init
21 | - attributes
22 | - closure_end_indentation
23 | - closure_spacing
24 | - collection_alignment
25 | - colon # promote to error
26 | - convenience_type
27 | - discouraged_object_literal
28 | - empty_collection_literal
29 | - empty_count
30 | - empty_string
31 | - enum_case_associated_values_count
32 | - fatal_error_message
33 | - first_where
34 | - implicitly_unwrapped_optional
35 | - last_where
36 | - legacy_random
37 | - trailing_closure
38 | - literal_expression_end_indentation
39 | - operator_usage_whitespace
40 | - overridden_super_call
41 | - pattern_matching_keywords
42 | - prefer_self_type_over_type_of_self
43 | - redundant_nil_coalescing
44 | - redundant_type_annotation
45 | - strict_fileprivate
46 | - toggle_bool
47 | - unneeded_parentheses_in_closure_argument
48 | - unused_import
49 | - vertical_whitespace_closing_braces
50 | - vertical_whitespace_opening_braces
51 | - yoda_condition
52 |
53 | force_cast: warning
54 | force_try: warning
55 |
56 | legacy_hashing: error
57 |
58 | identifier_name:
59 | excluded:
60 | - id
61 |
62 | line_length:
63 | warning: 150
64 | error: 200
65 | ignores_function_declarations: true
66 | ignores_comments: true
67 | ignores_urls: true
68 |
69 | function_body_length:
70 | warning: 300
71 | error: 500
72 |
73 | function_parameter_count:
74 | warning: 6
75 | error: 8
76 |
77 | type_body_length:
78 | warning: 300
79 | error: 500
80 |
81 | file_length:
82 | warning: 1000
83 | error: 1500
84 | ignore_comment_only_lines: true
85 |
86 | cyclomatic_complexity:
87 | warning: 15
88 | error: 25
89 |
90 | reporter: "xcode"
91 |
--------------------------------------------------------------------------------
/TimeMan/Core/TimeMan.xcdatamodeld/TimeMan.xcdatamodel/contents:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/TimeMan/Views/Screens/ScrollScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // ScrollScreen.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 10/3/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct ScrollScreen: View {
12 | private let calendar = Calendar.current
13 | @Binding var isPresented: Bool
14 | @State var calendarIndex = ((Calendar.current.component(.weekday, from: Date())) - 1)
15 |
16 | var body: some View {
17 | if #available(iOS 14.0, *) {
18 | ZStack {
19 | ScrollView(showsIndicators: false) {
20 | ZStack {
21 | Color("PrimaryBackground")
22 | VStack {
23 | WeekScroll(index: $calendarIndex).padding(.top, 20)
24 | VStack(alignment: .leading) {
25 | HStack {
26 | Text(calendarIndex == (calendar.component(.weekday, from: Date()) - 1)
27 | ? "Today's Classes" : longWeekDaySymbols[calendarIndex]
28 | + "'s Classes")
29 | .font(
30 | .system(size: 20, weight: .bold, design: .rounded)
31 | )
32 | .padding(.bottom, 15)
33 | Spacer()
34 | }
35 | CoursesList(calendarIndex: $calendarIndex)
36 | .sheet(isPresented: $isPresented) {
37 | CourseInput(isPresented: $isPresented)
38 | }
39 | }
40 | .frame(minHeight: 800.0, alignment: .top)
41 | .padding(25)
42 | .background(Color("SecondaryBackground"))
43 | .clipShape(CustomCorner(corners: [.topLeft, .topRight], size: 55))
44 | }
45 | }
46 | }
47 | FloatingActionButton(isPresented: $isPresented)
48 | }
49 | }
50 | }
51 | }
52 |
--------------------------------------------------------------------------------
/TimeMan/Views/Screens/GridScreen.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GridScreen.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 10/3/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct GridScreen: View {
12 | @Binding var isPresented: Bool
13 | var body: some View {
14 | if #available(iOS 14.0, *) {
15 | ZStack {
16 | VStack {
17 | GridWeekRow()
18 | .sheet(isPresented: $isPresented) {
19 | CourseInput(isPresented: $isPresented)
20 | }
21 | TimeGrid()
22 | }
23 | FloatingActionButton(isPresented: $isPresented)
24 | }
25 | }
26 | }
27 | }
28 |
29 | struct GridItem: View {
30 | var time: Int
31 | var weekDay: Int
32 | @FetchRequest(
33 | entity: Course.entity(),
34 | sortDescriptors: [NSSortDescriptor(key: "time", ascending: true)]
35 | ) var coursesList: FetchedResults
36 | var viewModel = GridViewModel()
37 | var body: some View {
38 | if viewModel.getCourseForGrid(
39 | list: coursesList,
40 | gridTime: time,
41 | weekDay: weekDay
42 | )[0] == "Error" {
43 | VStack {
44 | Text("")
45 | }
46 | .frame(width: GridValues.width, height: GridValues.height)
47 | .padding(4)
48 | .background(Color("SecondaryBackground"))
49 | } else {
50 | VStack(alignment: .leading) {
51 | Text(viewModel.getCourseForGrid(list: coursesList, gridTime: time, weekDay: weekDay)[0])
52 | .font(.system(size: 12))
53 | .foregroundColor(.black)
54 | Text(viewModel.getCourseForGrid(list: coursesList, gridTime: time, weekDay: weekDay)[1])
55 | .font(.system(size: 12))
56 | .foregroundColor(.black)
57 | Text(viewModel.getCourseForGrid(list: coursesList, gridTime: time, weekDay: weekDay)[3])
58 | .font(.system(size: 12))
59 | .foregroundColor(.black)
60 | }
61 | .frame(width: GridValues.width, height: GridValues.height)
62 | .padding(4)
63 | .background(viewModel.gridBackground(list: coursesList, gridTime: time, weekDay: weekDay))
64 | .cornerRadius(5)
65 | }
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/TimeMan/Info.plist:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | NSContactsUsageDescription
6 | This app requires your contacts
7 | NSRemindersUsageDescription
8 | This app accesses your reminders
9 | NSCalendarsUsageDescription
10 | This app uses your calendar
11 | CFBundleDevelopmentRegion
12 | $(DEVELOPMENT_LANGUAGE)
13 | CFBundleExecutable
14 | $(EXECUTABLE_NAME)
15 | CFBundleIdentifier
16 | $(PRODUCT_BUNDLE_IDENTIFIER)
17 | CFBundleInfoDictionaryVersion
18 | 6.0
19 | CFBundleName
20 | $(PRODUCT_NAME)
21 | CFBundlePackageType
22 | $(PRODUCT_BUNDLE_PACKAGE_TYPE)
23 | CFBundleShortVersionString
24 | 1.0
25 | CFBundleVersion
26 | 1
27 | LSRequiresIPhoneOS
28 |
29 | UIApplicationSceneManifest
30 |
31 | UIApplicationSupportsMultipleScenes
32 |
33 | UISceneConfigurations
34 |
35 | UIWindowSceneSessionRoleApplication
36 |
37 |
38 | UISceneConfigurationName
39 | Default Configuration
40 | UISceneDelegateClassName
41 | $(PRODUCT_MODULE_NAME).SceneDelegate
42 |
43 |
44 |
45 |
46 | UILaunchScreen
47 |
48 | UIImageName
49 | splash
50 | UIColorName
51 | CourseCardSecondaryAccent
52 |
53 | UIRequiredDeviceCapabilities
54 |
55 | armv7
56 |
57 | UISupportedInterfaceOrientations
58 |
59 | UIInterfaceOrientationPortrait
60 | UIInterfaceOrientationLandscapeLeft
61 | UIInterfaceOrientationLandscapeRight
62 |
63 | UISupportedInterfaceOrientations~ipad
64 |
65 | UIInterfaceOrientationPortrait
66 | UIInterfaceOrientationPortraitUpsideDown
67 | UIInterfaceOrientationLandscapeLeft
68 | UIInterfaceOrientationLandscapeRight
69 |
70 |
71 |
72 |
--------------------------------------------------------------------------------
/TimeMan/Services/LocalNotificationManager.swift:
--------------------------------------------------------------------------------
1 | //
2 | // LocalNotificationManager.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 10/20/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import Foundation
10 | import UserNotifications
11 |
12 | class LocalNotificationManager {
13 | private let center = UNUserNotificationCenter.current()
14 | private let options: UNAuthorizationOptions = [.alert, .sound, .badge]
15 | private let calendar = Calendar.current
16 | private let weekDayUtilities = WeekDayUtilities()
17 |
18 | // Method to register for Notifications on the Device
19 | private func registerNotifications() {
20 | center.requestAuthorization(options: options) { granted, error in
21 | if granted && error == nil {
22 | print("Notifications are registered")
23 | } else {
24 | print("D'oh.. Notifications aren't registered")
25 | }
26 | }
27 | }
28 |
29 | // Public Method used to schedule for Notifications
30 | func scheduleNotification(
31 | title: String,
32 | subtitle: String,
33 | body: String,
34 | time: Date,
35 | weekRepeat: Set
36 | ) {
37 | self.registerNotifications()
38 |
39 | // Content for the Notification
40 | let content = UNMutableNotificationContent()
41 | content.title = title
42 | content.subtitle = subtitle
43 | content.body = body
44 |
45 | // Setting time 10 minutes earlier of the class
46 | let newTime = calendar.date(byAdding: .minute, value: -10, to: time)
47 | var dateComponents = DateComponents()
48 | let hour = calendar.component(.hour, from: newTime!)
49 | let minute = calendar.component(.minute, from: newTime!)
50 | let mappedWeekDayArray = weekDayUtilities.mapToWeekDays(weekDaySet: weekRepeat)
51 |
52 | // We are looping across the weekDay and scheduling the notifcations
53 | for weekDay in mappedWeekDayArray {
54 | dateComponents.hour = hour
55 | dateComponents.minute = minute
56 | dateComponents.weekday = weekDay
57 | let trigger = UNCalendarNotificationTrigger(dateMatching: dateComponents, repeats: true)
58 | let request = UNNotificationRequest(
59 | identifier: UUID().uuidString,
60 | content: content,
61 | trigger: trigger)
62 | center.add(request)
63 | }
64 | }
65 | }
66 |
--------------------------------------------------------------------------------
/TimeMan/Views/Widgets/Cards/UpcomingCourseCard.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UpcomingCourseCard.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 10/3/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct UpcomingCourseCard: View {
12 | var course: Course
13 | var viewModel = UpcomingCourseCardViewModel()
14 | var body: some View {
15 | VStack {
16 | HStack(alignment: .top) {
17 | // Course Information
18 | VStack(alignment: .leading) {
19 | // Course Title
20 | Text(course.courseTitle!)
21 | .font(.system(size: 22, weight: .bold, design: .rounded))
22 | .padding(.bottom, 5)
23 | // Course Code + ID
24 | Text(course.courseCode! + " " + course.courseID!)
25 | .font(.system(size: 18, design: .rounded))
26 | .padding(.bottom, 10)
27 | // Instructor Name
28 | Text(course.instructorName!)
29 | .font(.system(size: 16, design: .rounded))
30 | }
31 | Spacer()
32 | // Class Time and Meet Link
33 | VStack {
34 | // Class Time
35 | Text(viewModel.classTime(course: course))
36 | .font(.system(size: 18, design: .rounded))
37 | .padding(.bottom, 20)
38 | if #available(iOS 14.0, *) {
39 | // Meet Link Button
40 | Link(destination: URL(string: course.meetLink!)!) {
41 | HStack {
42 | Text(viewModel.getClassType(course: course))
43 | .font(.largeTitle)
44 | .fontWeight(.heavy)
45 | .foregroundColor(.white)
46 | Image(systemName: "video.fill")
47 | .foregroundColor(.white)
48 | }
49 | .padding(8)
50 | .background(viewModel.secondaryBackground(course: course))
51 | .cornerRadius(15)
52 | }
53 | }
54 | }
55 | }
56 | }
57 | .padding(20)
58 | .background(viewModel.primaryBackground(course: course))
59 | .foregroundColor(Color.black)
60 | .cornerRadius(20)
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/TimeMan/Views/Components/CourseCardComponents/CourseCardComponents.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CourseCardComponents.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 10/4/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | // If Single Course Type is Present - Centered Number
12 | struct CenteredSingleCourseTypeView: View {
13 | var type: String
14 | var body: some View {
15 | HStack {
16 | Text(type)
17 | }
18 | }
19 | }
20 |
21 | // If Two Course Types are present - Numbers at the extreme ends
22 | struct ExtremeDualCourseTypeView: View {
23 | var type1: String
24 | var type2: String
25 | var body: some View {
26 | HStack {
27 | Text(type1)
28 | Spacer()
29 | Text(type2)
30 | }
31 | }
32 | }
33 |
34 | // If all three Course Types are present - Equally Spaced
35 | struct EquallySpacedCourseTypeView: View {
36 | var type1: String
37 | var type2: String
38 | var type3: String
39 | var body: some View {
40 | HStack {
41 | Text(type1)
42 | Spacer()
43 | Text(type2)
44 | Spacer()
45 | Text(type3)
46 | }
47 | }
48 | }
49 |
50 | struct CourseNumbers: View {
51 | var course: Course
52 | var body: some View {
53 | if course.lectureExists && (course.tutorialExists && course.practicalExists) {
54 | EquallySpacedCourseTypeView(
55 | type1: course.lectureNumber!,
56 | type2: course.tutorialNumber!,
57 | type3: course.practicalNumber!
58 | )
59 | } else if course.lectureExists && course.tutorialExists {
60 | ExtremeDualCourseTypeView(
61 | type1: course.lectureNumber!,
62 | type2: course.tutorialNumber!
63 | )
64 | } else if course.lectureExists && course.practicalExists {
65 | ExtremeDualCourseTypeView(
66 | type1: course.lectureNumber!,
67 | type2: course.practicalNumber!
68 | )
69 | } else if course.tutorialExists && course.practicalExists {
70 | ExtremeDualCourseTypeView(
71 | type1: course.tutorialNumber!,
72 | type2: course.practicalNumber!
73 | )
74 | } else if course.lectureExists {
75 | CenteredSingleCourseTypeView(type: course.lectureNumber!)
76 | } else if course.tutorialExists {
77 | CenteredSingleCourseTypeView(type: course.tutorialNumber!)
78 | } else if course.practicalExists {
79 | CenteredSingleCourseTypeView(type: course.practicalNumber!)
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/TimeMan/ViewModels/CourseViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CourseViewModel.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 9/23/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct WeekDayName: Hashable, Identifiable {
12 | var name: String
13 | var id: String { name }
14 | }
15 |
16 | struct ClassTypeRepeat {
17 | var name: String
18 | var weekDays: Set
19 | }
20 |
21 | class CourseViewModel: ObservableObject {
22 | // Resource for MultiSelector
23 | let weekDays: [WeekDayName] = [
24 | WeekDayName(name: "Monday"),
25 | WeekDayName(name: "Tuesday"),
26 | WeekDayName(name: "Wednesday"),
27 | WeekDayName(name: "Thursday"),
28 | WeekDayName(name: "Friday"),
29 | WeekDayName(name: "Saturday")
30 | ]
31 |
32 | // MARK: - Course Information
33 | @Published var courseTitle: String = ""
34 | @Published var courseCode: String = ""
35 | @Published var courseID: String = ""
36 | @Published var colorNum: Int = 0
37 |
38 | func generateLink(meetCode: String) -> String {
39 | let meetLinkPrefix = "https://meet.google.com/"
40 | return meetLinkPrefix + meetCode
41 | }
42 |
43 | // MARK: - Lecture Information
44 | @Published var lectureInstructorName: String = ""
45 | @Published var lectureNumber: Int = 1
46 | @Published var lectureTime = Date()
47 | @Published var lectureMeetCode: String = ""
48 | @Published var lectureRepeatWeek = ClassTypeRepeat(name: "", weekDays: [])
49 | @Published var isLectureExisting: Bool = true
50 | @Published var isLectureNotificationsEnabled: Bool = false
51 |
52 | func generateLectureNumber( lectureNumber: Int) -> String {
53 | return "L\(lectureNumber)"
54 | }
55 |
56 | // MARK: - Tutorial Information
57 | @Published var tutorialInstructorName: String = ""
58 | @Published var tutorialNumber: Int = 1
59 | @Published var tutorialTime = Date()
60 | @Published var tutorialMeetCode: String = ""
61 | @Published var tutorialRepeatWeek = ClassTypeRepeat(name: "", weekDays: [])
62 | @Published var isTutorialExisting: Bool = false
63 | @Published var isTutorialNotificationsEnabled: Bool = false
64 |
65 | func generateTutorialNumber( tutorialNumber: Int) -> String {
66 | return "L\(tutorialNumber)"
67 | }
68 |
69 | // MARK: - Practical Information
70 | @Published var practicalInstructorName: String = ""
71 | @Published var practicalNumber: Int = 1
72 | @Published var practicalTime = Date()
73 | @Published var practicalMeetCode: String = ""
74 | @Published var practicalRepeatWeek = ClassTypeRepeat(name: "", weekDays: [])
75 | @Published var isPracticalExisting: Bool = false
76 | @Published var isPracticalNotificationsEnabled: Bool = false
77 |
78 | func generatePracticalNumber( practicalNumber: Int) -> String {
79 | return "L\(practicalNumber)"
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/TimeMan/ViewModels/GridViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GridViewModel.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 10/4/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | class GridViewModel {
12 | private var colorCodes = ColorCodes()
13 | private let calendar = Calendar.current
14 |
15 | private func getClassType(course: Course) -> String {
16 | if course.isLecture {
17 | return course.lectureNumber ?? ""
18 | } else if course.isTutorial {
19 | return course.tutorialNumber ?? ""
20 | } else if course.isPractical {
21 | return course.practicalNumber ?? ""
22 | }
23 | return ""
24 | }
25 |
26 | private func shouldCourseBeIncluded(course: Course, index: Int) -> Bool {
27 | let weekDayName = longWeekDaySymbols[index]
28 | let converted = course.weekDayRepeat
29 | if converted!.contains(weekDayName) {
30 | return true
31 | }
32 | return false
33 | }
34 |
35 | func gridBackground(
36 | list: FetchedResults,
37 | gridTime: Int,
38 | weekDay: Int
39 | ) -> Color {
40 | let colorCodeIndex = self.getCourseForGrid(
41 | list: list,
42 | gridTime: gridTime,
43 | weekDay: weekDay)[2]
44 | return colorCodes.colorNumbers[Int(colorCodeIndex)!]
45 | }
46 |
47 | func getCourseForGrid(
48 | list: FetchedResults,
49 | gridTime: Int,
50 | weekDay: Int
51 | ) -> [String] {
52 | var gridCourse = Course()
53 | let gridLowerLimit = gridTime * 60
54 | let gridUpperLimit = gridLowerLimit + 60
55 | var isIncluded = false
56 |
57 | // To find if a course is at the Grid Position
58 | // If the courseTime which is in minutes is in the Grid Limits
59 | // gridCourse is assigned
60 | // isIncluded is the variable utilised to check if any course is present in that position or not
61 |
62 | for courseItem in list {
63 | if self.shouldCourseBeIncluded(course: courseItem, index: weekDay) {
64 | let courseTime = courseItem.time
65 | let courseHour = calendar.component(.hour, from: courseTime!)
66 | let courseMinute = calendar.component(.minute, from: courseTime!)
67 | let classTime = courseHour * 60 + courseMinute
68 | if classTime >= gridLowerLimit {
69 | if classTime < gridUpperLimit {
70 | gridCourse = courseItem
71 | isIncluded = true
72 | }
73 | }
74 | }
75 | }
76 | if !isIncluded {
77 | return ["Error"]
78 | }
79 | return [
80 | gridCourse.courseCode!,
81 | gridCourse.courseID!,
82 | String(gridCourse.colorNum),
83 | self.getClassType(course: gridCourse)
84 | ]
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/TimeMan/Views/Widgets/Cards/CourseCard.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CourseCard.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 9/21/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | struct CourseCard: View {
12 | var course: Course
13 | var viewModel = CourseCardViewModel()
14 | // CardView
15 | var body: some View {
16 | VStack {
17 | HStack(alignment: .top) {
18 | // Course Information
19 | VStack(alignment: .leading) {
20 | // Course Title
21 | Text(course.courseTitle!)
22 | .font(.system(size: 22, weight: .bold, design: .rounded))
23 | .padding(.bottom, 5)
24 | // Course Code + ID
25 | Text(course.courseCode! + " " + course.courseID!)
26 | .font(.system(size: 18, design: .rounded))
27 | .padding(.bottom, 10)
28 | // Instructor Name
29 | Text(course.instructorName!)
30 | .font(.system(size: 16, design: .rounded))
31 | }
32 | Spacer()
33 | // Class Time and Meet Link
34 | VStack {
35 | // Class Time
36 | Text(viewModel.classTime(course: course))
37 | .font(.system(size: 18, design: .rounded))
38 | .padding(.bottom, 20)
39 | if #available(iOS 14.0, *) {
40 | // Meet Link Button
41 | Link(destination: URL(string: course.meetLink!)!) {
42 | HStack {
43 | Text(viewModel.getClassType(course: course))
44 | .font(.largeTitle)
45 | .fontWeight(.heavy)
46 | .foregroundColor(.white)
47 | Image(systemName: "video.fill")
48 | .foregroundColor(.white)
49 | }
50 | .padding(8)
51 | .background(viewModel.secondaryBackground(course: course))
52 | .cornerRadius(15)
53 | }
54 | }
55 | }
56 | }
57 | Rectangle()
58 | .fill(Color.white)
59 | .frame(height: 2)
60 | .padding(.vertical)
61 | // All available Course Numbers
62 | CourseNumbers(course: course)
63 | }
64 | .padding(20)
65 | .background(viewModel.primaryBackground(course: course))
66 | .foregroundColor(Color.black)
67 | .cornerRadius(20)
68 | .padding(.bottom)
69 | .contextMenu {
70 | Button {
71 | viewModel.deleteItem(course: course)
72 | } label: {
73 | Text("Delete")
74 | }
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/TimeMan/Views/Components/GridComponents/GridComponents.swift:
--------------------------------------------------------------------------------
1 | //
2 | // GridComponents.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 10/4/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | enum GridValues {
12 | static let height: CGFloat = 40
13 | static let width: CGFloat = 40
14 | static let spacing: CGFloat = 8
15 | }
16 |
17 | struct GridWeekItem: View {
18 | var title: String
19 | var body: some View {
20 | VStack {
21 | Text(title)
22 | .font(.system(size: 18, weight: .bold, design: .rounded))
23 | }
24 | .frame(width: GridValues.width, height: GridValues.height)
25 | .padding(4)
26 | }
27 | }
28 |
29 | struct GridWeekRow: View {
30 | var body: some View {
31 | HStack(spacing: GridValues.spacing) {
32 | GridWeekItem(title: "")
33 | GridWeekItem(title: "M")
34 | GridWeekItem(title: "T")
35 | GridWeekItem(title: "W")
36 | GridWeekItem(title: "T")
37 | GridWeekItem(title: "F")
38 | GridWeekItem(title: "S")
39 | }
40 | }
41 | }
42 |
43 | struct Line: View {
44 | var body: some View {
45 | Rectangle()
46 | .fill(Color.gray)
47 | .frame(height: 2)
48 | }
49 | }
50 |
51 | struct MorningTimeGridListView: View {
52 | var body: some View {
53 | VStack {
54 | RowForGrid(time: 7)
55 | RowForGrid(time: 8)
56 | RowForGrid(time: 9)
57 | RowForGrid(time: 10)
58 | RowForGrid(time: 11)
59 | RowForGrid(time: 12)
60 | }
61 | }
62 | }
63 |
64 | struct EveningTimeGridListView: View {
65 | var body: some View {
66 | VStack {
67 | RowForGrid(time: 13)
68 | RowForGrid(time: 14)
69 | RowForGrid(time: 15)
70 | RowForGrid(time: 16)
71 | RowForGrid(time: 17)
72 | RowForGrid(time: 18)
73 | RowForGrid(time: 19)
74 | }
75 | }
76 | }
77 |
78 | struct TimeGrid: View {
79 | var body: some View {
80 | ScrollView {
81 | MorningTimeGridListView()
82 | EveningTimeGridListView()
83 | }
84 | }
85 | }
86 |
87 | struct RowForGrid: View {
88 | var time: Int
89 |
90 | var body: some View {
91 | VStack {
92 | Line()
93 | HStack(spacing: GridValues.spacing) {
94 | VStack(alignment: .center) {
95 | Text(String(time) + ":00")
96 | .font(.system(size: 13))
97 | }
98 | .frame(width: GridValues.width, height: GridValues.height)
99 | .padding(4)
100 | GridItem(time: time, weekDay: 1)
101 | GridItem(time: time, weekDay: 2)
102 | GridItem(time: time, weekDay: 3)
103 | GridItem(time: time, weekDay: 4)
104 | GridItem(time: time, weekDay: 5)
105 | GridItem(time: time, weekDay: 6)
106 | }
107 | }
108 | .padding(.leading, 5)
109 | .padding(.trailing, 5)
110 | }
111 | }
112 |
--------------------------------------------------------------------------------
/TimeMan/Utilities/WeekDaysUtilities.swift:
--------------------------------------------------------------------------------
1 | //
2 | // weekDaysUtilities.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 9/29/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import EventKit
10 | import Foundation
11 |
12 | var weekDaySymbols: [String] = ["Sun",
13 | "Mon",
14 | "Tue",
15 | "Wed",
16 | "Thu",
17 | "Fri",
18 | "Sat"]
19 |
20 | var longWeekDaySymbols: [String] = ["Sunday",
21 | "Monday",
22 | "Tuesday",
23 | "Wednesday",
24 | "Thursday",
25 | "Friday",
26 | "Saturday"]
27 |
28 | class WeekDayUtilities {
29 | // MARK: - Integer Mapping
30 | // Mapping the weekDayRepeat to Native Integer Indices
31 | func mapToWeekDays(weekDaySet: Set) -> [Int] {
32 | let lowerCasedSet = weekDaySet.map { $0.lowercased() }
33 | var mappedWeekDayArray: [Int] = []
34 |
35 | for weekDayName in lowerCasedSet {
36 | switch weekDayName {
37 | case "monday":
38 | mappedWeekDayArray.append(2)
39 | case "tuesday":
40 | mappedWeekDayArray.append(3)
41 | case "wednesday":
42 | mappedWeekDayArray.append(4)
43 | case "thursday":
44 | mappedWeekDayArray.append(5)
45 | case "friday":
46 | mappedWeekDayArray.append(6)
47 | case "saturday":
48 | mappedWeekDayArray.append(7)
49 | default:
50 | mappedWeekDayArray.append(1)
51 | }
52 | }
53 |
54 | return mappedWeekDayArray
55 | }
56 |
57 | // MARK: - EKRecurrenceDayOfWeek Mapping
58 | // Mapping the weekDayRepeat to EKRecurrenceDayOfWeek for Apple Event
59 | func mapToEvents(weekDaySet: Set) -> [EKRecurrenceDayOfWeek] {
60 | let lowerCasedSet = weekDaySet.map { $0.lowercased() }
61 | var mappedWeekEventArray: [EKRecurrenceDayOfWeek] = []
62 |
63 | for weekDayName in lowerCasedSet {
64 | switch weekDayName {
65 | case "monday":
66 | mappedWeekEventArray.append(EKRecurrenceDayOfWeek.init(EKWeekday.monday))
67 | case "tuesday":
68 | mappedWeekEventArray.append(EKRecurrenceDayOfWeek.init(EKWeekday.tuesday))
69 | case "wednesday":
70 | mappedWeekEventArray.append(EKRecurrenceDayOfWeek.init(EKWeekday.wednesday))
71 | case "thursday":
72 | mappedWeekEventArray.append(EKRecurrenceDayOfWeek.init(EKWeekday.thursday))
73 | case "friday":
74 | mappedWeekEventArray.append(EKRecurrenceDayOfWeek.init(EKWeekday.friday))
75 | case "saturday":
76 | mappedWeekEventArray.append(EKRecurrenceDayOfWeek.init(EKWeekday.saturday))
77 | default:
78 | mappedWeekEventArray.append(EKRecurrenceDayOfWeek.init(EKWeekday.sunday))
79 | }
80 | }
81 |
82 | return mappedWeekEventArray
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/TimeMan/SceneDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // SceneDelegate.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 9/21/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import SwiftUI
11 |
12 | class SceneDelegate: UIResponder, UIWindowSceneDelegate {
13 | var window: UIWindow?
14 |
15 | func scene(
16 | _ scene: UIScene,
17 | willConnectTo session: UISceneSession,
18 | options connectionOptions: UIScene.ConnectionOptions
19 | ) {
20 | // Use this method to optionally configure
21 | // and attach the UIWindow `window` to the provided UIWindowScene `scene`.
22 | // If using a storyboard, the `window` property will automatically be initialized and attached to the scene.
23 | // This delegate does not imply the connecting scene or session
24 | // are new (see `application:configurationForConnectingSceneSession` instead).
25 |
26 | // Get the managed object context from the shared persistent container.
27 | let managedObjectContext = (UIApplication.shared.delegate as! AppDelegate).persistentContainer.viewContext
28 |
29 | // Create the SwiftUI view and set the context as the value for the managedObjectContext environment keyPath.
30 | // Add `@Environment(\.managedObjectContext)` in the views that will need the context.
31 | let contentView = ContentView().environment(\.managedObjectContext, managedObjectContext)
32 |
33 | // Use a UIHostingController as window root view controller.
34 | if let windowScene = scene as? UIWindowScene {
35 | let window = UIWindow(windowScene: windowScene)
36 | window.rootViewController = UIHostingController(rootView: contentView)
37 | self.window = window
38 | window.makeKeyAndVisible()
39 | }
40 | }
41 |
42 | func sceneDidDisconnect(_ scene: UIScene) {
43 | // Called as the scene is being released by the system.
44 | // This occurs shortly after the scene enters the background, or when its session is discarded.
45 | // Release any resources associated with this scene that can be re-created the next time the scene connects.
46 | // The scene may re-connect later, as its session was not neccessarily discarded
47 | // (see `application:didDiscardSceneSessions` instead).
48 | }
49 |
50 | func sceneDidBecomeActive(_ scene: UIScene) {
51 | // Called when the scene has moved from an inactive state to an active state.
52 | // Use this method to restart any tasks that were paused (or not yet started) when the scene was inactive.
53 | }
54 |
55 | func sceneWillResignActive(_ scene: UIScene) {
56 | // Called when the scene will move from an active state to an inactive state.
57 | // This may occur due to temporary interruptions (ex. an incoming phone call).
58 | }
59 |
60 | func sceneWillEnterForeground(_ scene: UIScene) {
61 | // Called as the scene transitions from the background to the foreground.
62 | // Use this method to undo the changes made on entering the background.
63 | }
64 |
65 | func sceneDidEnterBackground(_ scene: UIScene) {
66 | // Called as the scene transitions from the foreground to the background.
67 | // Use this method to save data, release shared resources, and store enough scene-specific state information
68 | // to restore the scene back to its current state.
69 |
70 | // Save changes in the application's managed object context when the application transitions to the background.
71 | (UIApplication.shared.delegate as? AppDelegate)?.saveContext()
72 | }
73 | }
74 |
--------------------------------------------------------------------------------
/TimeMan/AppDelegate.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppDelegate.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 9/21/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import UIKit
10 | import CoreData
11 |
12 | @UIApplicationMain
13 | class AppDelegate: UIResponder, UIApplicationDelegate {
14 | var window: UIWindow?
15 | private func requestNotificationsAuthorization(application: UIApplication) {
16 | let center = UNUserNotificationCenter.current()
17 | let options: UNAuthorizationOptions = [.alert, .sound, .badge]
18 | center.requestAuthorization(options: options) { _, error in
19 | if let error = error {
20 | print(error.localizedDescription)
21 | }
22 | }
23 | }
24 |
25 | func application(
26 | _ application: UIApplication,
27 | didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?
28 | ) -> Bool {
29 | // Override point for customization after application launch.
30 | return true
31 | }
32 |
33 | // MARK: UISceneSession Lifecycle
34 |
35 | func application(
36 | _ application: UIApplication,
37 | configurationForConnecting connectingSceneSession: UISceneSession,
38 | options: UIScene.ConnectionOptions
39 | ) -> UISceneConfiguration {
40 | // Called when a new scene session is being created.
41 | // Use this method to select a configuration to create the new scene with.
42 | return UISceneConfiguration(name: "Default Configuration", sessionRole: connectingSceneSession.role)
43 | }
44 |
45 | func application(_ application: UIApplication, didDiscardSceneSessions sceneSessions: Set) {
46 | // Called when the user discards a scene session.
47 | // If any sessions were discarded while the application was not running,
48 | // this will be called shortly after application:didFinishLaunchingWithOptions.
49 | // Use this method to release any resources that were specific to the discarded scenes, as they will not return.
50 | }
51 |
52 | // MARK: - Core Data stack
53 |
54 | lazy var persistentContainer: NSPersistentContainer = {
55 | /*
56 | The persistent container for the application. This implementation
57 | creates and returns a container, having loaded the store for the
58 | application to it. This property is optional since there are legitimate
59 | error conditions that could cause the creation of the store to fail.
60 | */
61 | let container = NSPersistentContainer(name: "TimeMan")
62 | container.loadPersistentStores { storeDescription, error in
63 | if let error = error as NSError? {
64 | fatalError("Unresolved error \(error), \(error.userInfo)")
65 | }
66 | }
67 | return container
68 | }()
69 |
70 | // MARK: - Core Data Saving support
71 |
72 | func saveContext () {
73 | let context = persistentContainer.viewContext
74 | if context.hasChanges {
75 | do {
76 | try context.save()
77 | } catch {
78 | // Replace this implementation with code to handle the error appropriately.
79 | // fatalError() causes the application to generate a crash log and terminate.
80 | // You should not use this function in a shipping application,
81 | // although it may be useful during development.
82 | let nserror = error as NSError
83 | fatalError("Unresolved error \(nserror), \(nserror.userInfo)")
84 | }
85 | }
86 | }
87 | }
88 |
--------------------------------------------------------------------------------
/TimeMan/ViewModels/UpcomingClassViewModel.swift:
--------------------------------------------------------------------------------
1 | //
2 | // UpcomingClassViewModel.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 10/2/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import SwiftUI
10 |
11 | class UpcomingClassViewModel {
12 | @Environment(\.managedObjectContext) var managedObjectContext
13 | private let calendar = Calendar.current
14 |
15 | private func getClassType(course: Course) -> String {
16 | if course.isLecture {
17 | return course.lectureNumber!
18 | } else if course.isTutorial {
19 | return course.tutorialNumber!
20 | } else if course.isPractical {
21 | return course.practicalNumber!
22 | }
23 | return ""
24 | }
25 |
26 | private func shouldCourseBeIncluded(course: Course, weekDayindex: Int) -> Bool {
27 | let weekDayName = longWeekDaySymbols[weekDayindex]
28 | let converted = course.weekDayRepeat
29 | if converted!.contains(weekDayName) {
30 | return true
31 | }
32 | return false
33 | }
34 |
35 | // MARK: - Error Course
36 | private var errorCourse: Course {
37 | let errorCourse = Course(context: self.managedObjectContext)
38 | errorCourse.courseTitle = "No upcoming Classes"
39 | errorCourse.courseID = "E"
40 | errorCourse.courseCode = "E"
41 | errorCourse.instructorName = "E"
42 | errorCourse.weekDayRepeat = ["Monday"]
43 | errorCourse.meetLink = "E"
44 | errorCourse.lectureNumber = "E"
45 | errorCourse.tutorialNumber = "E"
46 | errorCourse.practicalNumber = "E"
47 | errorCourse.isLecture = true
48 | errorCourse.isTutorial = false
49 | errorCourse.isPractical = false
50 | errorCourse.lectureExists = true
51 | errorCourse.tutorialExists = true
52 | errorCourse.practicalExists = true
53 | errorCourse.time = Date()
54 | errorCourse.colorNum = 0
55 |
56 | return errorCourse
57 | }
58 |
59 | func areUpcomingClassesAvailable(list: FetchedResults) -> Bool {
60 | return getUpcomingClass(list: list).courseID != "E"
61 | }
62 |
63 | func getUpcomingClass(list: FetchedResults) -> Course {
64 | let courseslist = list
65 | var upcomingCourse = Course()
66 | // Procedure followed to find the upcoming classes
67 |
68 | // We calculate the Current Time and Course Time in minutes
69 | // We find the difference between the Course Time and Current Time
70 | // Difference > 0 => Course Time is ahead of the Current Time
71 | // We loop across all the courses that are happening on the Current Day using the shouldCourseBeIncluded method
72 | // We find the minimum difference to find out which course is the upcoming course
73 | // areUpcomingClassesAvailable is set to true when there is atleast one course upcoming
74 |
75 | let currentTimeHour = calendar.component(.hour, from: Date())
76 | let currentTimeMinute = calendar.component(.minute, from: Date())
77 | let currentTime = currentTimeHour * 60 + currentTimeMinute
78 | var minimumDifference = 1440
79 | var areUpcomingClassesAvailable = false
80 | let currentDayIndex = (Calendar.current.component(.weekday, from: Date())) - 1
81 |
82 | for courseClass in courseslist {
83 | if shouldCourseBeIncluded(course: courseClass, weekDayindex: currentDayIndex) {
84 | let courseClassTime = courseClass.time
85 | let courseHour = calendar.component(.hour, from: courseClassTime!)
86 | let courseMinute = calendar.component(.minute, from: courseClassTime!)
87 | let courseTime = courseHour * 60 + courseMinute
88 | let difference = courseTime - currentTime
89 | if difference > 0 {
90 | if difference < minimumDifference {
91 | minimumDifference = difference
92 | upcomingCourse = courseClass
93 | areUpcomingClassesAvailable = true
94 | }
95 | }
96 | }
97 | }
98 |
99 | guard areUpcomingClassesAvailable else {
100 | return errorCourse
101 | }
102 | return upcomingCourse
103 | }
104 | }
105 |
--------------------------------------------------------------------------------
/TimeMan/Services/AppleEvents.swift:
--------------------------------------------------------------------------------
1 | //
2 | // AppleEvents.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 9/29/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import EventKit
10 | import SwiftUI
11 |
12 | class AppleEvents {
13 | private let utils = WeekDayUtilities()
14 | private let eventStore = EKEventStore()
15 |
16 | // MARK: - Save Event
17 | // Method used to save event to Apple Calendar
18 | // Params include all the content pertaining to the event
19 | // If the event "isPractical" end Time increases from 50 min to 1hr50min
20 |
21 | private func saveEvent(
22 | title: String,
23 | startDate: Date,
24 | notes: String,
25 | eventStore: EKEventStore,
26 | weekDayRepeat: Set,
27 | isPractical: Bool
28 | ) {
29 | // Content for the Event
30 | let event = EKEvent(eventStore: eventStore)
31 | event.title = title
32 | event.startDate = startDate
33 | event.endDate = isPractical ? startDate.addingTimeInterval(6600) : startDate.addingTimeInterval(3000)
34 | event.notes = notes
35 |
36 | // Reccurence Rule for the event
37 | // Repeats every week on the given Reccurence Day of the Week
38 | // Recurrence Day is given by mapping the repeat to EKRecurrenceDayOfWeek
39 |
40 | let mappedWeekDayArray: [EKRecurrenceDayOfWeek] = utils.mapToEvents(weekDaySet: weekDayRepeat)
41 | let recurrenceRule = EKRecurrenceRule.init(recurrenceWith: .daily,
42 | interval: 1,
43 | daysOfTheWeek: mappedWeekDayArray,
44 | daysOfTheMonth: nil,
45 | monthsOfTheYear: nil,
46 | weeksOfTheYear: nil,
47 | daysOfTheYear: nil,
48 | setPositions: nil,
49 | end: EKRecurrenceEnd.init(occurrenceCount: 200))
50 |
51 | event.recurrenceRules = [recurrenceRule]
52 | event.calendar = eventStore.defaultCalendarForNewEvents
53 |
54 | // Save
55 | do {
56 | try eventStore.save(event, span: .thisEvent)
57 | } catch let error as NSError {
58 | print("error : \(error)")
59 | }
60 | }
61 |
62 | // MARK: - Lecture Event
63 | // Public method used to store Lecture Event using the main save event method
64 | // We check for the access and store the event in Apple Calendar
65 |
66 | func addLecture(
67 | lectureRepeat: Set,
68 | title: String,
69 | startDate: Date,
70 | notes: String
71 | ) {
72 | eventStore.requestAccess(to: .event) { granted, error in
73 | if granted && error == nil {
74 | self.saveEvent(
75 | title: title,
76 | startDate: startDate,
77 | notes: notes,
78 | eventStore: self.eventStore,
79 | weekDayRepeat: lectureRepeat,
80 | isPractical: false)
81 | } else {
82 | print("error : \(String(describing: error))")
83 | }
84 | }
85 | }
86 |
87 | // MARK: - Tutorial Event
88 | // Public method used to store Tutorial Event using the main save event method
89 | // We check for the access and store the event in Apple Calendar
90 |
91 | func addTutorial(
92 | tutorialRepeat: Set,
93 | title: String,
94 | startDate: Date,
95 | notes: String
96 | ) {
97 | eventStore.requestAccess(to: .event) { granted, error in
98 | if granted && error == nil {
99 | self.saveEvent(
100 | title: title,
101 | startDate: startDate,
102 | notes: notes,
103 | eventStore: self.eventStore,
104 | weekDayRepeat: tutorialRepeat,
105 | isPractical: false)
106 | } else {
107 | print("error : \(String(describing: error))")
108 | }
109 | }
110 | }
111 |
112 | // MARK: - Practical Event
113 | // Public method used to store Practical Event using the main save event method
114 | // We check for the access and store the event in Apple Calendar
115 |
116 | func addPractical(
117 | practicalRepeat: Set,
118 | title: String,
119 | startDate: Date,
120 | notes: String
121 | ) {
122 | eventStore.requestAccess(to: .event) { granted, error in
123 | if granted && error == nil {
124 | self.saveEvent(
125 | title: title,
126 | startDate: startDate,
127 | notes: notes,
128 | eventStore: self.eventStore,
129 | weekDayRepeat: practicalRepeat,
130 | isPractical: true)
131 | } else {
132 | print("error : \(String(describing: error))")
133 | }
134 | }
135 | }
136 | }
137 |
--------------------------------------------------------------------------------
/TimeMan/Views/Screens/CourseInput.swift:
--------------------------------------------------------------------------------
1 | //
2 | // CourseInput.swift
3 | // TimeMan
4 | //
5 | // Created by Sai Ankit on 9/22/20.
6 | // Copyright © 2020 Sai Ankit. All rights reserved.
7 | //
8 |
9 | import EventKit
10 | import SwiftUI
11 |
12 | struct CourseInput: View {
13 | @Environment(\.managedObjectContext) var managedObjectContext
14 | @ObservedObject var viewModel = CourseViewModel()
15 | @Binding var isPresented: Bool
16 | private let colorCodes = ColorCodes()
17 | private let appleEvents = AppleEvents()
18 | private let notificationManager = LocalNotificationManager()
19 |
20 | var body: some View {
21 | NavigationView {
22 | Form {
23 | Section(header: Text("Course Information")) {
24 | TextField("Course Title ( Digital Design )", text: $viewModel.courseTitle)
25 |
26 | TextField("Course Code ( ECE )", text: $viewModel.courseCode)
27 |
28 | TextField("Course ID ( F215 )", text: $viewModel.courseID)
29 | }
30 | // MARK: - Lecture
31 | Toggle(isOn: $viewModel.isLectureExisting) {
32 | Text("Lecture")
33 | }
34 | if viewModel.isLectureExisting {
35 | Section(header: Text("Lecture Information")) {
36 | TextField("Lecture Instructor", text: $viewModel.lectureInstructorName)
37 | Stepper(value: $viewModel.lectureNumber, in: 1...10) {
38 | Text("L" + String(viewModel.lectureNumber))
39 | }
40 | DatePicker(
41 | "Lecture Time",
42 | selection: $viewModel.lectureTime,
43 | displayedComponents: .hourAndMinute)
44 | TextField("Lecture Meet Code", text: $viewModel.lectureMeetCode)
45 | MultiSelector(
46 | label: Text("WeekDay Repeat"),
47 | options: viewModel.weekDays,
48 | optionToString: { $0.name },
49 | selected: $viewModel.lectureRepeatWeek.weekDays
50 | )
51 | Toggle(isOn: $viewModel.isLectureNotificationsEnabled) {
52 | Text("Enable Lecture Notifications")
53 | }
54 | }
55 | }
56 |
57 | // MARK: - Tutorial
58 | Toggle(isOn: $viewModel.isTutorialExisting) {
59 | Text("Tutorial")
60 | }
61 | if viewModel.isTutorialExisting {
62 | Section(header: Text("Tutorial Information")) {
63 | TextField("Tutorial Instructor", text: $viewModel.tutorialInstructorName)
64 | Stepper(value: $viewModel.tutorialNumber, in: 1...10) {
65 | Text("T" + String(viewModel.tutorialNumber))
66 | }
67 | DatePicker("Tutorial Time",
68 | selection: $viewModel.tutorialTime,
69 | displayedComponents: .hourAndMinute)
70 | TextField("Tutorial Meet Code", text: $viewModel.tutorialMeetCode)
71 | MultiSelector(
72 | label: Text("WeekDay Repeat"),
73 | options: viewModel.weekDays,
74 | optionToString: { $0.name },
75 | selected: $viewModel.tutorialRepeatWeek.weekDays
76 | )
77 | Toggle(isOn: $viewModel.isTutorialNotificationsEnabled) {
78 | Text("Enable Tutorial Notifications")
79 | }
80 | }
81 | }
82 |
83 | // MARK: - Practical
84 | Toggle(isOn: $viewModel.isPracticalExisting) {
85 | Text("Practical")
86 | }
87 | if viewModel.isPracticalExisting {
88 | Section(header: Text("Practical Information")) {
89 | TextField("Practical Instructor", text: $viewModel.practicalInstructorName)
90 | Stepper(value: $viewModel.practicalNumber, in: 1...10) {
91 | Text("P" + String(viewModel.practicalNumber))
92 | }
93 | DatePicker("Practical Time",
94 | selection: $viewModel.practicalTime,
95 | displayedComponents: .hourAndMinute)
96 | TextField("Practical Meet Code", text: $viewModel.practicalMeetCode)
97 | MultiSelector(
98 | label: Text("WeekDay Repeat"),
99 | options: viewModel.weekDays,
100 | optionToString: { $0.name },
101 | selected: $viewModel.practicalRepeatWeek.weekDays
102 | )
103 | Toggle(isOn: $viewModel.isPracticalNotificationsEnabled) {
104 | Text("Enable Practical Notifications")
105 | }
106 | }
107 | }
108 |
109 | // MARK: - Color Coding
110 | Section {
111 | Picker(selection: $viewModel.colorNum, label: Text("Color Code")) {
112 | ForEach(0 ..< colorCodes.colorNumbers.count) {
113 | Text(colorCodes.colorNames[ $0 ]).foregroundColor(colorCodes.colorNumbers[ $0 ])
114 | }
115 | }
116 | }
117 |
118 | // MARK: - Add course
119 | Section {
120 | Button {
121 | if viewModel.isLectureExisting {
122 | let mappedLectureRepeatWeek = Set(viewModel.lectureRepeatWeek.weekDays.map { $0.name })
123 | let newLecture = Course(context: self.managedObjectContext)
124 | newLecture.courseTitle = viewModel.courseTitle
125 | newLecture.courseID = viewModel.courseID
126 | newLecture.courseCode = viewModel.courseCode
127 | newLecture.instructorName = viewModel.lectureInstructorName
128 | newLecture.weekDayRepeat = mappedLectureRepeatWeek
129 | newLecture.meetLink = viewModel.generateLink(meetCode: viewModel.lectureMeetCode)
130 | newLecture.lectureNumber = viewModel.generateLectureNumber(
131 | lectureNumber: viewModel.lectureNumber)
132 | newLecture.tutorialNumber = viewModel.generateTutorialNumber(
133 | tutorialNumber: viewModel.tutorialNumber)
134 | newLecture.practicalNumber = viewModel.generatePracticalNumber(
135 | practicalNumber: viewModel.practicalNumber)
136 | newLecture.isLecture = true
137 | newLecture.isTutorial = false
138 | newLecture.isPractical = false
139 | newLecture.lectureExists = viewModel.isLectureExisting
140 | newLecture.tutorialExists = viewModel.isTutorialExisting
141 | newLecture.practicalExists = viewModel.isPracticalExisting
142 | newLecture.time = viewModel.lectureTime
143 | newLecture.colorNum = Int16(viewModel.colorNum)
144 |
145 | // Saving Lecture to CoreData
146 | do {
147 | try self.managedObjectContext.save()
148 | } catch {
149 | print(error)
150 | }
151 |
152 | // Adding Lecture to Apple Calendar
153 | let metaData = viewModel.courseTitle + " " + viewModel.courseCode + " "
154 | + viewModel.courseID + " " + viewModel.generateLectureNumber(
155 | lectureNumber: viewModel.lectureNumber)
156 | + " Instructor Name: " + viewModel.lectureInstructorName
157 | appleEvents.addLecture(
158 | lectureRepeat: mappedLectureRepeatWeek,
159 | title: viewModel.courseTitle + " " + viewModel.generateLectureNumber(
160 | lectureNumber: viewModel.lectureNumber),
161 | startDate: viewModel.lectureTime,
162 | notes: metaData)
163 |
164 | if viewModel.isLectureNotificationsEnabled {
165 | // Scheduling Notifications for Lecture
166 | let notificationTitle = viewModel.courseCode + " " + viewModel.courseID + " "
167 | + "L" + String(viewModel.lectureNumber)
168 | notificationManager.scheduleNotification(
169 | title: notificationTitle,
170 | subtitle: viewModel.courseTitle,
171 | body: "Lecture in 10 min",
172 | time: viewModel.lectureTime,
173 | weekRepeat: mappedLectureRepeatWeek)
174 | }
175 | }
176 | if viewModel.isTutorialExisting {
177 | let mappedTutorialRepeatWeek = Set(viewModel.tutorialRepeatWeek.weekDays.map { $0.name })
178 | let newTutorial = Course(context: self.managedObjectContext)
179 | newTutorial.courseTitle = viewModel.courseTitle
180 | newTutorial.courseID = viewModel.courseID
181 | newTutorial.courseCode = viewModel.courseCode
182 | newTutorial.instructorName = viewModel.tutorialInstructorName
183 | newTutorial.weekDayRepeat = mappedTutorialRepeatWeek
184 | newTutorial.meetLink = viewModel.generateLink(meetCode: viewModel.tutorialMeetCode)
185 | newTutorial.lectureNumber = viewModel.generateLectureNumber(
186 | lectureNumber: viewModel.lectureNumber)
187 | newTutorial.tutorialNumber = viewModel.generateTutorialNumber(
188 | tutorialNumber: viewModel.tutorialNumber)
189 | newTutorial.practicalNumber = viewModel.generatePracticalNumber(
190 | practicalNumber: viewModel.practicalNumber)
191 | newTutorial.isLecture = false
192 | newTutorial.isTutorial = true
193 | newTutorial.isPractical = false
194 | newTutorial.lectureExists = viewModel.isLectureExisting
195 | newTutorial.tutorialExists = viewModel.isTutorialExisting
196 | newTutorial.practicalExists = viewModel.isPracticalExisting
197 | newTutorial.time = viewModel.tutorialTime
198 | newTutorial.colorNum = Int16(viewModel.colorNum)
199 |
200 | // Saving Tutorial to CoreData
201 | do {
202 | try self.managedObjectContext.save()
203 | } catch {
204 | print(error)
205 | }
206 |
207 | // Adding Tutorial to Apple Calendar
208 | let metaData = viewModel.courseTitle + viewModel.courseCode + " " + viewModel.courseID + " "
209 | + viewModel.generateTutorialNumber(tutorialNumber: viewModel.tutorialNumber)
210 | + " Instructor Name: " + viewModel.lectureInstructorName
211 | appleEvents.addTutorial(
212 | tutorialRepeat: mappedTutorialRepeatWeek,
213 | title: viewModel.courseTitle + " "
214 | + viewModel.generateTutorialNumber(tutorialNumber: viewModel.tutorialNumber),
215 | startDate: viewModel.tutorialTime,
216 | notes: metaData)
217 |
218 | if viewModel.isTutorialNotificationsEnabled {
219 | // Scheduling Notifications for Tutorial
220 | let notificationTitle = viewModel.courseCode + " "
221 | + viewModel.courseID + " "
222 | + viewModel.generateTutorialNumber(tutorialNumber: viewModel.tutorialNumber)
223 | notificationManager.scheduleNotification(
224 | title: notificationTitle,
225 | subtitle: viewModel.courseTitle,
226 | body: "Tutorial in 10 min",
227 | time: viewModel.tutorialTime,
228 | weekRepeat: mappedTutorialRepeatWeek)
229 | }
230 | }
231 |
232 | if viewModel.isPracticalExisting {
233 | let mappedPracticalRepeatWeek = Set(viewModel.practicalRepeatWeek.weekDays.map { $0.name })
234 | let newPractical = Course(context: self.managedObjectContext)
235 | newPractical.courseTitle = viewModel.courseTitle
236 | newPractical.courseID = viewModel.courseID
237 | newPractical.courseCode = viewModel.courseCode
238 | newPractical.instructorName = viewModel.practicalInstructorName
239 | newPractical.weekDayRepeat = mappedPracticalRepeatWeek
240 | newPractical.meetLink = viewModel.generateLink(meetCode: viewModel.practicalMeetCode)
241 | newPractical.lectureNumber = viewModel.generateLectureNumber(
242 | lectureNumber: viewModel.lectureNumber)
243 | newPractical.tutorialNumber = viewModel.generateTutorialNumber(
244 | tutorialNumber: viewModel.tutorialNumber)
245 | newPractical.practicalNumber = viewModel.generatePracticalNumber(
246 | practicalNumber: viewModel.practicalNumber)
247 | newPractical.isLecture = false
248 | newPractical.isTutorial = false
249 | newPractical.isPractical = true
250 | newPractical.lectureExists = viewModel.isLectureExisting
251 | newPractical.tutorialExists = viewModel.isTutorialExisting
252 | newPractical.practicalExists = viewModel.isPracticalExisting
253 | newPractical.time = viewModel.practicalTime
254 | newPractical.colorNum = Int16(viewModel.colorNum)
255 |
256 | // Saving Practical to CoreData
257 | do {
258 | try self.managedObjectContext.save()
259 | } catch {
260 | print(error)
261 | }
262 |
263 | // Adding Practical to Apple Calendar
264 | let metaData = viewModel.courseTitle + viewModel.courseCode + " " + viewModel.courseID + " "
265 | + viewModel.generatePracticalNumber(practicalNumber: viewModel.practicalNumber)
266 | + " Instructor Name: " + viewModel.lectureInstructorName
267 | appleEvents.addPractical(
268 | practicalRepeat: mappedPracticalRepeatWeek,
269 | title: viewModel.courseTitle + " "
270 | + viewModel.generatePracticalNumber(practicalNumber: viewModel.practicalNumber),
271 | startDate: viewModel.practicalTime,
272 | notes: metaData)
273 |
274 | if viewModel.isPracticalNotificationsEnabled {
275 | // Scheduling Notifications for Practical
276 | let notificationTitle = viewModel.courseCode + " "
277 | + viewModel.courseID + " "
278 | + viewModel.generatePracticalNumber(practicalNumber: viewModel.practicalNumber)
279 | notificationManager.scheduleNotification(
280 | title: notificationTitle,
281 | subtitle: viewModel.courseTitle,
282 | body: "Practical in 10 min",
283 | time: viewModel.practicalTime,
284 | weekRepeat: mappedPracticalRepeatWeek)
285 | }
286 | }
287 |
288 | self.isPresented.toggle()
289 | } label: {
290 | Text("Add Course")
291 | }
292 | }
293 | }
294 | .navigationBarTitle(Text("Add Course"), displayMode: .inline)
295 | }
296 | }
297 | }
298 |
--------------------------------------------------------------------------------
/TimeMan.xcodeproj/project.pbxproj:
--------------------------------------------------------------------------------
1 | // !$*UTF8*$!
2 | {
3 | archiveVersion = 1;
4 | classes = {
5 | };
6 | objectVersion = 50;
7 | objects = {
8 |
9 | /* Begin PBXBuildFile section */
10 | 6C04E3C0251A17A70018BD77 /* CourseInput.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C04E3BF251A17A70018BD77 /* CourseInput.swift */; };
11 | 6C1444A3251DE18500CFA2BF /* Header.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C1444A2251DE18500CFA2BF /* Header.swift */; };
12 | 6C15DDA42525E0530068BCD8 /* Course+CoreDataClass.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C15DDA22525E0530068BCD8 /* Course+CoreDataClass.swift */; };
13 | 6C15DDA52525E0530068BCD8 /* Course+CoreDataProperties.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C15DDA32525E0530068BCD8 /* Course+CoreDataProperties.swift */; };
14 | 6C169CC02532A43000E7521E /* CourseCardViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C169CBF2532A43000E7521E /* CourseCardViewModel.swift */; };
15 | 6C169CC42532AADC00E7521E /* UpcomingCourseCardViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C169CC32532AADC00E7521E /* UpcomingCourseCardViewModel.swift */; };
16 | 6C16F6E5251904A10010857F /* CourseCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C16F6E4251904A10010857F /* CourseCard.swift */; };
17 | 6C3EA3DB2529BEAD00D84CA1 /* GridViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C3EA3DA2529BEAD00D84CA1 /* GridViewModel.swift */; };
18 | 6C3EA3E02529C32E00D84CA1 /* GridComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C3EA3DF2529C32E00D84CA1 /* GridComponents.swift */; };
19 | 6C4589B52519B7370023B4BD /* WeekScroll.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C4589B42519B7370023B4BD /* WeekScroll.swift */; };
20 | 6C4589B82519B7A70023B4BD /* CalendarItem.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C4589B72519B7A70023B4BD /* CalendarItem.swift */; };
21 | 6C4F41EB2531E7FC005EDD61 /* .swiftlint.yml in Resources */ = {isa = PBXBuildFile; fileRef = 6C4F41EA2531E7FC005EDD61 /* .swiftlint.yml */; };
22 | 6C5FB76A252788B500534BC9 /* UpcomingClasses.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C5FB769252788B500534BC9 /* UpcomingClasses.swift */; };
23 | 6C5FB76D252788DA00534BC9 /* UpcomingClassViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C5FB76C252788DA00534BC9 /* UpcomingClassViewModel.swift */; };
24 | 6C8621E22529ED4700F6A9E4 /* FloatingActionButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8621E12529ED4700F6A9E4 /* FloatingActionButton.swift */; };
25 | 6C8621E62529EE7100F6A9E4 /* CourseCardComponents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8621E52529EE7100F6A9E4 /* CourseCardComponents.swift */; };
26 | 6C8621EE2529F39F00F6A9E4 /* CourseListViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8621ED2529F39F00F6A9E4 /* CourseListViewModel.swift */; };
27 | 6C8C34C8253171E300E9A164 /* ColorCodes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8C34C7253171E300E9A164 /* ColorCodes.swift */; };
28 | 6C8C34CC2531824400E9A164 /* DateTimeUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8C34CB2531824400E9A164 /* DateTimeUtilities.swift */; };
29 | 6C8DDFDA252850550055B7DB /* HomeScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8DDFD9252850550055B7DB /* HomeScreen.swift */; };
30 | 6C8DDFDD252850BF0055B7DB /* ScrollScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8DDFDC252850BF0055B7DB /* ScrollScreen.swift */; };
31 | 6C8DDFE0252850CE0055B7DB /* GridScreen.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8DDFDF252850CE0055B7DB /* GridScreen.swift */; };
32 | 6C8DDFE325286FEC0055B7DB /* CustomShapeTriangle.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8DDFE225286FEC0055B7DB /* CustomShapeTriangle.swift */; };
33 | 6C8DDFE725287B9F0055B7DB /* CustomRoundedEdgesShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8DDFE625287B9F0055B7DB /* CustomRoundedEdgesShape.swift */; };
34 | 6C8DDFEB252889A20055B7DB /* UpcomingCourseCard.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6C8DDFEA252889A20055B7DB /* UpcomingCourseCard.swift */; };
35 | 6CABB8CE253EB09800024525 /* LocalNotificationManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CABB8CD253EB09800024525 /* LocalNotificationManager.swift */; };
36 | 6CABB8D2253EB09900024525 /* WeekDaysUtilities.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CABB8D1253EB09900024525 /* WeekDaysUtilities.swift */; };
37 | 6CABB8D5253EB09900024525 /* AppleEvents.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CABB8D4253EB09900024525 /* AppleEvents.swift */; };
38 | 6CB2C3E6251CAB4000D70E8A /* MultiSelector.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CB2C3E5251CAB4000D70E8A /* MultiSelector.swift */; };
39 | 6CB2C3E9251CAC1400D70E8A /* MultiSelectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CB2C3E8251CAC1400D70E8A /* MultiSelectionView.swift */; };
40 | 6CD5FF75251B6E7E00DBB204 /* CourseViewModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CD5FF74251B6E7E00DBB204 /* CourseViewModel.swift */; };
41 | 6CD7247A252834F700ED0EB0 /* TabBar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CD72479252834F700ED0EB0 /* TabBar.swift */; };
42 | 6CE81F932520966D00E6D390 /* CoursesList.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CE81F922520966D00E6D390 /* CoursesList.swift */; };
43 | 6CF634162518B812005F022D /* AppDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF634152518B812005F022D /* AppDelegate.swift */; };
44 | 6CF634182518B812005F022D /* SceneDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF634172518B812005F022D /* SceneDelegate.swift */; };
45 | 6CF6341B2518B812005F022D /* TimeMan.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 6CF634192518B812005F022D /* TimeMan.xcdatamodeld */; };
46 | 6CF6341D2518B812005F022D /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6CF6341C2518B812005F022D /* ContentView.swift */; };
47 | 6CF6341F2518B815005F022D /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6CF6341E2518B815005F022D /* Assets.xcassets */; };
48 | 6CF634222518B815005F022D /* Preview Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 6CF634212518B815005F022D /* Preview Assets.xcassets */; };
49 | 6CF634252518B815005F022D /* LaunchScreen.storyboard in Resources */ = {isa = PBXBuildFile; fileRef = 6CF634232518B815005F022D /* LaunchScreen.storyboard */; };
50 | /* End PBXBuildFile section */
51 |
52 | /* Begin PBXCopyFilesBuildPhase section */
53 | 6C8DE0032528A7580055B7DB /* Embed App Extensions */ = {
54 | isa = PBXCopyFilesBuildPhase;
55 | buildActionMask = 2147483647;
56 | dstPath = "";
57 | dstSubfolderSpec = 13;
58 | files = (
59 | );
60 | name = "Embed App Extensions";
61 | runOnlyForDeploymentPostprocessing = 0;
62 | };
63 | /* End PBXCopyFilesBuildPhase section */
64 |
65 | /* Begin PBXFileReference section */
66 | 6C04E3BF251A17A70018BD77 /* CourseInput.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseInput.swift; sourceTree = ""; };
67 | 6C1444A2251DE18500CFA2BF /* Header.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Header.swift; sourceTree = ""; };
68 | 6C15DDA22525E0530068BCD8 /* Course+CoreDataClass.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Course+CoreDataClass.swift"; sourceTree = ""; };
69 | 6C15DDA32525E0530068BCD8 /* Course+CoreDataProperties.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "Course+CoreDataProperties.swift"; sourceTree = ""; };
70 | 6C169CBF2532A43000E7521E /* CourseCardViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseCardViewModel.swift; sourceTree = ""; };
71 | 6C169CC32532AADC00E7521E /* UpcomingCourseCardViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpcomingCourseCardViewModel.swift; sourceTree = ""; };
72 | 6C16F6E4251904A10010857F /* CourseCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseCard.swift; sourceTree = ""; };
73 | 6C3EA3DA2529BEAD00D84CA1 /* GridViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GridViewModel.swift; sourceTree = ""; };
74 | 6C3EA3DF2529C32E00D84CA1 /* GridComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GridComponents.swift; sourceTree = ""; };
75 | 6C4589B42519B7370023B4BD /* WeekScroll.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekScroll.swift; sourceTree = ""; };
76 | 6C4589B72519B7A70023B4BD /* CalendarItem.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CalendarItem.swift; sourceTree = ""; };
77 | 6C4F41EA2531E7FC005EDD61 /* .swiftlint.yml */ = {isa = PBXFileReference; lastKnownFileType = text.yaml; path = .swiftlint.yml; sourceTree = ""; };
78 | 6C5FB769252788B500534BC9 /* UpcomingClasses.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpcomingClasses.swift; sourceTree = ""; };
79 | 6C5FB76C252788DA00534BC9 /* UpcomingClassViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpcomingClassViewModel.swift; sourceTree = ""; };
80 | 6C8621E12529ED4700F6A9E4 /* FloatingActionButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FloatingActionButton.swift; sourceTree = ""; };
81 | 6C8621E52529EE7100F6A9E4 /* CourseCardComponents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseCardComponents.swift; sourceTree = ""; };
82 | 6C8621ED2529F39F00F6A9E4 /* CourseListViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseListViewModel.swift; sourceTree = ""; };
83 | 6C8C34C7253171E300E9A164 /* ColorCodes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ColorCodes.swift; sourceTree = ""; };
84 | 6C8C34CB2531824400E9A164 /* DateTimeUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DateTimeUtilities.swift; sourceTree = ""; };
85 | 6C8DDFD9252850550055B7DB /* HomeScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HomeScreen.swift; sourceTree = ""; };
86 | 6C8DDFDC252850BF0055B7DB /* ScrollScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScrollScreen.swift; sourceTree = ""; };
87 | 6C8DDFDF252850CE0055B7DB /* GridScreen.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GridScreen.swift; sourceTree = ""; };
88 | 6C8DDFE225286FEC0055B7DB /* CustomShapeTriangle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomShapeTriangle.swift; sourceTree = ""; };
89 | 6C8DDFE625287B9F0055B7DB /* CustomRoundedEdgesShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomRoundedEdgesShape.swift; sourceTree = ""; };
90 | 6C8DDFEA252889A20055B7DB /* UpcomingCourseCard.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UpcomingCourseCard.swift; sourceTree = ""; };
91 | 6C8DDFF32528A7530055B7DB /* WidgetKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = WidgetKit.framework; path = System/Library/Frameworks/WidgetKit.framework; sourceTree = SDKROOT; };
92 | 6C8DDFF52528A7530055B7DB /* SwiftUI.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = SwiftUI.framework; path = System/Library/Frameworks/SwiftUI.framework; sourceTree = SDKROOT; };
93 | 6CABB8CD253EB09800024525 /* LocalNotificationManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalNotificationManager.swift; sourceTree = ""; };
94 | 6CABB8D1253EB09900024525 /* WeekDaysUtilities.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WeekDaysUtilities.swift; sourceTree = ""; };
95 | 6CABB8D4253EB09900024525 /* AppleEvents.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppleEvents.swift; sourceTree = ""; };
96 | 6CB2C3E5251CAB4000D70E8A /* MultiSelector.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiSelector.swift; sourceTree = ""; };
97 | 6CB2C3E8251CAC1400D70E8A /* MultiSelectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MultiSelectionView.swift; sourceTree = ""; };
98 | 6CD5FF74251B6E7E00DBB204 /* CourseViewModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CourseViewModel.swift; sourceTree = ""; };
99 | 6CD72479252834F700ED0EB0 /* TabBar.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TabBar.swift; sourceTree = ""; };
100 | 6CE81F922520966D00E6D390 /* CoursesList.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoursesList.swift; sourceTree = ""; };
101 | 6CF634122518B812005F022D /* TimeMan.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = TimeMan.app; sourceTree = BUILT_PRODUCTS_DIR; };
102 | 6CF634152518B812005F022D /* AppDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppDelegate.swift; sourceTree = ""; };
103 | 6CF634172518B812005F022D /* SceneDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SceneDelegate.swift; sourceTree = ""; };
104 | 6CF6341A2518B812005F022D /* TimeMan.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = TimeMan.xcdatamodel; sourceTree = ""; };
105 | 6CF6341C2518B812005F022D /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = ""; };
106 | 6CF6341E2518B815005F022D /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = ""; };
107 | 6CF634212518B815005F022D /* Preview Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = "Preview Assets.xcassets"; sourceTree = ""; };
108 | 6CF634242518B815005F022D /* Base */ = {isa = PBXFileReference; lastKnownFileType = file.storyboard; name = Base; path = Base.lproj/LaunchScreen.storyboard; sourceTree = ""; };
109 | 6CF634262518B815005F022D /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; };
110 | /* End PBXFileReference section */
111 |
112 | /* Begin PBXFrameworksBuildPhase section */
113 | 6CF6340F2518B812005F022D /* Frameworks */ = {
114 | isa = PBXFrameworksBuildPhase;
115 | buildActionMask = 2147483647;
116 | files = (
117 | );
118 | runOnlyForDeploymentPostprocessing = 0;
119 | };
120 | /* End PBXFrameworksBuildPhase section */
121 |
122 | /* Begin PBXGroup section */
123 | 6C02F2E82532CCD700EA493C /* Services */ = {
124 | isa = PBXGroup;
125 | children = (
126 | 6CABB8CD253EB09800024525 /* LocalNotificationManager.swift */,
127 | 6CABB8D4253EB09900024525 /* AppleEvents.swift */,
128 | );
129 | path = Services;
130 | sourceTree = "";
131 | };
132 | 6C02F2EF2532CD5C00EA493C /* App */ = {
133 | isa = PBXGroup;
134 | children = (
135 | 6CF6341E2518B815005F022D /* Assets.xcassets */,
136 | 6CF634202518B815005F022D /* Preview Content */,
137 | 6C4F41EA2531E7FC005EDD61 /* .swiftlint.yml */,
138 | 6CF634262518B815005F022D /* Info.plist */,
139 | 6CF634152518B812005F022D /* AppDelegate.swift */,
140 | 6CF634232518B815005F022D /* LaunchScreen.storyboard */,
141 | 6CF634172518B812005F022D /* SceneDelegate.swift */,
142 | );
143 | name = App;
144 | sourceTree = "";
145 | };
146 | 6C1444A1251DE16D00CFA2BF /* MultiSelector */ = {
147 | isa = PBXGroup;
148 | children = (
149 | 6CB2C3E8251CAC1400D70E8A /* MultiSelectionView.swift */,
150 | 6CB2C3E5251CAB4000D70E8A /* MultiSelector.swift */,
151 | );
152 | path = MultiSelector;
153 | sourceTree = "";
154 | };
155 | 6C3EA3DD2529C2BF00D84CA1 /* GridComponents */ = {
156 | isa = PBXGroup;
157 | children = (
158 | 6C3EA3DF2529C32E00D84CA1 /* GridComponents.swift */,
159 | );
160 | path = GridComponents;
161 | sourceTree = "";
162 | };
163 | 6C4589B62519B7680023B4BD /* Components */ = {
164 | isa = PBXGroup;
165 | children = (
166 | 6C8621E42529EE5A00F6A9E4 /* CourseCardComponents */,
167 | 6C3EA3DD2529C2BF00D84CA1 /* GridComponents */,
168 | 6C8621E12529ED4700F6A9E4 /* FloatingActionButton.swift */,
169 | 6C4589B72519B7A70023B4BD /* CalendarItem.swift */,
170 | 6C1444A1251DE16D00CFA2BF /* MultiSelector */,
171 | 6C1444A2251DE18500CFA2BF /* Header.swift */,
172 | );
173 | path = Components;
174 | sourceTree = "";
175 | };
176 | 6C8621E42529EE5A00F6A9E4 /* CourseCardComponents */ = {
177 | isa = PBXGroup;
178 | children = (
179 | 6C8621E52529EE7100F6A9E4 /* CourseCardComponents.swift */,
180 | );
181 | path = CourseCardComponents;
182 | sourceTree = "";
183 | };
184 | 6C8621E82529EFA500F6A9E4 /* Widgets */ = {
185 | isa = PBXGroup;
186 | children = (
187 | 6CD72478252834E800ED0EB0 /* TabBar */,
188 | 6C8621EB2529F0E000F6A9E4 /* Cards */,
189 | 6C4589B42519B7370023B4BD /* WeekScroll.swift */,
190 | 6C5FB769252788B500534BC9 /* UpcomingClasses.swift */,
191 | 6CE81F922520966D00E6D390 /* CoursesList.swift */,
192 | );
193 | path = Widgets;
194 | sourceTree = "";
195 | };
196 | 6C8621EB2529F0E000F6A9E4 /* Cards */ = {
197 | isa = PBXGroup;
198 | children = (
199 | 6C8DDFEA252889A20055B7DB /* UpcomingCourseCard.swift */,
200 | 6C16F6E4251904A10010857F /* CourseCard.swift */,
201 | );
202 | path = Cards;
203 | sourceTree = "";
204 | };
205 | 6C8DDFD8252850440055B7DB /* Screens */ = {
206 | isa = PBXGroup;
207 | children = (
208 | 6C04E3BF251A17A70018BD77 /* CourseInput.swift */,
209 | 6C8DDFD9252850550055B7DB /* HomeScreen.swift */,
210 | 6C8DDFDC252850BF0055B7DB /* ScrollScreen.swift */,
211 | 6C8DDFDF252850CE0055B7DB /* GridScreen.swift */,
212 | );
213 | path = Screens;
214 | sourceTree = "";
215 | };
216 | 6C8DDFE525287B650055B7DB /* Shapes */ = {
217 | isa = PBXGroup;
218 | children = (
219 | 6C8DDFE225286FEC0055B7DB /* CustomShapeTriangle.swift */,
220 | 6C8DDFE625287B9F0055B7DB /* CustomRoundedEdgesShape.swift */,
221 | );
222 | path = Shapes;
223 | sourceTree = "";
224 | };
225 | 6C8DDFF22528A7530055B7DB /* Frameworks */ = {
226 | isa = PBXGroup;
227 | children = (
228 | 6C8DDFF32528A7530055B7DB /* WidgetKit.framework */,
229 | 6C8DDFF52528A7530055B7DB /* SwiftUI.framework */,
230 | );
231 | name = Frameworks;
232 | sourceTree = "";
233 | };
234 | 6CABB8D0253EB09900024525 /* Utilities */ = {
235 | isa = PBXGroup;
236 | children = (
237 | 6C8DDFE525287B650055B7DB /* Shapes */,
238 | 6CABB8D1253EB09900024525 /* WeekDaysUtilities.swift */,
239 | 6C8C34C7253171E300E9A164 /* ColorCodes.swift */,
240 | 6C8C34CB2531824400E9A164 /* DateTimeUtilities.swift */,
241 | );
242 | path = Utilities;
243 | sourceTree = "";
244 | };
245 | 6CD5FF73251B6E6700DBB204 /* ViewModels */ = {
246 | isa = PBXGroup;
247 | children = (
248 | 6CD5FF74251B6E7E00DBB204 /* CourseViewModel.swift */,
249 | 6C5FB76C252788DA00534BC9 /* UpcomingClassViewModel.swift */,
250 | 6C3EA3DA2529BEAD00D84CA1 /* GridViewModel.swift */,
251 | 6C8621ED2529F39F00F6A9E4 /* CourseListViewModel.swift */,
252 | 6C169CBF2532A43000E7521E /* CourseCardViewModel.swift */,
253 | 6C169CC32532AADC00E7521E /* UpcomingCourseCardViewModel.swift */,
254 | );
255 | path = ViewModels;
256 | sourceTree = "";
257 | };
258 | 6CD72478252834E800ED0EB0 /* TabBar */ = {
259 | isa = PBXGroup;
260 | children = (
261 | 6CD72479252834F700ED0EB0 /* TabBar.swift */,
262 | );
263 | path = TabBar;
264 | sourceTree = "";
265 | };
266 | 6CF634092518B812005F022D = {
267 | isa = PBXGroup;
268 | children = (
269 | 6CF634142518B812005F022D /* TimeMan */,
270 | 6C8DDFF22528A7530055B7DB /* Frameworks */,
271 | 6CF634132518B812005F022D /* Products */,
272 | );
273 | sourceTree = "";
274 | };
275 | 6CF634132518B812005F022D /* Products */ = {
276 | isa = PBXGroup;
277 | children = (
278 | 6CF634122518B812005F022D /* TimeMan.app */,
279 | );
280 | name = Products;
281 | sourceTree = "";
282 | };
283 | 6CF634142518B812005F022D /* TimeMan */ = {
284 | isa = PBXGroup;
285 | children = (
286 | 6C02F2EF2532CD5C00EA493C /* App */,
287 | 6C02F2E82532CCD700EA493C /* Services */,
288 | 6CABB8D0253EB09900024525 /* Utilities */,
289 | 6CD5FF73251B6E6700DBB204 /* ViewModels */,
290 | 6CF634322518B9C7005F022D /* Core */,
291 | 6CF6342F2518B97C005F022D /* Views */,
292 | );
293 | path = TimeMan;
294 | sourceTree = "";
295 | };
296 | 6CF634202518B815005F022D /* Preview Content */ = {
297 | isa = PBXGroup;
298 | children = (
299 | 6CF634212518B815005F022D /* Preview Assets.xcassets */,
300 | );
301 | path = "Preview Content";
302 | sourceTree = "";
303 | };
304 | 6CF6342F2518B97C005F022D /* Views */ = {
305 | isa = PBXGroup;
306 | children = (
307 | 6C8621E82529EFA500F6A9E4 /* Widgets */,
308 | 6C8DDFD8252850440055B7DB /* Screens */,
309 | 6C4589B62519B7680023B4BD /* Components */,
310 | 6CF6341C2518B812005F022D /* ContentView.swift */,
311 | );
312 | path = Views;
313 | sourceTree = "";
314 | };
315 | 6CF634322518B9C7005F022D /* Core */ = {
316 | isa = PBXGroup;
317 | children = (
318 | 6C15DDA22525E0530068BCD8 /* Course+CoreDataClass.swift */,
319 | 6C15DDA32525E0530068BCD8 /* Course+CoreDataProperties.swift */,
320 | 6CF634192518B812005F022D /* TimeMan.xcdatamodeld */,
321 | );
322 | path = Core;
323 | sourceTree = "";
324 | };
325 | /* End PBXGroup section */
326 |
327 | /* Begin PBXNativeTarget section */
328 | 6CF634112518B812005F022D /* TimeMan */ = {
329 | isa = PBXNativeTarget;
330 | buildConfigurationList = 6CF634292518B815005F022D /* Build configuration list for PBXNativeTarget "TimeMan" */;
331 | buildPhases = (
332 | 6CF6340E2518B812005F022D /* Sources */,
333 | 6CF6340F2518B812005F022D /* Frameworks */,
334 | 6CF634102518B812005F022D /* Resources */,
335 | 6C8C34CE2531E4D900E9A164 /* Swiftlint */,
336 | 6C8DE0032528A7580055B7DB /* Embed App Extensions */,
337 | );
338 | buildRules = (
339 | );
340 | dependencies = (
341 | );
342 | name = TimeMan;
343 | productName = TimeMan;
344 | productReference = 6CF634122518B812005F022D /* TimeMan.app */;
345 | productType = "com.apple.product-type.application";
346 | };
347 | /* End PBXNativeTarget section */
348 |
349 | /* Begin PBXProject section */
350 | 6CF6340A2518B812005F022D /* Project object */ = {
351 | isa = PBXProject;
352 | attributes = {
353 | LastSwiftUpdateCheck = 1200;
354 | LastUpgradeCheck = 1200;
355 | ORGANIZATIONNAME = "Sai Ankit";
356 | TargetAttributes = {
357 | 6CF634112518B812005F022D = {
358 | CreatedOnToolsVersion = 11.7;
359 | };
360 | };
361 | };
362 | buildConfigurationList = 6CF6340D2518B812005F022D /* Build configuration list for PBXProject "TimeMan" */;
363 | compatibilityVersion = "Xcode 9.3";
364 | developmentRegion = en;
365 | hasScannedForEncodings = 0;
366 | knownRegions = (
367 | en,
368 | Base,
369 | );
370 | mainGroup = 6CF634092518B812005F022D;
371 | productRefGroup = 6CF634132518B812005F022D /* Products */;
372 | projectDirPath = "";
373 | projectRoot = "";
374 | targets = (
375 | 6CF634112518B812005F022D /* TimeMan */,
376 | );
377 | };
378 | /* End PBXProject section */
379 |
380 | /* Begin PBXResourcesBuildPhase section */
381 | 6CF634102518B812005F022D /* Resources */ = {
382 | isa = PBXResourcesBuildPhase;
383 | buildActionMask = 2147483647;
384 | files = (
385 | 6C4F41EB2531E7FC005EDD61 /* .swiftlint.yml in Resources */,
386 | 6CF634252518B815005F022D /* LaunchScreen.storyboard in Resources */,
387 | 6CF634222518B815005F022D /* Preview Assets.xcassets in Resources */,
388 | 6CF6341F2518B815005F022D /* Assets.xcassets in Resources */,
389 | );
390 | runOnlyForDeploymentPostprocessing = 0;
391 | };
392 | /* End PBXResourcesBuildPhase section */
393 |
394 | /* Begin PBXShellScriptBuildPhase section */
395 | 6C8C34CE2531E4D900E9A164 /* Swiftlint */ = {
396 | isa = PBXShellScriptBuildPhase;
397 | buildActionMask = 2147483647;
398 | files = (
399 | );
400 | inputFileListPaths = (
401 | );
402 | inputPaths = (
403 | );
404 | name = Swiftlint;
405 | outputFileListPaths = (
406 | );
407 | outputPaths = (
408 | );
409 | runOnlyForDeploymentPostprocessing = 0;
410 | shellPath = /bin/sh;
411 | shellScript = "# Type a script or drag a script file from your workspace to insert its path.\nif which swiftlint >/dev/null; then\n swiftlint\nelse\n echo \"warning: SwiftLint not installed, download from https://github.com/realm/SwiftLint\"\nfi\n";
412 | };
413 | /* End PBXShellScriptBuildPhase section */
414 |
415 | /* Begin PBXSourcesBuildPhase section */
416 | 6CF6340E2518B812005F022D /* Sources */ = {
417 | isa = PBXSourcesBuildPhase;
418 | buildActionMask = 2147483647;
419 | files = (
420 | 6C5FB76A252788B500534BC9 /* UpcomingClasses.swift in Sources */,
421 | 6CF6341B2518B812005F022D /* TimeMan.xcdatamodeld in Sources */,
422 | 6CABB8D5253EB09900024525 /* AppleEvents.swift in Sources */,
423 | 6C3EA3E02529C32E00D84CA1 /* GridComponents.swift in Sources */,
424 | 6C8621E22529ED4700F6A9E4 /* FloatingActionButton.swift in Sources */,
425 | 6C169CC02532A43000E7521E /* CourseCardViewModel.swift in Sources */,
426 | 6CABB8CE253EB09800024525 /* LocalNotificationManager.swift in Sources */,
427 | 6C8621E62529EE7100F6A9E4 /* CourseCardComponents.swift in Sources */,
428 | 6C8621EE2529F39F00F6A9E4 /* CourseListViewModel.swift in Sources */,
429 | 6CF634162518B812005F022D /* AppDelegate.swift in Sources */,
430 | 6C15DDA52525E0530068BCD8 /* Course+CoreDataProperties.swift in Sources */,
431 | 6CB2C3E6251CAB4000D70E8A /* MultiSelector.swift in Sources */,
432 | 6CB2C3E9251CAC1400D70E8A /* MultiSelectionView.swift in Sources */,
433 | 6C8DDFEB252889A20055B7DB /* UpcomingCourseCard.swift in Sources */,
434 | 6CF6341D2518B812005F022D /* ContentView.swift in Sources */,
435 | 6C169CC42532AADC00E7521E /* UpcomingCourseCardViewModel.swift in Sources */,
436 | 6C04E3C0251A17A70018BD77 /* CourseInput.swift in Sources */,
437 | 6CABB8D2253EB09900024525 /* WeekDaysUtilities.swift in Sources */,
438 | 6C8C34C8253171E300E9A164 /* ColorCodes.swift in Sources */,
439 | 6CE81F932520966D00E6D390 /* CoursesList.swift in Sources */,
440 | 6C4589B82519B7A70023B4BD /* CalendarItem.swift in Sources */,
441 | 6CF634182518B812005F022D /* SceneDelegate.swift in Sources */,
442 | 6C8DDFE0252850CE0055B7DB /* GridScreen.swift in Sources */,
443 | 6C5FB76D252788DA00534BC9 /* UpcomingClassViewModel.swift in Sources */,
444 | 6C15DDA42525E0530068BCD8 /* Course+CoreDataClass.swift in Sources */,
445 | 6C8C34CC2531824400E9A164 /* DateTimeUtilities.swift in Sources */,
446 | 6C1444A3251DE18500CFA2BF /* Header.swift in Sources */,
447 | 6C8DDFE325286FEC0055B7DB /* CustomShapeTriangle.swift in Sources */,
448 | 6C8DDFE725287B9F0055B7DB /* CustomRoundedEdgesShape.swift in Sources */,
449 | 6C4589B52519B7370023B4BD /* WeekScroll.swift in Sources */,
450 | 6CD7247A252834F700ED0EB0 /* TabBar.swift in Sources */,
451 | 6C8DDFDD252850BF0055B7DB /* ScrollScreen.swift in Sources */,
452 | 6C3EA3DB2529BEAD00D84CA1 /* GridViewModel.swift in Sources */,
453 | 6C8DDFDA252850550055B7DB /* HomeScreen.swift in Sources */,
454 | 6CD5FF75251B6E7E00DBB204 /* CourseViewModel.swift in Sources */,
455 | 6C16F6E5251904A10010857F /* CourseCard.swift in Sources */,
456 | );
457 | runOnlyForDeploymentPostprocessing = 0;
458 | };
459 | /* End PBXSourcesBuildPhase section */
460 |
461 | /* Begin PBXVariantGroup section */
462 | 6CF634232518B815005F022D /* LaunchScreen.storyboard */ = {
463 | isa = PBXVariantGroup;
464 | children = (
465 | 6CF634242518B815005F022D /* Base */,
466 | );
467 | name = LaunchScreen.storyboard;
468 | sourceTree = "";
469 | };
470 | /* End PBXVariantGroup section */
471 |
472 | /* Begin XCBuildConfiguration section */
473 | 6CF634272518B815005F022D /* Debug */ = {
474 | isa = XCBuildConfiguration;
475 | buildSettings = {
476 | ALWAYS_SEARCH_USER_PATHS = NO;
477 | CLANG_ANALYZER_NONNULL = YES;
478 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
479 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
480 | CLANG_CXX_LIBRARY = "libc++";
481 | CLANG_ENABLE_MODULES = YES;
482 | CLANG_ENABLE_OBJC_ARC = YES;
483 | CLANG_ENABLE_OBJC_WEAK = YES;
484 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
485 | CLANG_WARN_BOOL_CONVERSION = YES;
486 | CLANG_WARN_COMMA = YES;
487 | CLANG_WARN_CONSTANT_CONVERSION = YES;
488 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
489 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
490 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
491 | CLANG_WARN_EMPTY_BODY = YES;
492 | CLANG_WARN_ENUM_CONVERSION = YES;
493 | CLANG_WARN_INFINITE_RECURSION = YES;
494 | CLANG_WARN_INT_CONVERSION = YES;
495 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
496 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
497 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
498 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
499 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
500 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
501 | CLANG_WARN_STRICT_PROTOTYPES = YES;
502 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
503 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
504 | CLANG_WARN_UNREACHABLE_CODE = YES;
505 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
506 | COPY_PHASE_STRIP = NO;
507 | DEBUG_INFORMATION_FORMAT = dwarf;
508 | ENABLE_STRICT_OBJC_MSGSEND = YES;
509 | ENABLE_TESTABILITY = YES;
510 | GCC_C_LANGUAGE_STANDARD = gnu11;
511 | GCC_DYNAMIC_NO_PIC = NO;
512 | GCC_NO_COMMON_BLOCKS = YES;
513 | GCC_OPTIMIZATION_LEVEL = 0;
514 | GCC_PREPROCESSOR_DEFINITIONS = (
515 | "DEBUG=1",
516 | "$(inherited)",
517 | );
518 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
519 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
520 | GCC_WARN_UNDECLARED_SELECTOR = YES;
521 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
522 | GCC_WARN_UNUSED_FUNCTION = YES;
523 | GCC_WARN_UNUSED_VARIABLE = YES;
524 | IPHONEOS_DEPLOYMENT_TARGET = 13.7;
525 | MTL_ENABLE_DEBUG_INFO = INCLUDE_SOURCE;
526 | MTL_FAST_MATH = YES;
527 | ONLY_ACTIVE_ARCH = YES;
528 | SDKROOT = iphoneos;
529 | SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
530 | SWIFT_OPTIMIZATION_LEVEL = "-Onone";
531 | };
532 | name = Debug;
533 | };
534 | 6CF634282518B815005F022D /* Release */ = {
535 | isa = XCBuildConfiguration;
536 | buildSettings = {
537 | ALWAYS_SEARCH_USER_PATHS = NO;
538 | CLANG_ANALYZER_NONNULL = YES;
539 | CLANG_ANALYZER_NUMBER_OBJECT_CONVERSION = YES_AGGRESSIVE;
540 | CLANG_CXX_LANGUAGE_STANDARD = "gnu++14";
541 | CLANG_CXX_LIBRARY = "libc++";
542 | CLANG_ENABLE_MODULES = YES;
543 | CLANG_ENABLE_OBJC_ARC = YES;
544 | CLANG_ENABLE_OBJC_WEAK = YES;
545 | CLANG_WARN_BLOCK_CAPTURE_AUTORELEASING = YES;
546 | CLANG_WARN_BOOL_CONVERSION = YES;
547 | CLANG_WARN_COMMA = YES;
548 | CLANG_WARN_CONSTANT_CONVERSION = YES;
549 | CLANG_WARN_DEPRECATED_OBJC_IMPLEMENTATIONS = YES;
550 | CLANG_WARN_DIRECT_OBJC_ISA_USAGE = YES_ERROR;
551 | CLANG_WARN_DOCUMENTATION_COMMENTS = YES;
552 | CLANG_WARN_EMPTY_BODY = YES;
553 | CLANG_WARN_ENUM_CONVERSION = YES;
554 | CLANG_WARN_INFINITE_RECURSION = YES;
555 | CLANG_WARN_INT_CONVERSION = YES;
556 | CLANG_WARN_NON_LITERAL_NULL_CONVERSION = YES;
557 | CLANG_WARN_OBJC_IMPLICIT_RETAIN_SELF = YES;
558 | CLANG_WARN_OBJC_LITERAL_CONVERSION = YES;
559 | CLANG_WARN_OBJC_ROOT_CLASS = YES_ERROR;
560 | CLANG_WARN_QUOTED_INCLUDE_IN_FRAMEWORK_HEADER = YES;
561 | CLANG_WARN_RANGE_LOOP_ANALYSIS = YES;
562 | CLANG_WARN_STRICT_PROTOTYPES = YES;
563 | CLANG_WARN_SUSPICIOUS_MOVE = YES;
564 | CLANG_WARN_UNGUARDED_AVAILABILITY = YES_AGGRESSIVE;
565 | CLANG_WARN_UNREACHABLE_CODE = YES;
566 | CLANG_WARN__DUPLICATE_METHOD_MATCH = YES;
567 | COPY_PHASE_STRIP = NO;
568 | DEBUG_INFORMATION_FORMAT = "dwarf-with-dsym";
569 | ENABLE_NS_ASSERTIONS = NO;
570 | ENABLE_STRICT_OBJC_MSGSEND = YES;
571 | GCC_C_LANGUAGE_STANDARD = gnu11;
572 | GCC_NO_COMMON_BLOCKS = YES;
573 | GCC_WARN_64_TO_32_BIT_CONVERSION = YES;
574 | GCC_WARN_ABOUT_RETURN_TYPE = YES_ERROR;
575 | GCC_WARN_UNDECLARED_SELECTOR = YES;
576 | GCC_WARN_UNINITIALIZED_AUTOS = YES_AGGRESSIVE;
577 | GCC_WARN_UNUSED_FUNCTION = YES;
578 | GCC_WARN_UNUSED_VARIABLE = YES;
579 | IPHONEOS_DEPLOYMENT_TARGET = 13.7;
580 | MTL_ENABLE_DEBUG_INFO = NO;
581 | MTL_FAST_MATH = YES;
582 | SDKROOT = iphoneos;
583 | SWIFT_COMPILATION_MODE = wholemodule;
584 | SWIFT_OPTIMIZATION_LEVEL = "-O";
585 | VALIDATE_PRODUCT = YES;
586 | };
587 | name = Release;
588 | };
589 | 6CF6342A2518B815005F022D /* Debug */ = {
590 | isa = XCBuildConfiguration;
591 | buildSettings = {
592 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
593 | CODE_SIGN_STYLE = Automatic;
594 | DEVELOPMENT_ASSET_PATHS = "\"TimeMan/Preview Content\"";
595 | DEVELOPMENT_TEAM = Y3T5R5M4ZL;
596 | ENABLE_PREVIEWS = YES;
597 | INFOPLIST_FILE = TimeMan/Info.plist;
598 | LD_RUNPATH_SEARCH_PATHS = (
599 | "$(inherited)",
600 | "@executable_path/Frameworks",
601 | );
602 | PRODUCT_BUNDLE_IDENTIFIER = com.saiankit.TimeMan;
603 | PRODUCT_NAME = "$(TARGET_NAME)";
604 | SWIFT_VERSION = 5.0;
605 | TARGETED_DEVICE_FAMILY = "1,2";
606 | };
607 | name = Debug;
608 | };
609 | 6CF6342B2518B815005F022D /* Release */ = {
610 | isa = XCBuildConfiguration;
611 | buildSettings = {
612 | ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
613 | CODE_SIGN_STYLE = Automatic;
614 | DEVELOPMENT_ASSET_PATHS = "\"TimeMan/Preview Content\"";
615 | DEVELOPMENT_TEAM = Y3T5R5M4ZL;
616 | ENABLE_PREVIEWS = YES;
617 | INFOPLIST_FILE = TimeMan/Info.plist;
618 | LD_RUNPATH_SEARCH_PATHS = (
619 | "$(inherited)",
620 | "@executable_path/Frameworks",
621 | );
622 | PRODUCT_BUNDLE_IDENTIFIER = com.saiankit.TimeMan;
623 | PRODUCT_NAME = "$(TARGET_NAME)";
624 | SWIFT_VERSION = 5.0;
625 | TARGETED_DEVICE_FAMILY = "1,2";
626 | };
627 | name = Release;
628 | };
629 | /* End XCBuildConfiguration section */
630 |
631 | /* Begin XCConfigurationList section */
632 | 6CF6340D2518B812005F022D /* Build configuration list for PBXProject "TimeMan" */ = {
633 | isa = XCConfigurationList;
634 | buildConfigurations = (
635 | 6CF634272518B815005F022D /* Debug */,
636 | 6CF634282518B815005F022D /* Release */,
637 | );
638 | defaultConfigurationIsVisible = 0;
639 | defaultConfigurationName = Release;
640 | };
641 | 6CF634292518B815005F022D /* Build configuration list for PBXNativeTarget "TimeMan" */ = {
642 | isa = XCConfigurationList;
643 | buildConfigurations = (
644 | 6CF6342A2518B815005F022D /* Debug */,
645 | 6CF6342B2518B815005F022D /* Release */,
646 | );
647 | defaultConfigurationIsVisible = 0;
648 | defaultConfigurationName = Release;
649 | };
650 | /* End XCConfigurationList section */
651 |
652 | /* Begin XCVersionGroup section */
653 | 6CF634192518B812005F022D /* TimeMan.xcdatamodeld */ = {
654 | isa = XCVersionGroup;
655 | children = (
656 | 6CF6341A2518B812005F022D /* TimeMan.xcdatamodel */,
657 | );
658 | currentVersion = 6CF6341A2518B812005F022D /* TimeMan.xcdatamodel */;
659 | path = TimeMan.xcdatamodeld;
660 | sourceTree = "";
661 | versionGroupType = wrapper.xcdatamodel;
662 | };
663 | /* End XCVersionGroup section */
664 | };
665 | rootObject = 6CF6340A2518B812005F022D /* Project object */;
666 | }
667 |
--------------------------------------------------------------------------------