├── 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 | | ![1](Screenshots/ScrollView-Dark.png) | ![1](Screenshots/HomeScreen-Dark.png) | ![1](Screenshots/GridView-Dark.png) | 28 | |---|---|---| 29 | | ![1](Screenshots/Notifications-Light.png) | ![1](Screenshots/ColorCode-Dark.png) | ![1](Screenshots/Notifications-Dark.png) | 30 | | ![1](Screenshots/AddCourse-Dark.png) | ![1](Screenshots/AppleCalendar-Dark.png) | ![1](Screenshots/AddCourse-Light.png) | 31 | | ![1](Screenshots/ScrollView-Light.png) | ![1](Screenshots/HomeScreen-Light.png) | ![1](Screenshots/GridView-Light.png) | 32 | | ![1](Screenshots/AppleCalendar-Light.png) | | ![1](Screenshots/Delete-Dark.png) | 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 | --------------------------------------------------------------------------------