├── .github ├── FUNDING.yml └── workflows │ └── xcodebuild.yml ├── .gitignore ├── Analytics.xcdatamodeld └── Analytics.xcdatamodel │ └── contents ├── CHANGELOG.md ├── CONTRIBUTING.md ├── CONTRIBUTORS.md ├── Campus-iOS.xcodeproj ├── project.pbxproj ├── project.xcworkspace │ ├── contents.xcworkspacedata │ └── xcshareddata │ │ ├── IDEWorkspaceChecks.plist │ │ └── swiftpm │ │ └── Package.resolved~Stashed changes └── xcshareddata │ ├── xcbaselines │ └── 366F0E9427580CFD0091651D.xcbaseline │ │ ├── 3439EAFF-1FCF-41B1-96A9-2FD6666B9105.plist │ │ └── Info.plist │ └── xcschemes │ └── Campus-iOS.xcscheme ├── Campus-iOS ├── App.swift ├── Assets.xcassets │ ├── 3D.imageset │ │ ├── 3D@2x.png │ │ ├── 3D@3x.png │ │ └── Contents.json │ ├── AccentColor.colorset │ │ └── Contents.json │ ├── AppIcon.appiconset │ │ ├── Contents.json │ │ ├── Icon-App-20x20@1x.png │ │ ├── Icon-App-20x20@2x-1.png │ │ ├── Icon-App-20x20@2x.png │ │ ├── Icon-App-20x20@3x.png │ │ ├── Icon-App-29x29@1x.png │ │ ├── Icon-App-29x29@2x-1.png │ │ ├── Icon-App-29x29@2x.png │ │ ├── Icon-App-29x29@3x.png │ │ ├── Icon-App-40x40@1x.png │ │ ├── Icon-App-40x40@2x-1.png │ │ ├── Icon-App-40x40@2x.png │ │ ├── Icon-App-40x40@3x.png │ │ ├── Icon-App-60x60@2x.png │ │ ├── Icon-App-60x60@3x.png │ │ ├── Icon-App-76x76@1x.png │ │ ├── Icon-App-76x76@2x.png │ │ ├── Icon-App-83.5x83.5@2x.png │ │ ├── ItunesArtwork@2x.png │ │ ├── slice1@3x copy 2.png │ │ ├── slice1@3x copy 3-1.png │ │ ├── slice1@3x copy 3.png │ │ ├── slice1@3x copy 4.png │ │ ├── slice1@3x copy 5.png │ │ ├── slice1@3x copy 6-1.png │ │ ├── slice1@3x copy 6.png │ │ ├── slice1@3x copy 7-1.png │ │ ├── slice1@3x copy 7.png │ │ └── slice1@3x copy 8.png │ ├── Campus │ │ ├── Contents.json │ │ ├── campus-freising.imageset │ │ │ ├── Contents.json │ │ │ └── campus-freising.jpg │ │ ├── campus-garching.imageset │ │ │ ├── Contents.json │ │ │ └── campus-garching.jpeg │ │ ├── campus-klinikum.imageset │ │ │ ├── Contents.json │ │ │ └── campus-klinikum.jpeg │ │ ├── campus-olympia.imageset │ │ │ ├── Contents.json │ │ │ └── campus-olympia.jpeg │ │ └── campus-stamm.imageset │ │ │ ├── Contents.json │ │ │ └── campus-stamm.jpeg │ ├── Colors │ │ ├── Contents.json │ │ ├── contrastText.colorset │ │ │ └── Contents.json │ │ ├── primaryBackground.colorset │ │ │ └── Contents.json │ │ ├── primaryText.colorset │ │ │ └── Contents.json │ │ ├── secondaryBackground.colorset │ │ │ └── Contents.json │ │ ├── tumBlue.colorset │ │ │ └── Contents.json │ │ ├── tumBlueWhite.colorset │ │ │ └── Contents.json │ │ └── tumBrand.colorset │ │ │ └── Contents.json │ ├── Contents.json │ ├── Error-logo-transparent.imageset │ │ ├── Contents.json │ │ └── Error-logo-transparent.png │ ├── Error-logo.imageset │ │ ├── 3_Something Went Wrong@2x.svg │ │ └── Contents.json │ ├── Logos │ │ ├── Contents.json │ │ ├── logo-blue-responsive.imageset │ │ │ ├── Contents.json │ │ │ ├── tum-blue-dark.svg │ │ │ └── tum-blue-light.svg │ │ ├── logo-blue.imageset │ │ │ ├── Contents.json │ │ │ └── TUMLogo_oZ_Vollfl_blau_RGB.png │ │ ├── logo-rainbow.imageset │ │ │ ├── Contents.json │ │ │ └── logo-rainbow.png │ │ ├── logo-responsive.imageset │ │ │ ├── Contents.json │ │ │ ├── logo-black 1.png │ │ │ ├── logo-black 2.png │ │ │ ├── logo-black.png │ │ │ ├── logo-white 1.png │ │ │ ├── logo-white 2.png │ │ │ └── logo-white.png │ │ └── logo-white.imageset │ │ │ ├── Contents.json │ │ │ └── TUM2.png │ ├── darkGray.colorset │ │ └── Contents.json │ ├── default.imageset │ │ ├── Contents.json │ │ ├── default@2x.png │ │ └── default@3x.png │ ├── movie.imageset │ │ ├── Contents.json │ │ └── movie-placeholder.jpg │ ├── outline.imageset │ │ ├── Contents.json │ │ ├── outline@2x.png │ │ └── outline@3x.png │ ├── placeholder.imageset │ │ ├── Contents.json │ │ ├── placeholder@2x.heic │ │ └── placeholder@3x.heic │ ├── pride.imageset │ │ ├── Contents.json │ │ ├── pride@2x.png │ │ └── pride@3x.png │ ├── set-permissions.imageset │ │ ├── Contents.json │ │ └── set-permissions.png │ ├── token_step1.imageset │ │ ├── Contents.json │ │ └── token_step1.png │ ├── token_step2.imageset │ │ ├── Contents.json │ │ └── token_step2.png │ ├── token_step3.imageset │ │ ├── Contents.json │ │ └── token_step3.png │ ├── token_step4.imageset │ │ ├── Contents.json │ │ └── token_step4.png │ ├── token_step5.imageset │ │ ├── Contents.json │ │ └── token_step5.png │ ├── tower.imageset │ │ ├── Contents.json │ │ └── image4-94.png │ ├── tumtower.imageset │ │ ├── Contents.json │ │ └── tumtower.png │ ├── white.imageset │ │ ├── Contents.json │ │ ├── white@2x.png │ │ └── white@3x.png │ ├── whiteTumBlue.colorset │ │ └── Contents.json │ └── widgetColor.colorset │ │ └── Contents.json ├── Base.lproj │ ├── Login~.storyboard │ └── Main.storyboard ├── Base │ ├── Constants │ │ ├── APIConstants.swift │ │ └── Constants.swift │ ├── Enums │ │ └── Enums.swift │ ├── Errors │ │ ├── AlertErrorHandler.swift │ │ ├── BackendError.swift │ │ ├── Environment+Error.swift │ │ ├── Error+Category.swift │ │ ├── ErrorCategory.swift │ │ ├── ErrorEmittingViewModifier.swift │ │ ├── ErrorHandler.swift │ │ ├── NetworkingError.swift │ │ └── View+Error.swift │ ├── Helpers │ │ ├── DecoderProtocol.swift │ │ ├── ImagePicker.swift │ │ └── XMLSerializer.swift │ ├── Icons │ │ ├── 3D@2x.png │ │ ├── 3D@3x.png │ │ ├── default@2x.png │ │ ├── default@3x.png │ │ ├── outline@2x.png │ │ ├── outline@3x.png │ │ ├── pride@2x.png │ │ ├── pride@3x.png │ │ ├── white@2x.png │ │ └── white@3x.png │ ├── Model │ │ ├── MockModel.swift │ │ ├── Model.swift │ │ └── Persistence.swift │ ├── Networking │ │ ├── APIErrors │ │ │ ├── EatAPIError.swift │ │ │ ├── MVGAPIError.swift │ │ │ ├── NavigaTUMAPIError.swift │ │ │ ├── TUMCabeAPIError.swift │ │ │ ├── TUMDevAppAPIError.swift │ │ │ ├── TUMOnlineAPIError.swift │ │ │ └── TUMSexyAPIError.swift │ │ ├── APIResponse.swift │ │ ├── APIs │ │ │ ├── EatAPI.swift │ │ │ ├── MVGAPI.swift │ │ │ ├── MVVDeparturesAPI.swift │ │ │ ├── NavigaTUMAPI.swift │ │ │ ├── TUMCabeAPI.swift │ │ │ ├── TUMDevAppAPI.swift │ │ │ ├── TUMOnlineAPI.swift │ │ │ └── TUMSexyAPI.swift │ │ ├── Cache.swift │ │ ├── CampusOnlineAPI.swift │ │ ├── EatAPI.swift │ │ ├── NetworkingAPI.swift │ │ ├── Protocols │ │ │ ├── API.swift │ │ │ ├── APIError.swift │ │ │ ├── MainAPI.swift │ │ │ └── Service.swift │ │ └── TUMDevAppAPI.swift │ ├── Preview Content │ │ └── Preview Assets.xcassets │ │ │ └── Contents.json │ ├── VideoAssets │ │ └── token-tutorial.mov │ └── Views │ │ ├── Collapsible.swift │ │ ├── FailedView.swift │ │ ├── GroupBoxLabelView.swift │ │ ├── ImageFullScreenView.swift │ │ ├── LoadingView.swift │ │ ├── NavigationBarView.swift │ │ ├── NoDataView.swift │ │ └── SFSafariViewWrapper.swift ├── CalendarComponent │ ├── Model │ │ ├── CalendarEvent+PreviewData.swift │ │ ├── CalendarEvent.swift │ │ └── TumCalendarStyle.swift │ ├── Screen │ │ └── CalendarScreen.swift │ ├── Service │ │ └── CalendarService.swift │ ├── ViewModel │ │ └── CalendarViewModel.swift │ └── Views │ │ ├── CalendarContentView.swift │ │ ├── CalendarDisplayView.swift │ │ ├── CalendarSingleEventView.swift │ │ └── Widget │ │ ├── CalendarWidgetEventView.swift │ │ ├── CalendarWidgetScreen.swift │ │ └── CalendarWidgetView.swift ├── Campus-iOS.entitlements ├── Campus-iOS │ ├── Base.lproj │ │ └── Localizable.strings │ └── de.lproj │ │ └── Localizable.strings ├── Crashlytics │ └── CrashlyticsService.swift ├── DataModels │ ├── Campus_iOS.xcdatamodeld │ │ ├── .xccurrentversion │ │ └── Campus_iOS.xcdatamodel │ │ │ └── contents │ ├── DataTypeClassifierV4English.mlmodel │ ├── DataTypeClassifierV4German.mlmodel │ └── Old models │ │ ├── DataTypeClassifierV1.mlmodel │ │ ├── DataTypeClassifierV2.mlmodel │ │ └── DataTypeClassifierV3.mlmodel ├── DeparturesComponent │ ├── Types │ │ └── Departures.swift │ ├── View │ │ ├── DeparturesDetailsRowView.swift │ │ └── DeparturesDetailsView.swift │ └── ViewModel │ │ └── DeparturesWidgetViewModel.swift ├── Design │ ├── CustomRoundedBorderTextFieldStyle.swift │ ├── RoundedCorners.swift │ ├── Theme.swift │ ├── TrailingIconLabelStyle.swift │ └── ViewModifiers.swift ├── Extensions │ ├── Array+Groups.swift │ ├── Array+Rearrange.swift │ ├── CLLocation+isInvalid.swift │ ├── Date+Time.swift │ ├── Date+daysBetween.swift │ ├── Extensions.swift │ ├── NSMutableString+Extensions.swift │ └── Operators.swift ├── GoogleService-Info.plist ├── GradesComponent │ ├── Model │ │ ├── AcademicDegree.swift │ │ ├── AverageGrade.swift │ │ ├── Grade+PreviewData.swift │ │ ├── Grade.swift │ │ └── Modus.swift │ ├── Screen │ │ └── GradesScreen.swift │ ├── Service │ │ ├── AverageGradesService.swift │ │ └── GradesService.swift │ ├── ViewModel │ │ ├── GradeColor.swift │ │ ├── GradesViewModel+ChartData.swift │ │ ├── GradesViewModel+State.swift │ │ ├── GradesViewModel.swift │ │ └── MockGradesViewModel.swift │ └── Views │ │ ├── BarChartView.swift │ │ ├── GlowBorder.swift │ │ ├── GradeView.swift │ │ ├── GradesSemesterView.swift │ │ ├── GradesStudyProgramView.swift │ │ └── GradesView.swift ├── HomeComponent │ ├── ContactComponent │ │ ├── ContactScreen.swift │ │ └── Views │ │ │ ├── ContactCardView.swift │ │ │ └── LinkView.swift │ ├── HomeScreen.swift │ ├── WidgetComponent │ │ └── Views │ │ │ └── Widget │ │ │ ├── DeparturesWidgetScreen.swift │ │ │ └── DeparturesWidgetView.swift │ └── WidgetScreen.swift ├── Info.plist ├── LectureComponent │ ├── Model │ │ ├── Lecture.swift │ │ └── LectureDetails.swift │ ├── Screen │ │ ├── LectureDetailsScreen.swift │ │ └── LecturesScreen.swift │ ├── Service │ │ ├── LectureDetailsService.swift │ │ └── LecturesService.swift │ ├── ViewModel │ │ ├── LectureDetailsViewModel+State.swift │ │ ├── LectureDetailsViewModel.swift │ │ ├── LecturesViewModel+State.swift │ │ └── LecturesViewModel.swift │ └── Views │ │ ├── LectureDetailsViews │ │ ├── LectureDetailsBasicInfoRowView.swift │ │ ├── LectureDetailsBasicInfoView.swift │ │ ├── LectureDetailsDetailedInfoRowView.swift │ │ ├── LectureDetailsDetailedInfoView.swift │ │ ├── LectureDetailsEventInfoView.swift │ │ ├── LectureDetailsLinkView.swift │ │ └── LectureDetailsTitleView.swift │ │ ├── LectureView.swift │ │ ├── LecturesDetailView.swift │ │ └── LecturesView.swift ├── LectureSearchComponent │ ├── Screen │ │ └── LectureSearchScreen.swift │ ├── Service │ │ └── LectureSearchService.swift │ ├── View │ │ └── LectureSearchView.swift │ └── ViewModel │ │ └── LectureSearchViewModel.swift ├── LoginComponent │ ├── Model │ │ ├── Confirmation.swift │ │ ├── Credentials.swift │ │ └── Token.swift │ ├── Service │ │ └── AuthenticationHandler.swift │ ├── ViewModel │ │ ├── LoginViewModel+LoginState.swift │ │ ├── LoginViewModel+TokenState.swift │ │ ├── LoginViewModel.swift │ │ ├── TokenPermissionsViewModel+PermissionType.swift │ │ ├── TokenPermissionsViewModel+State.swift │ │ └── TokenPermissionsViewModel.swift │ └── Views │ │ ├── Launch Screen.storyboard │ │ ├── LoginView.swift │ │ ├── TUMSplashScreen.swift │ │ ├── TokenConfirmationView.swift │ │ └── TokenPermissionsView.swift ├── Map │ └── MainView │ │ └── PanelContent.swift ├── MapComponent │ ├── Model │ │ └── MapLocation.swift │ ├── Service │ │ ├── CafeteriasService.swift │ │ ├── DishService.swift │ │ ├── MealPlanService.swift │ │ └── StudyRoomsService.swift │ ├── Types │ │ ├── Cafeterias │ │ │ ├── Cafeteria+PreviewData.swift │ │ │ ├── Cafeteria.swift │ │ │ ├── Dish.swift │ │ │ ├── DishLabel.swift │ │ │ ├── MealPlan.swift │ │ │ ├── MensaCategory.swift │ │ │ └── MensaMenu.swift │ │ ├── StudyRooms │ │ │ ├── RoomImageMapping.swift │ │ │ ├── StudyRoom.swift │ │ │ ├── StudyRoomApiResponse+PreviewData.swift │ │ │ ├── StudyRoomApiResponse.swift │ │ │ ├── StudyRoomAttribute.swift │ │ │ └── StudyRoomGroup.swift │ │ └── TUMLocation.swift │ ├── View │ │ ├── AnnotatedMapView.swift │ │ ├── Cafeterias │ │ │ ├── CafeteriaView.swift │ │ │ ├── CafeteriasListView.swift │ │ │ ├── CafeteriasView.swift │ │ │ ├── DishView.swift │ │ │ ├── MealPlanScreen.swift │ │ │ ├── MealPlanView.swift │ │ │ ├── MenuView.swift │ │ │ ├── MenuWeekView.swift │ │ │ ├── OldCafeteriaComponent │ │ │ │ ├── DishViewOld.swift │ │ │ │ └── MenuViewOld.swift │ │ │ └── Widget │ │ │ │ ├── CafeteriaWidget.swift │ │ │ │ ├── CafeteriaWidgetScreen.swift │ │ │ │ └── MealIngredientsView.swift │ │ ├── Campuses │ │ │ ├── AnnotationView.swift │ │ │ ├── CampusCellView.swift │ │ │ └── CampusView.swift │ │ ├── LocationView.swift │ │ ├── PlacesScreen.swift │ │ ├── PlacesView.swift │ │ ├── StudyGroups │ │ │ ├── StudyRoomGroupListView.swift │ │ │ ├── StudyRoomGroupView.swift │ │ │ ├── StudyRoomGroupsView.swift │ │ │ └── Widget │ │ │ │ ├── StudyRoomWidgetScreen.swift │ │ │ │ └── StudyRoomWidgetView.swift │ │ └── Toolbar.swift │ └── ViewModel │ │ ├── AnnotatedMapViewModel.swift │ │ ├── CafeteriaWidgetViewModel.swift │ │ ├── DishViewModel.swift │ │ ├── MapViewModel+State.swift │ │ ├── MapViewModel.swift │ │ ├── MealPlanViewModel.swift │ │ ├── MenuViewModel.swift │ │ └── StudyRoomWidgetViewModel.swift ├── MoviesComponent │ ├── Model │ │ ├── Movie+PreviewData.swift │ │ └── Movie.swift │ ├── Screen │ │ └── MoviesScreen.swift │ ├── Service │ │ └── MovieService.swift │ ├── ViewModel │ │ ├── MovieViewModel.swift │ │ └── MoviesViewModel.swift │ └── Views │ │ ├── MovieCard.swift │ │ ├── MovieDetailCellView.swift │ │ ├── MovieDetailedView.swift │ │ ├── MovieDetailsBasicInfoRowView.swift │ │ ├── MovieDetailsBasicInfoView.swift │ │ ├── MovieDetailsDetailedInfoRowView.swift │ │ ├── MovieDetailsDetailedInfoView.swift │ │ ├── MovieView.swift │ │ ├── MoviesView.swift │ │ └── Widget │ │ └── MoviesWidgetView.swift ├── NewsComponent │ ├── Model │ │ ├── News+PreviewData.swift │ │ ├── News.swift │ │ └── NewsSource.swift │ ├── NewsScreen │ │ └── NewsScreen.swift │ ├── Screen │ │ └── NewsScreen.swift │ ├── Service │ │ └── NewsService.swift │ ├── ViewModel │ │ └── NewsViewModel.swift │ └── Views │ │ ├── NewsCard.swift │ │ ├── NewsCardsHorizontalScrollingView.swift │ │ ├── NewsView.swift │ │ └── Widget │ │ └── NewsWidgetView.swift ├── PersonDetailedComponent │ ├── Entity │ │ ├── Organization.swift │ │ ├── PersonDetails.swift │ │ ├── PhoneExtension.swift │ │ └── Room.swift │ ├── Screen │ │ ├── PersonDetailedScreenSearch.swift │ │ └── PersonDetailedScreenUser.swift │ ├── Service │ │ └── PersonDetailedService.swift │ ├── View │ │ ├── AddToContactsView.swift │ │ ├── PersonDetailedView.swift │ │ └── PersonDetailedViewUser.swift │ └── ViewModel │ │ └── PersonDetailedViewModel.swift ├── PersonSearchComponent │ ├── Entity │ │ └── Person.swift │ ├── Screen │ │ └── PersonSearchScreen.swift │ ├── Service │ │ └── PersonSearchService.swift │ ├── View │ │ └── PersonSearchView.swift │ └── ViewModel │ │ └── PersonSearchViewModel.swift ├── ProfileComponent │ ├── Entity │ │ └── Profile.swift │ ├── Service │ │ └── ProfileService.swift │ ├── View │ │ ├── ProfileToolbar.swift │ │ ├── ProfileView.swift │ │ └── TuitionScreen.swift │ └── ViewModel │ │ └── ProfileViewModel.swift ├── RoomFinderComponent │ ├── Entity │ │ ├── FoundRoom.swift │ │ └── RoomFinderLocation.swift │ ├── Model │ │ ├── Details │ │ │ ├── NavigaTumNavigationAdditionalProperties.swift │ │ │ ├── NavigaTumNavigationCoordinates.swift │ │ │ ├── NavigaTumNavigationMaps.swift │ │ │ ├── NavigaTumOverlaysMaps.swift │ │ │ └── NavigaTumRoomFinderMaps.swift │ │ ├── NavigaTumNavigationDetails.swift │ │ ├── NavigaTumNavigationEntity.swift │ │ ├── NavigaTumNavigationProperty.swift │ │ ├── NavigaTumOverlayMap.swift │ │ ├── NavigaTumRoomFinderMap.swift │ │ └── Search │ │ │ ├── NavigaTumSearchResponse.swift │ │ │ └── NavigaTumSearchResponseSection.swift │ ├── Service │ │ └── RoomFinderService.swift │ ├── ViewModel │ │ ├── NavigaTumDetailsViewModel.swift │ │ └── NavigaTumViewModel.swift │ └── Views │ │ ├── NavigaTumDetailsBaseView.swift │ │ ├── NavigaTumDetailsView.swift │ │ ├── NavigaTumListView.swift │ │ ├── NavigaTumMapImagesView.swift │ │ ├── NavigaTumMapView.swift │ │ ├── NavigaTumView.swift │ │ ├── RoomDetailsScreen.swift │ │ ├── RoomDetailsView.swift │ │ └── RoomsView.swift ├── SearchComponent │ ├── Types and Protocols │ │ ├── ComparisonToken.swift │ │ ├── Extensions │ │ │ ├── String+Keep.swift │ │ │ └── String+Levenshtein.swift │ │ ├── GlobalSearch.swift │ │ ├── SearchError.swift │ │ ├── SearchState.swift │ │ └── Searchable.swift │ ├── ViewModels │ │ ├── SearchResultViewModel.swift │ │ └── Searchable ViewModels │ │ │ ├── CafeteriaSearchResultViewModel.swift │ │ │ ├── EventSearchResultViewModel.swift │ │ │ ├── GradeSearchResultViewModel.swift │ │ │ ├── LectureSearchResultViewModel.swift │ │ │ ├── MovieSearchResultViewModel.swift │ │ │ ├── NewsSearchResultViewModel.swift │ │ │ ├── PersonSearchResultViewModel.swift │ │ │ ├── RoomFinderSearchResultViewModel.swift │ │ │ └── StudyRoomSearchResultViewModel.swift │ └── Views │ │ ├── Additional Views │ │ ├── SearchResultBarView.swift │ │ ├── SearchResultErrorView.swift │ │ └── SearchResultLoadingView.swift │ │ ├── Extensions and Custom Views │ │ ├── ExpandIcon.swift │ │ └── View+Search.swift │ │ ├── SearchResultView.swift │ │ ├── SearchResultViews │ │ ├── Cafeteria │ │ │ ├── CafeteriaSearchResultScreen.swift │ │ │ └── CafeteriaSearchResultView.swift │ │ ├── Event │ │ │ ├── EventSearchResultScreen.swift │ │ │ └── EventSearchResultView.swift │ │ ├── Grade │ │ │ ├── GradeSearchResultScreen.swift │ │ │ └── GradeSearchResultView.swift │ │ ├── Lecture │ │ │ ├── LectureSearchResultScreen.swift │ │ │ └── LectureSearchResultView.swift │ │ ├── Movie │ │ │ ├── MovieSearchResultScreen.swift │ │ │ └── MovieSearchResultView.swift │ │ ├── News │ │ │ ├── NewsSearchResultScreen.swift │ │ │ └── NewsSearchResultView.swift │ │ ├── Person │ │ │ ├── PersonSearchResultScreen.swift │ │ │ └── PersonSearchResultView.swift │ │ ├── RoomFinder │ │ │ ├── RoomFinderSearchResultScreen.swift │ │ │ └── RoomFinderSearchResultView.swift │ │ └── StudyRoom │ │ │ ├── StudyRoomSearchResultScreen.swift │ │ │ └── StudyRoomSearchResultView.swift │ │ └── SearchView.swift ├── Secrets.xcconfig ├── Styles │ └── Modifiers │ │ └── RoundedCorners.swift ├── TUMSexyComponent │ ├── Model │ │ └── TUMSexyLink.swift │ ├── Screen │ │ └── TUMSexyScreen.swift │ ├── Service │ │ └── TUMSexyService.swift │ ├── ViewModel │ │ └── TUMSexyViewModel.swift │ └── Views │ │ └── TUMSexyView.swift ├── TuitionComponent │ ├── Model │ │ └── Tuition.swift │ └── View │ │ ├── TuitionCard.swift │ │ ├── TuitionDetailsView.swift │ │ ├── TuitionScreen.swift │ │ └── TuitionView.swift ├── WidgetComponent │ ├── Recommender │ │ ├── RecommenderError.swift │ │ ├── Strategy │ │ │ ├── LocationStrategy.swift │ │ │ ├── MLModelDataHandler.swift │ │ │ ├── SpatioTemporalStrategy.swift │ │ │ ├── TimeStrategy.swift │ │ │ └── WidgetRecommenderStrategy.swift │ │ ├── Widget.swift │ │ ├── WidgetRecommendation.swift │ │ └── WidgetRecommender.swift │ └── View │ │ └── WidgetLoadingView.swift └── de.lproj │ └── Main.strings ├── Campus-iOSTests └── Campus_iOSTests.swift ├── Campus-iOSUITests ├── Campus_iOSUITests.swift └── Campus_iOSUITestsLaunchTests.swift ├── LICENSE ├── PULL_REQUEST_TEMPLATE.md ├── README.md └── default.profraw /.github/FUNDING.yml: -------------------------------------------------------------------------------- 1 | # These are supported funding model platforms 2 | 3 | github: TUM-Dev 4 | open_collective: tum-dev 5 | -------------------------------------------------------------------------------- /.github/workflows/xcodebuild.yml: -------------------------------------------------------------------------------- 1 | name: xcodebuild 2 | 3 | on: 4 | push: 5 | branches: [ master ] 6 | pull_request: 7 | branches: [ master ] 8 | 9 | jobs: 10 | build: 11 | 12 | runs-on: macos-latest 13 | 14 | steps: 15 | - uses: actions/checkout@v2 16 | - uses: actions/cache@v2 17 | with: 18 | path: .cache 19 | key: ${{ runner.os }}-spm-v2-${{ hashFiles('**/Package.resolved') }} 20 | restore-keys: ${{ runner.os }}-spm-v2- 21 | - name: Build 22 | run: xcodebuild -scheme "TUM Campus App" -clonedSourcePackagesDirPath .cache -destination "platform=iOS Simulator,name=iPhone 11 Pro" 23 | -------------------------------------------------------------------------------- /Analytics.xcdatamodeld/Analytics.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | -------------------------------------------------------------------------------- /CONTRIBUTORS.md: -------------------------------------------------------------------------------- 1 | ## Ehrenamtliche Beitragende 2 | Hier sind alle jene aufgeführt, welche in Ihrer Freizeit sich dem Projekt widmen und auf freiwilliger Basis Beiträge leisten, damit diese App weiter existiert. 3 | 4 | #### Projektmanagement: 5 | + [TUM - Computer Science / F13 Fachgebiet für Betriebssysteme](https://www.os.in.tum.de/startseite/) 6 | + [Prof. Dr. Uwe Baumgarten](https://www.os.in.tum.de/personen/baumgarten/) 7 | 8 | #### Technische Unterstützung: 9 | + Andreas Bernhofer 10 | + Bernhard Blieninger 11 | + [Kordian Bruck](https://github.com/kordianbruck/) 12 | 13 | #### Aktive Entwickler 14 | + [Mathias Quintero](https://github.com/mathiasquintero) 15 | + [Max Muth](https://github.com/mammuth) 16 | + [Tim Gymnich](https://github.com/TG908) 17 | 18 | #### Weitere Entwickler: 19 | + [Lukas Kollmer](https://github.com/lukaskollmer) 20 | + [rodalfus](https://github.com/rodalfus) 21 | + [Larry Zuo](https://github.com/larryzuo) 22 | + [rodalfus](https://github.com/rodalfus) 23 | + [Sandra](https://github.com/melloskitten) 24 | -------------------------------------------------------------------------------- /Campus-iOS.xcodeproj/project.xcworkspace/contents.xcworkspacedata: -------------------------------------------------------------------------------- 1 | 2 | 4 | 6 | 7 | 8 | -------------------------------------------------------------------------------- /Campus-iOS.xcodeproj/project.xcworkspace/xcshareddata/IDEWorkspaceChecks.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | IDEDidComputeMac32BitWarning 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Campus-iOS.xcodeproj/xcshareddata/xcbaselines/366F0E9427580CFD0091651D.xcbaseline/3439EAFF-1FCF-41B1-96A9-2FD6666B9105.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | classNames 6 | 7 | Campus_iOSTests 8 | 9 | testPerformanceExample() 10 | 11 | com.apple.XCTPerformanceMetric_WallClockTime 12 | 13 | baselineAverage 14 | 0.005971 15 | baselineIntegrationDisplayName 16 | Local Baseline 17 | 18 | 19 | 20 | 21 | 22 | 23 | -------------------------------------------------------------------------------- /Campus-iOS.xcodeproj/xcshareddata/xcbaselines/366F0E9427580CFD0091651D.xcbaseline/Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | runDestinationsByUUID 6 | 7 | 3439EAFF-1FCF-41B1-96A9-2FD6666B9105 8 | 9 | localComputer 10 | 11 | busSpeedInMHz 12 | 0 13 | cpuCount 14 | 1 15 | cpuKind 16 | Apple M1 17 | cpuSpeedInMHz 18 | 0 19 | logicalCPUCoresPerPackage 20 | 8 21 | modelCode 22 | MacBookAir10,1 23 | physicalCPUCoresPerPackage 24 | 8 25 | platformIdentifier 26 | com.apple.platform.macosx 27 | 28 | targetArchitecture 29 | arm64 30 | targetDevice 31 | 32 | modelCode 33 | iPhone15,2 34 | platformIdentifier 35 | com.apple.platform.iphonesimulator 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/3D.imageset/3D@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/3D.imageset/3D@2x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/3D.imageset/3D@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/3D.imageset/3D@3x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/3D.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "3D@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "3D@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AccentColor.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "idiom" : "universal" 5 | } 6 | ], 7 | "info" : { 8 | "author" : "xcode", 9 | "version" : 1 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@1x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x-1.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@2x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-20x20@3x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@1x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x-1.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@2x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-29x29@3x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@1x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x-1.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@2x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-40x40@3x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@2x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-60x60@3x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@1x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-76x76@2x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/Icon-App-83.5x83.5@2x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/ItunesArtwork@2x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 2.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 3-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 3-1.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 3.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 4.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 5.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 6-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 6-1.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 6.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 7-1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 7-1.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 7.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/AppIcon.appiconset/slice1@3x copy 8.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Campus/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Campus/campus-freising.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "campus-freising.jpg", 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 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Campus/campus-freising.imageset/campus-freising.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Campus/campus-freising.imageset/campus-freising.jpg -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Campus/campus-garching.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "campus-garching.jpeg", 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 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Campus/campus-garching.imageset/campus-garching.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Campus/campus-garching.imageset/campus-garching.jpeg -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Campus/campus-klinikum.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "campus-klinikum.jpeg", 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 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Campus/campus-klinikum.imageset/campus-klinikum.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Campus/campus-klinikum.imageset/campus-klinikum.jpeg -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Campus/campus-olympia.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "campus-olympia.jpeg", 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 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Campus/campus-olympia.imageset/campus-olympia.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Campus/campus-olympia.imageset/campus-olympia.jpeg -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Campus/campus-stamm.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "campus-stamm.jpeg", 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 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Campus/campus-stamm.imageset/campus-stamm.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Campus/campus-stamm.imageset/campus-stamm.jpeg -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Colors/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Colors/contrastText.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xFF", 9 | "green" : "0xFF", 10 | "red" : "0xFF" 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" : "0x00", 27 | "green" : "0x00", 28 | "red" : "0x00" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Colors/primaryBackground.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xF7", 9 | "green" : "0xF2", 10 | "red" : "0xF2" 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" : "0x19", 27 | "green" : "0x19", 28 | "red" : "0x19" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Colors/primaryText.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0x00", 9 | "green" : "0x00", 10 | "red" : "0x00" 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" : "0xFF", 27 | "green" : "0xFF", 28 | "red" : "0xFF" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Colors/secondaryBackground.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xFF", 9 | "green" : "0xFF", 10 | "red" : "0xFF" 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" : "0x25", 27 | "green" : "0x25", 28 | "red" : "0x25" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Colors/tumBlue.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xBC", 9 | "green" : "0x64", 10 | "red" : "0x00" 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" : "0xB3", 27 | "green" : "0x70", 28 | "red" : "0x30" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Colors/tumBlueWhite.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.741", 9 | "green" : "0.396", 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 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Colors/tumBrand.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "srgb", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0xB3", 9 | "green" : "0x70", 10 | "red" : "0x30" 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" : "0xB3", 27 | "green" : "0x70", 28 | "red" : "0x30" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Error-logo-transparent.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "Error-logo-transparent.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 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Error-logo-transparent.imageset/Error-logo-transparent.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Error-logo-transparent.imageset/Error-logo-transparent.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Error-logo.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "3_Something Went Wrong@2x.svg", 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 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Logos/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Logos/logo-blue-responsive.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "tum-blue-light.svg", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "filename" : "tum-blue-dark.svg", 16 | "idiom" : "universal", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "idiom" : "universal", 21 | "scale" : "2x" 22 | }, 23 | { 24 | "appearances" : [ 25 | { 26 | "appearance" : "luminosity", 27 | "value" : "dark" 28 | } 29 | ], 30 | "idiom" : "universal", 31 | "scale" : "2x" 32 | }, 33 | { 34 | "idiom" : "universal", 35 | "scale" : "3x" 36 | }, 37 | { 38 | "appearances" : [ 39 | { 40 | "appearance" : "luminosity", 41 | "value" : "dark" 42 | } 43 | ], 44 | "idiom" : "universal", 45 | "scale" : "3x" 46 | } 47 | ], 48 | "info" : { 49 | "author" : "xcode", 50 | "version" : 1 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Logos/logo-blue-responsive.imageset/tum-blue-dark.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Logos/logo-blue-responsive.imageset/tum-blue-light.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Logos/logo-blue.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "TUMLogo_oZ_Vollfl_blau_RGB.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 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Logos/logo-blue.imageset/TUMLogo_oZ_Vollfl_blau_RGB.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Logos/logo-blue.imageset/TUMLogo_oZ_Vollfl_blau_RGB.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Logos/logo-rainbow.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "logo-rainbow.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 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Logos/logo-rainbow.imageset/logo-rainbow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Logos/logo-rainbow.imageset/logo-rainbow.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "logo-black.png", 5 | "idiom" : "universal", 6 | "scale" : "1x" 7 | }, 8 | { 9 | "appearances" : [ 10 | { 11 | "appearance" : "luminosity", 12 | "value" : "dark" 13 | } 14 | ], 15 | "filename" : "logo-white.png", 16 | "idiom" : "universal", 17 | "scale" : "1x" 18 | }, 19 | { 20 | "filename" : "logo-black 1.png", 21 | "idiom" : "universal", 22 | "scale" : "2x" 23 | }, 24 | { 25 | "appearances" : [ 26 | { 27 | "appearance" : "luminosity", 28 | "value" : "dark" 29 | } 30 | ], 31 | "filename" : "logo-white 1.png", 32 | "idiom" : "universal", 33 | "scale" : "2x" 34 | }, 35 | { 36 | "filename" : "logo-black 2.png", 37 | "idiom" : "universal", 38 | "scale" : "3x" 39 | }, 40 | { 41 | "appearances" : [ 42 | { 43 | "appearance" : "luminosity", 44 | "value" : "dark" 45 | } 46 | ], 47 | "filename" : "logo-white 2.png", 48 | "idiom" : "universal", 49 | "scale" : "3x" 50 | } 51 | ], 52 | "info" : { 53 | "author" : "xcode", 54 | "version" : 1 55 | } 56 | } 57 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-black 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-black 1.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-black 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-black 2.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-black.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-white 1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-white 1.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-white 2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-white 2.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-white.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Logos/logo-responsive.imageset/logo-white.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Logos/logo-white.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "TUM2.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 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/Logos/logo-white.imageset/TUM2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/Logos/logo-white.imageset/TUM2.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/darkGray.colorset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "colors" : [ 3 | { 4 | "color" : { 5 | "color-space" : "display-p3", 6 | "components" : { 7 | "alpha" : "1.000", 8 | "blue" : "0.288", 9 | "green" : "0.280", 10 | "red" : "0.281" 11 | } 12 | }, 13 | "idiom" : "universal" 14 | } 15 | ], 16 | "info" : { 17 | "author" : "xcode", 18 | "version" : 1 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/default.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "default@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "default@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/default.imageset/default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/default.imageset/default@2x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/default.imageset/default@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/default.imageset/default@3x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/movie.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "movie-placeholder.jpg", 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 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/movie.imageset/movie-placeholder.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/movie.imageset/movie-placeholder.jpg -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/outline.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "outline@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "outline@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/outline.imageset/outline@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/outline.imageset/outline@2x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/outline.imageset/outline@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/outline.imageset/outline@3x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/placeholder.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "placeholder@2x.heic", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "placeholder@3x.heic", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/placeholder.imageset/placeholder@2x.heic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/placeholder.imageset/placeholder@2x.heic -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/placeholder.imageset/placeholder@3x.heic: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/placeholder.imageset/placeholder@3x.heic -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/pride.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "pride@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "pride@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/pride.imageset/pride@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/pride.imageset/pride@2x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/pride.imageset/pride@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/pride.imageset/pride@3x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/set-permissions.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "set-permissions.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 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/set-permissions.imageset/set-permissions.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/set-permissions.imageset/set-permissions.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/token_step1.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "token_step1.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 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/token_step1.imageset/token_step1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/token_step1.imageset/token_step1.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/token_step2.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "token_step2.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 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/token_step2.imageset/token_step2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/token_step2.imageset/token_step2.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/token_step3.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "token_step3.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 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/token_step3.imageset/token_step3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/token_step3.imageset/token_step3.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/token_step4.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "token_step4.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 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/token_step4.imageset/token_step4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/token_step4.imageset/token_step4.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/token_step5.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "token_step5.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 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/token_step5.imageset/token_step5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/token_step5.imageset/token_step5.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/tower.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "image4-94.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 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/tower.imageset/image4-94.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/tower.imageset/image4-94.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/tumtower.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "filename" : "tumtower.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 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/tumtower.imageset/tumtower.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/tumtower.imageset/tumtower.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/white.imageset/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "images" : [ 3 | { 4 | "idiom" : "universal", 5 | "scale" : "1x" 6 | }, 7 | { 8 | "filename" : "white@2x.png", 9 | "idiom" : "universal", 10 | "scale" : "2x" 11 | }, 12 | { 13 | "filename" : "white@3x.png", 14 | "idiom" : "universal", 15 | "scale" : "3x" 16 | } 17 | ], 18 | "info" : { 19 | "author" : "xcode", 20 | "version" : 1 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/white.imageset/white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/white.imageset/white@2x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/white.imageset/white@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Assets.xcassets/white.imageset/white@3x.png -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/whiteTumBlue.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" : "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" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0.741", 27 | "green" : "0.396", 28 | "red" : "0.000" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Campus-iOS/Assets.xcassets/widgetColor.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" : "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" : "srgb", 24 | "components" : { 25 | "alpha" : "1.000", 26 | "blue" : "0.200", 27 | "green" : "0.200", 28 | "red" : "0.200" 29 | } 30 | }, 31 | "idiom" : "universal" 32 | } 33 | ], 34 | "info" : { 35 | "author" : "xcode", 36 | "version" : 1 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Constants/Constants.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Constants.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 22.12.21. 6 | // 7 | 8 | import Foundation 9 | 10 | enum Constants { 11 | static let tokenManagementTUMOnlineUrl = URL(string: "https://campus.tum.de/tumonline/ee/ui/ca2/app/desktop/#/pl/ui/$ctx/wbservicesadmin.userTokenManagement?$ctx=")! 12 | } 13 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Errors/BackendError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Errors.swift 3 | // Campus-iOS 4 | // 5 | // Created by Milen Vitanov on 15.12.21. 6 | // 7 | 8 | enum BackendError: Error { 9 | case network(error: Error) 10 | case AFError(message: String) 11 | case dataSerialization(error: Error) 12 | case jsonSerialization(error: Error) 13 | case xmlSerialization(error: Error) 14 | case objectSerialization(reason: String) 15 | } 16 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Errors/Environment+Error.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Environment+Error.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 22.12.21. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct ErrorHandlerEnvironmentKey: EnvironmentKey { 12 | static var defaultValue: ErrorHandler = AlertErrorHandler() 13 | } 14 | 15 | extension EnvironmentValues { 16 | var errorHandler: ErrorHandler { 17 | get { self[ErrorHandlerEnvironmentKey.self] } 18 | set { self[ErrorHandlerEnvironmentKey.self] = newValue } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Errors/Error+Category.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Error+Category.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 22.12.21. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Error { 11 | func resolveCategory() -> ErrorCategory { 12 | guard let categorized = self as? CategorizedError else { 13 | // We could optionally choose to trigger an assertion 14 | // here, if we consider it important that all of our 15 | // errors have categories assigned to them. 16 | return .nonRetryable 17 | } 18 | 19 | return categorized.category 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Errors/ErrorCategory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorCategory.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 22.12.21. 6 | // 7 | 8 | import Foundation 9 | 10 | enum ErrorCategory { 11 | case nonRetryable 12 | case retryable 13 | case requiresLogout 14 | } 15 | 16 | protocol CategorizedError: Error { 17 | var category: ErrorCategory { get } 18 | } 19 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Errors/ErrorEmittingViewModifier.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorEmittingViewModifier.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 22.12.21. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct ErrorEmittingViewModifier: ViewModifier { 12 | @EnvironmentObject var customEnvironmentValues: Model 13 | @Environment(\.errorHandler) var handler 14 | 15 | var error: Error? 16 | var retryHandler: () -> Void 17 | 18 | func body(content: Content) -> some View { 19 | handler.handle(error, 20 | in: content, 21 | customEnvironmentValues: customEnvironmentValues, 22 | retryHandler: retryHandler 23 | ) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Errors/ErrorHandler.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ErrorHandler.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 22.12.21. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | protocol ErrorHandler { 12 | func handle( 13 | _ error: Error?, 14 | in view: T, 15 | customEnvironmentValues: Model, 16 | retryHandler: @escaping () -> Void 17 | ) -> AnyView 18 | } 19 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Errors/View+Error.swift: -------------------------------------------------------------------------------- 1 | // 2 | // View+Error.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 22.12.21. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | extension View { 12 | func handlingErrors( 13 | using handler: ErrorHandler 14 | ) -> some View { 15 | environment(\.errorHandler, handler) 16 | } 17 | } 18 | 19 | extension View { 20 | func emittingError( 21 | _ error: Error?, 22 | retryHandler: @escaping () -> Void 23 | ) -> some View { 24 | modifier(ErrorEmittingViewModifier( 25 | error: error, 26 | retryHandler: retryHandler 27 | )) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Helpers/DecoderProtocol.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DecoderProtocol.swift 3 | // Campus-iOS 4 | // 5 | // Created by Milen Vitanov on 13.01.22. 6 | // 7 | 8 | import Foundation 9 | import Alamofire 10 | 11 | protocol DecoderProtocol: AnyObject, DataDecoder { 12 | associatedtype DateDecodingStrategy: DecodingStrategyProtocol 13 | func decode(_ type: T.Type, from data: Data) throws -> T where T : Decodable 14 | var userInfo: [CodingUserInfoKey : Any] { get set } 15 | var dateDecodingStrategy: DateDecodingStrategy { get set } 16 | static var contentType: [String] { get } 17 | static func instantiate() -> Self 18 | } 19 | 20 | protocol DecodingStrategyProtocol { } 21 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Icons/3D@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Base/Icons/3D@2x.png -------------------------------------------------------------------------------- /Campus-iOS/Base/Icons/3D@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Base/Icons/3D@3x.png -------------------------------------------------------------------------------- /Campus-iOS/Base/Icons/default@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Base/Icons/default@2x.png -------------------------------------------------------------------------------- /Campus-iOS/Base/Icons/default@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Base/Icons/default@3x.png -------------------------------------------------------------------------------- /Campus-iOS/Base/Icons/outline@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Base/Icons/outline@2x.png -------------------------------------------------------------------------------- /Campus-iOS/Base/Icons/outline@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Base/Icons/outline@3x.png -------------------------------------------------------------------------------- /Campus-iOS/Base/Icons/pride@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Base/Icons/pride@2x.png -------------------------------------------------------------------------------- /Campus-iOS/Base/Icons/pride@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Base/Icons/pride@3x.png -------------------------------------------------------------------------------- /Campus-iOS/Base/Icons/white@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Base/Icons/white@2x.png -------------------------------------------------------------------------------- /Campus-iOS/Base/Icons/white@3x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Base/Icons/white@3x.png -------------------------------------------------------------------------------- /Campus-iOS/Base/Model/MockModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MockModel.swift 3 | // Campus-iOS 4 | // 5 | // Created by Milen Vitanov on 01.12.21. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | import SwiftUI 11 | 12 | public class MockModel: Model { 13 | 14 | 15 | } 16 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Networking/APIErrors/EatAPIError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // EatAPIError.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 10.02.23. 6 | // 7 | 8 | import Foundation 9 | 10 | enum EatAPIError: APIError, LocalizedError { 11 | case unknown(String) 12 | 13 | enum CodingKeys: String, CodingKey { 14 | case message 15 | } 16 | 17 | init(from decoder: Decoder) throws { 18 | let container = try decoder.container(keyedBy: CodingKeys.self) 19 | let error = try container.decode(String.self, forKey: .message) 20 | 21 | switch error { 22 | default: 23 | self = .unknown(error) 24 | } 25 | } 26 | 27 | init(message: String) { 28 | self = .unknown(message) 29 | } 30 | 31 | public var errorDescription: String? { 32 | switch self { 33 | case let .unknown(message): 34 | return "\("Unkonw error".localized): \(message)" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Networking/APIErrors/MVGAPIError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MVGAPIError.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 10.02.23. 6 | // 7 | 8 | import Foundation 9 | 10 | enum MVGAPIError: APIError, LocalizedError { 11 | case unknown(String) 12 | 13 | enum CodingKeys: String, CodingKey { 14 | case message 15 | } 16 | 17 | init(from decoder: Decoder) throws { 18 | let container = try decoder.container(keyedBy: CodingKeys.self) 19 | let error = try container.decode(String.self, forKey: .message) 20 | 21 | switch error { 22 | default: 23 | self = .unknown(error) 24 | } 25 | } 26 | 27 | init(message: String) { 28 | self = .unknown(message) 29 | } 30 | 31 | public var errorDescription: String? { 32 | switch self { 33 | case let .unknown(message): 34 | return "\("Unkonw error".localized): \(message)" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Networking/APIErrors/NavigaTUMAPIError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NAvigaTUMAPIError.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 10.04.23. 6 | // 7 | 8 | import Foundation 9 | 10 | enum NavigaTUMAPIError: APIError, LocalizedError { 11 | case unknown(String) 12 | 13 | enum CodingKeys: String, CodingKey { 14 | case message 15 | } 16 | 17 | init(from decoder: Decoder) throws { 18 | let container = try decoder.container(keyedBy: CodingKeys.self) 19 | let error = try container.decode(String.self, forKey: .message) 20 | 21 | switch error { 22 | default: 23 | self = .unknown(error) 24 | } 25 | } 26 | 27 | init(message: String) { 28 | self = .unknown(message) 29 | } 30 | 31 | public var errorDescription: String? { 32 | switch self { 33 | case let .unknown(message): 34 | return "\("Unkonw error".localized): \(message)" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Networking/APIErrors/TUMCabeAPIError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TUMCabeAPIError.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 10.02.23. 6 | // 7 | 8 | import Foundation 9 | 10 | enum TUMCabeAPIError: APIError, LocalizedError { 11 | case unknown(String) 12 | 13 | enum CodingKeys: String, CodingKey { 14 | case message 15 | } 16 | 17 | init(from decoder: Decoder) throws { 18 | let container = try decoder.container(keyedBy: CodingKeys.self) 19 | let error = try container.decode(String.self, forKey: .message) 20 | 21 | switch error { 22 | default: 23 | self = .unknown(error) 24 | } 25 | } 26 | 27 | init(message: String) { 28 | self = .unknown(message) 29 | } 30 | 31 | public var errorDescription: String? { 32 | switch self { 33 | case let .unknown(message): 34 | return "\("Unkonw error".localized): \(message)" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Networking/APIErrors/TUMDevAppAPIError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TUMDevAppAPI.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 10.02.23. 6 | // 7 | 8 | import Foundation 9 | 10 | enum TUMDevAppAPIError: APIError, LocalizedError { 11 | case unknown(String) 12 | 13 | enum CodingKeys: String, CodingKey { 14 | case message 15 | } 16 | 17 | init(from decoder: Decoder) throws { 18 | let container = try decoder.container(keyedBy: CodingKeys.self) 19 | let error = try container.decode(String.self, forKey: .message) 20 | 21 | switch error { 22 | default: 23 | self = .unknown(error) 24 | } 25 | } 26 | 27 | init(message: String) { 28 | self = .unknown(message) 29 | } 30 | 31 | public var errorDescription: String? { 32 | switch self { 33 | case let .unknown(message): 34 | return "\("Unkonw error".localized): \(message)" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Networking/APIErrors/TUMSexyAPIError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TUMSexyAPIError.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 10.02.23. 6 | // 7 | 8 | import Foundation 9 | 10 | enum TUMSexyAPIError: APIError, LocalizedError { 11 | case unknown(String) 12 | 13 | enum CodingKeys: String, CodingKey { 14 | case message 15 | } 16 | 17 | init(from decoder: Decoder) throws { 18 | let container = try decoder.container(keyedBy: CodingKeys.self) 19 | let error = try container.decode(String.self, forKey: .message) 20 | 21 | switch error { 22 | default: 23 | self = .unknown(error) 24 | } 25 | } 26 | 27 | init(message: String) { 28 | self = .unknown(message) 29 | } 30 | 31 | public var errorDescription: String? { 32 | switch self { 33 | case let .unknown(message): 34 | return "\("Unkonw error".localized): \(message)" 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Networking/APIResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // APIResponse.swift 3 | // TUM Campus App 4 | // 5 | // Created by Tim Gymnich on 2/27/19. 6 | // Copyright © 2019 TUM. All rights reserved. 7 | // 8 | 9 | import Foundation 10 | import FirebaseCrashlytics 11 | 12 | //struct APIResponse: Decodable { 13 | // var response: ResponseType 14 | // 15 | // init(from decoder: Decoder) throws { 16 | // if let error = try? ErrorType(from: decoder) { 17 | // throw error 18 | // } else { 19 | // let response = try ResponseType(from: decoder) 20 | // self.response = response 21 | // } 22 | // } 23 | //} 24 | // 25 | //struct TUMOnlineAPIResponse: Decodable { 26 | // var rows: [T]? 27 | // 28 | // enum CodingKeys: String, CodingKey { 29 | // case rows = "row" 30 | // } 31 | // 32 | // init(from decoder: Decoder) throws { 33 | // let container = try decoder.container(keyedBy: CodingKeys.self) 34 | // self.rows = try container.decode([Throwable].self, forKey: .rows).compactMap { 35 | // do { 36 | // return try $0.result.get() 37 | // } 38 | // catch { 39 | // CrashlyticsService.log(error) 40 | // return nil 41 | // } 42 | // } 43 | // } 44 | //} 45 | // 46 | //struct Throwable: Decodable { 47 | // let result: Result 48 | // 49 | // init(from decoder: Decoder) throws { 50 | // result = Result(catching: { try T(from: decoder) }) 51 | // } 52 | //} 53 | 54 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Networking/APIs/MVVDeparturesAPI.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MVVDeparturesAPI.swift 3 | // Campus-iOS 4 | // 5 | // Created by Jakob Paul Körber on 28.02.23. 6 | // 7 | 8 | import Alamofire 9 | import Foundation 10 | import CoreLocation 11 | 12 | struct MVVDeparturesAPI: URLRequestConvertible { 13 | 14 | static let baseURLStringPrefix = "https://efa.mvv-muenchen.de/ng/XML_DM_REQUEST?outputFormat=JSON&language=en&stateless=1&coordOutputFormat=WGS84&type_dm=stop&name_dm=" 15 | static let baseURLStringMiddle = "&timeOffset=" 16 | static let baseURLStringSufix = "&useRealtime=1&itOptionsActive=1&ptOptionsActive=1&limit=20&mergeDep=1&useAllStops=1&mode=direct" 17 | 18 | let station: String 19 | let walkingTime: Int? 20 | 21 | var method: HTTPMethod { 22 | switch self { 23 | default: 24 | return .get 25 | } 26 | } 27 | 28 | func asURLRequest() throws -> URLRequest { 29 | if let walkingTime { 30 | let url = try ("\(MVVDeparturesAPI.baseURLStringPrefix)\(station)\(MVVDeparturesAPI.baseURLStringMiddle)\(walkingTime)\(MVVDeparturesAPI.baseURLStringSufix)").asURL() 31 | let urlRequest = try URLRequest(url: url, method: method) 32 | return urlRequest 33 | } else { 34 | let url = try ("\(MVVDeparturesAPI.baseURLStringPrefix)\(station)\(MVVDeparturesAPI.baseURLStringSufix)").asURL() 35 | let urlRequest = try URLRequest(url: url, method: method) 36 | return urlRequest 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Networking/APIs/TUMDevAppAPI.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TUMDevAppAPI.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 10.02.23. 6 | // 7 | 8 | import Foundation 9 | import Alamofire 10 | 11 | enum TUMDevAppAPI: API { 12 | case room(roomNr: Int) 13 | case rooms 14 | 15 | static var baseURL: String = "https://www.devapp.it.tum.de/" 16 | 17 | static var baseHeaders: Alamofire.HTTPHeaders = [] 18 | 19 | static var error: APIError.Type = TUMDevAppAPIError.self 20 | 21 | var paths: String { 22 | switch self { 23 | case .room, .rooms: return "iris/ris_api.php" 24 | } 25 | } 26 | 27 | var parameters: [String : String] { 28 | switch self { 29 | case .room(roomNr: let roomNr): 30 | return ["format": "json", "raum": String(roomNr)] 31 | case .rooms: 32 | return ["format": "json"] 33 | } 34 | } 35 | 36 | var needsAuth: Bool { false } 37 | 38 | func decode(_ type: T.Type, from data: Data) throws -> T where T : Decodable { 39 | return try JSONDecoder().decode(type, from: data) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Networking/APIs/TUMSexyAPI.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TUMSexyAPI.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 10.02.23. 6 | // 7 | 8 | import Foundation 9 | import Alamofire 10 | 11 | enum TUMSexyAPI: API { 12 | case standard 13 | 14 | static var baseURL: String = "https://json.tum.sexy/" 15 | 16 | static var baseHeaders: Alamofire.HTTPHeaders = [] 17 | 18 | static var error: APIError.Type = TUMSexyAPIError.self 19 | 20 | var paths: String { "" } 21 | 22 | var parameters: [String : String] { [:] } 23 | 24 | var needsAuth: Bool { false } 25 | 26 | func decode(_ type: T.Type, from data: Data) throws -> T where T : Decodable { 27 | return try JSONDecoder().decode(type, from: data) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Networking/NetworkingAPI.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NetworkingAPI.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 22.12.21. 6 | // 7 | 8 | import Foundation 9 | import Combine 10 | 11 | protocol NetworkingAPI { 12 | // Renaming to `DecoderType` as we otherwise have a conflict between the `Decoder` associatedtype of `Decodable` and the `Decoder` associatedtype of `NetworkingAPI` 13 | associatedtype DecoderType: TopLevelDecoder 14 | static var decoder: DecoderType { get } 15 | static var cache: Cache { get } 16 | 17 | static func makeRequest(endpoint: APIConstants, token: String?, forcedRefresh: Bool?) async throws -> T 18 | } 19 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Networking/Protocols/APIError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // APIErrors.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 19.01.23. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol APIError: Error, Decodable { 11 | init(message: String) 12 | } 13 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Networking/Protocols/Service.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ServiceProtocols.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 20.01.23. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol ServiceTokenProtocol { 11 | associatedtype T : Decodable 12 | func fetch(token: String, forcedRefresh: Bool) async throws -> [T] 13 | } 14 | 15 | protocol ServiceProtocol { 16 | associatedtype T : Decodable 17 | func fetch(forcedRefresh: Bool) async throws -> [T] 18 | } 19 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Preview Content/Preview Assets.xcassets/Contents.json: -------------------------------------------------------------------------------- 1 | { 2 | "info" : { 3 | "author" : "xcode", 4 | "version" : 1 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /Campus-iOS/Base/VideoAssets/token-tutorial.mov: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/Base/VideoAssets/token-tutorial.mov -------------------------------------------------------------------------------- /Campus-iOS/Base/Views/GroupBoxLabelView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GroupBoxLabelView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 24.12.21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct GroupBoxLabelView: View { 11 | var iconName: String 12 | var text: String 13 | 14 | var body: some View { 15 | HStack { 16 | Image(systemName: iconName) 17 | .imageScale(.medium) 18 | .font(.headline.bold()) 19 | Text(text) 20 | .font(.headline.bold()) 21 | }.foregroundColor(Color("tumBlue")) 22 | } 23 | } 24 | 25 | struct GroupBoxLabelView_Previews: PreviewProvider { 26 | static var previews: some View { 27 | GroupBoxLabelView(iconName: "graduationcap.fill", text: "Wintersemester 2021/22") 28 | } 29 | } 30 | 31 | 32 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Views/LoadingView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoadingView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 21.12.21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct LoadingView: View { 11 | let text: String 12 | let position: LoadingViewPosition 13 | 14 | init(text: String, position: LoadingViewPosition = .middle) { 15 | self.text = text 16 | self.position = position 17 | } 18 | 19 | var body: some View { 20 | GeometryReader { geo in 21 | VStack { 22 | VStack(spacing: 8) { 23 | ProgressView() 24 | Text(text) 25 | } 26 | }.position(x: geo.size.width/2, y: geo.size.height * position.rawValue) 27 | } 28 | .background(Color.primaryBackground) 29 | } 30 | } 31 | 32 | struct LoadingView_Previews: PreviewProvider { 33 | static var previews: some View { 34 | LoadingView(text: "Fetching Grades") 35 | } 36 | } 37 | 38 | enum LoadingViewPosition: Double { 39 | case middle = 0.5 40 | case middletop = 0.25 41 | } 42 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Views/NoDataView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TestView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Mohanned Kandil on 23.10.2023. 6 | // 7 | 8 | 9 | import SwiftUI 10 | 11 | struct NoDataView: View { 12 | let description: String 13 | 14 | init(description: String) { 15 | self.description = description; 16 | } 17 | 18 | var body: some View { 19 | VStack (alignment: .center) { 20 | Text(description.localized) 21 | .multilineTextAlignment(.center) 22 | } 23 | .frame(maxWidth: .infinity, maxHeight: .infinity) 24 | } 25 | } 26 | 27 | struct NoDataView_Previews: PreviewProvider { 28 | static var previews: some View { 29 | NoDataView(description: "No data") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Campus-iOS/Base/Views/SFSafariViewWrapper.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SFSafariViewWrapper.swift 3 | // Campus-iOS 4 | // 5 | // Created by Milen Vitanov on 19.01.22. 6 | // 7 | 8 | import SwiftUI 9 | import SafariServices 10 | 11 | struct SFSafariViewWrapper: UIViewControllerRepresentable { 12 | let url: URL 13 | 14 | func makeUIViewController(context: UIViewControllerRepresentableContext) -> SFSafariViewController { 15 | return SFSafariViewController(url: url) 16 | } 17 | 18 | func updateUIViewController(_ uiViewController: SFSafariViewController, context: UIViewControllerRepresentableContext) { 19 | return 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/CalendarComponent/Service/CalendarService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CalendarService.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 20.01.23. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol CalendarServiceProtocol { 11 | func fetch(token: String, forcedRefresh: Bool) async throws -> [CalendarEvent] 12 | } 13 | 14 | struct CalendarService: ServiceTokenProtocol, CalendarServiceProtocol { 15 | 16 | func fetch(token: String, forcedRefresh: Bool = false) async throws -> [CalendarEvent] { 17 | let response: TUMOnlineAPI.CalendarResponse = try await MainAPI.makeRequest(endpoint: TUMOnlineAPI.calendar, token: token, forcedRefresh: forcedRefresh) 18 | 19 | return response.event 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/CalendarComponent/Views/Widget/CalendarWidgetScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CalendarWidgetScreen.swift 3 | // Campus-iOS 4 | // 5 | // Created by Timothy Summers on 19.01.23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CalendarWidgetScreen: View { 11 | 12 | @StateObject var vm: CalendarViewModel 13 | 14 | var body: some View { 15 | switch self.vm.state { 16 | case .success(data: _): 17 | CalendarWidgetView(vm: self.vm) 18 | case .loading: 19 | ProgressView() 20 | case .failed(error: let error): 21 | EmptyView().onAppear{ 22 | print(error) 23 | } 24 | case .na: 25 | EmptyView() 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Campus-iOS/Campus-iOS.entitlements: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | com.apple.security.personal-information.location 6 | 7 | 8 | 9 | -------------------------------------------------------------------------------- /Campus-iOS/Crashlytics/CrashlyticsService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CrashlyticsService.swift 3 | // Campus-iOS 4 | // 5 | // Created by Anton Wyrowski on 28.01.23. 6 | // 7 | 8 | import Foundation 9 | import FirebaseCrashlytics 10 | 11 | class CrashlyticsService { 12 | static private let crashlytics = Crashlytics.crashlytics() 13 | 14 | static func log(_ error: Error) -> Void { 15 | #if !DEBUG 16 | CrashlyticsService.crashlytics.record(error: error) 17 | #endif 18 | } 19 | 20 | static func log(_ value: String) -> Void { 21 | #if !DEBUG 22 | CrashlyticsService.crashlytics.log(value) 23 | #endif 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Campus-iOS/DataModels/Campus_iOS.xcdatamodeld/.xccurrentversion: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | _XCCurrentVersionName 6 | Campus_iOS.xcdatamodel 7 | 8 | 9 | -------------------------------------------------------------------------------- /Campus-iOS/DataModels/Campus_iOS.xcdatamodeld/Campus_iOS.xcdatamodel/contents: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /Campus-iOS/DataModels/DataTypeClassifierV4English.mlmodel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/DataModels/DataTypeClassifierV4English.mlmodel -------------------------------------------------------------------------------- /Campus-iOS/DataModels/DataTypeClassifierV4German.mlmodel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/DataModels/DataTypeClassifierV4German.mlmodel -------------------------------------------------------------------------------- /Campus-iOS/DataModels/Old models/DataTypeClassifierV1.mlmodel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/DataModels/Old models/DataTypeClassifierV1.mlmodel -------------------------------------------------------------------------------- /Campus-iOS/DataModels/Old models/DataTypeClassifierV2.mlmodel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/DataModels/Old models/DataTypeClassifierV2.mlmodel -------------------------------------------------------------------------------- /Campus-iOS/DataModels/Old models/DataTypeClassifierV3.mlmodel: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/Campus-iOS/DataModels/Old models/DataTypeClassifierV3.mlmodel -------------------------------------------------------------------------------- /Campus-iOS/Design/CustomRoundedBorderTextFieldStyle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CustomRoundedBorderTextFieldStyle.swift 3 | // Campus-iOS 4 | // 5 | // Created by Milen Vitanov on 09.12.21. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct CustomRoundedTextFieldStyle: TextFieldStyle { 12 | func _body(configuration: TextField) -> some View { 13 | configuration 14 | .padding(5) 15 | .background(RoundedRectangle(cornerRadius: 5).fill(Color(UIColor.systemGray6))) 16 | .overlay( 17 | RoundedRectangle(cornerRadius: 5) 18 | .stroke(lineWidth: 0.5) 19 | .foregroundColor(Color(UIColor.systemGray4)) 20 | ) 21 | } 22 | } 23 | -------------------------------------------------------------------------------- /Campus-iOS/Design/RoundedCorners.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RoundedCorners.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 16.07.22. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct RoundedCorner: Shape { 12 | 13 | var radius: CGFloat = .infinity 14 | var corners: UIRectCorner = .allCorners 15 | 16 | func path(in rect: CGRect) -> Path { 17 | let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius)) 18 | return Path(path.cgPath) 19 | } 20 | } 21 | 22 | extension View { 23 | func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View { 24 | clipShape( RoundedCorner(radius: radius, corners: corners) ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Campus-iOS/Design/Theme.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Theme.swift 3 | // Campus-iOS 4 | // 5 | // Created by Timothy Summers on 08.11.22. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | extension Color { 12 | static let tumBrand = Color("tumBrand") 13 | static let primaryBackground = Color("primaryBackground") 14 | static let secondaryBackground = Color("secondaryBackground") 15 | static let primaryText = Color("primaryText") 16 | static let contrastText = Color("contrastText") 17 | static var highlightText = Color("tumBlue") 18 | static var widget = Color("widgetColor") 19 | } 20 | 21 | extension UIColor { 22 | static let tumBlue = UIColor(red: 0, green: 101/255, blue: 189/255, alpha: 1) 23 | static let primaryBackground = UIColor(Color("primaryBackground")) 24 | static let secondaryBackground = UIColor(Color("secondaryBackground")) 25 | static let primaryText = UIColor(Color("primaryText")) 26 | static let contrastText = UIColor(Color("contrastText")) 27 | } 28 | 29 | struct Radius { 30 | static let regular = CGFloat(10) 31 | } 32 | 33 | struct Size { 34 | static let cardWidth = UIScreen.main.bounds.size.width * 0.9 35 | } 36 | 37 | -------------------------------------------------------------------------------- /Campus-iOS/Design/TrailingIconLabelStyle.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TrailingIconLabelStyle.swift 3 | // Campus-iOS 4 | // 5 | // Created by Jakob Paul Körber on 28.02.23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct TrailingIconLabelStyle: LabelStyle { 11 | func makeBody(configuration: Configuration) -> some View { 12 | HStack(alignment: .center) { 13 | configuration.title 14 | configuration.icon 15 | } 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Campus-iOS/Design/ViewModifiers.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ViewModifiers.swift 3 | // Campus-iOS 4 | // 5 | // Created by Timothy Summers on 17.01.23. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct Title: ViewModifier { 12 | func body(content: Content) -> some View { 13 | HStack { 14 | content 15 | .font(.headline.bold()) 16 | .foregroundColor(Color.highlightText) 17 | Spacer() 18 | } 19 | .padding(.leading) 20 | .padding(.bottom, 10) 21 | } 22 | } 23 | 24 | struct ListSection: ViewModifier { 25 | func body(content: Content) -> some View { 26 | content 27 | .padding() 28 | .frame(width: Size.cardWidth) 29 | .background(Color.secondaryBackground) 30 | .clipShape(RoundedRectangle(cornerRadius: Radius.regular)) 31 | } 32 | } 33 | 34 | struct ScrollableCardsViewModifier: ViewModifier { //only used for news credit: Milen Vitanov 35 | func body(content: Content) -> some View { 36 | content 37 | .background(Color.blue.opacity(0.05)) 38 | .cornerRadius(20) 39 | .shadow(color: Color.black.opacity(0.2), radius: 20, x: 0, y: 0) 40 | .padding(10) 41 | } 42 | } 43 | 44 | extension View { 45 | func titleStyle() -> some View { 46 | modifier(Title()) 47 | } 48 | func sectionStyle() -> some View { 49 | modifier(ListSection()) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /Campus-iOS/Extensions/Array+Groups.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Array+Groups.swift 3 | // Campus-iOS 4 | // 5 | // Created by Robyn Kölle on 27.09.22. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Array { 11 | func groups(where predicate: (Element, Element) -> Bool) -> [[Element]] { 12 | 13 | var result: [[Element]] = [] 14 | 15 | if self.isEmpty { 16 | return [] 17 | } 18 | 19 | for i in 0.. Bool { 13 | let lat = coordinate.latitude 14 | let lon = coordinate.longitude 15 | return lat < -90 || lat > 90 || lon < -180 || lon > 180 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Campus-iOS/Extensions/Date+daysBetween.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Date+daysBetween.swift 3 | // Campus-iOS 4 | // 5 | // Created by Robyn Kölle on 28.09.22. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Date{ 11 | static func daysBetween(_ date1: Date, _ date2: Date) -> Int { 12 | return abs(Calendar.current.dateComponents([.day], from: date1, to: date2).day!) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Campus-iOS/Extensions/Operators.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Operators.swift 3 | // Campus-iOS 4 | // 5 | // Created by Robyn Kölle on 07.09.22. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | // Source: https://stackoverflow.com/a/61733134 12 | prefix func ! (value: Binding) -> Binding { 13 | Binding( 14 | get: { !value.wrappedValue }, 15 | set: { value.wrappedValue = !$0 } 16 | ) 17 | } 18 | -------------------------------------------------------------------------------- /Campus-iOS/GoogleService-Info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | CLIENT_ID 6 | 944892355389-qfnnv8c4344dk8ka4904ue35rclf3ipg.apps.googleusercontent.com 7 | REVERSED_CLIENT_ID 8 | com.googleusercontent.apps.944892355389-qfnnv8c4344dk8ka4904ue35rclf3ipg 9 | ANDROID_CLIENT_ID 10 | 944892355389-jat7kf35dqlvc2uvlsh3vbuq8ge6s6s5.apps.googleusercontent.com 11 | API_KEY 12 | AIzaSyAcgSYu70ITyg2JyUc26038zhGH1Klo3cg 13 | GCM_SENDER_ID 14 | 944892355389 15 | PLIST_VERSION 16 | 1 17 | BUNDLE_ID 18 | de.tum.tca 19 | PROJECT_ID 20 | tca-backend-0001 21 | STORAGE_BUCKET 22 | tca-backend-0001.appspot.com 23 | IS_ADS_ENABLED 24 | 25 | IS_ANALYTICS_ENABLED 26 | 27 | IS_APPINVITE_ENABLED 28 | 29 | IS_GCM_ENABLED 30 | 31 | IS_SIGNIN_ENABLED 32 | 33 | GOOGLE_APP_ID 34 | 1:944892355389:ios:70b9e0e71c71af4b52db54 35 | DATABASE_URL 36 | https://tca-backend-0001.firebaseio.com 37 | 38 | -------------------------------------------------------------------------------- /Campus-iOS/GradesComponent/Model/AcademicDegree.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AcademicDegree.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 19.03.22. 6 | // 7 | 8 | import Foundation 9 | 10 | enum AcademicDegree: String { 11 | case PhD = "Doctor of Philosophy" 12 | case BE = "Bachelor of Education" 13 | case ME = "Master of Education" 14 | case BSc = "Bachelor of Science" 15 | case MSc = "Master of Science" 16 | case MBA = "Master of Business Administration" 17 | case BA = "Bachelor of Arts" 18 | case MA = "Master of Arts" 19 | case MBD = "Master Brewer Diploma" 20 | case BECE = "Bachelor of Engineering in Chemical Engineering" 21 | case BEEDE = "Bachelor of Engineering in Electronics and Data Engineering" 22 | case unknown = "" 23 | 24 | var short: String { 25 | switch self { 26 | case .PhD: return "Ph.D." 27 | case .BE: return "BEd." 28 | case .ME: return "MEd." 29 | case .BSc: return "BSc." 30 | case .MSc: return "MSc." 31 | case .MBA: return "MBA" 32 | case .BA: return "B.A." 33 | case .MA: return "M.A." 34 | case .MBD: return "M.B.D." 35 | case .BECE: return "B.Eng. ChE." 36 | case .BEEDE: return "B.Eng. EDE." 37 | case .unknown: return "" 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Campus-iOS/GradesComponent/Model/AverageGrade.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AverageGrade.swift 3 | // Campus-iOS 4 | // 5 | // Created by Anton Wyrowski on 08.05.23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct AverageGradeStudien: Decodable { 11 | let studium: [AverageGrade] 12 | } 13 | 14 | struct AverageGrade: Decodable, Identifiable { 15 | public var id: String { 16 | "\(studyId)-\(averageGradeWeightedByCredits)" 17 | } 18 | public var studyId: String 19 | public var studyDesignation: String 20 | public var averageGradeWeightedByCredits: String 21 | 22 | public var averageGradeRounded: String { 23 | let doubleValue = Double(averageGradeWeightedByCredits.replacingOccurrences(of: ",", with: ".")) ?? 0.0 24 | return String(format: "%.2f", doubleValue) 25 | } 26 | 27 | enum CodingKeys: String, CodingKey { 28 | case studyId = "studidf" 29 | case studyDesignation = "studbez" 30 | case averageGradeWeightedByCredits = "avg_grade_weighted_by_credits" 31 | } 32 | } 33 | 34 | extension AverageGrade { 35 | static let dummyData: [AverageGrade] = [ 36 | AverageGrade(studyId: "1630 17 030", studyDesignation: "Informatik", averageGradeWeightedByCredits: "1,777") 37 | ] 38 | } 39 | -------------------------------------------------------------------------------- /Campus-iOS/GradesComponent/Model/Modus.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Modus.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 24.12.21. 6 | // 7 | 8 | import Foundation 9 | 10 | // Not currently is use, as we don't know if this list is exhaustive 11 | enum Modus: String, Decodable { 12 | case written = "Schriftlich" 13 | case graded = "Beurteilt/immanenter Prüfungscharakter" 14 | case wirrtenAndVerbal = "Schriftlich und Mündlich" 15 | case verbal = "Mündlich" 16 | 17 | var short: String { 18 | switch self { 19 | case .written: return "Schriftlich" 20 | case .graded: return "Beurteilt" 21 | case .wirrtenAndVerbal: return "Schriftlich/Mündlich" 22 | case .verbal: return "Mündlich" 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Campus-iOS/GradesComponent/Service/AverageGradesService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AverageGradeService.swift 3 | // Campus-iOS 4 | // 5 | // Created by Anton Wyrowski on 08.05.23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct AverageGradesService: ServiceTokenProtocol { 11 | func fetch(token: String, forcedRefresh: Bool = false) async throws -> [AverageGrade] { 12 | let response: TUMOnlineAPI.AverageGradesResponse = try await MainAPI.makeRequest(endpoint: TUMOnlineAPI.averageGrades, token: token, forcedRefresh: forcedRefresh) 13 | 14 | return response.studium 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Campus-iOS/GradesComponent/Service/GradesService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GradesService.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 21.12.21. 6 | // 7 | 8 | import Foundation 9 | import Alamofire 10 | 11 | protocol GradesServiceProtocol { 12 | func fetch(token: String, forcedRefresh: Bool) async throws -> [Grade] 13 | } 14 | 15 | typealias GradesSemesterDegrees = [(String, [(String, [Grade])])] 16 | 17 | struct GradesService: ServiceTokenProtocol, GradesServiceProtocol { 18 | 19 | func fetch(token: String, forcedRefresh: Bool = false) async throws -> [Grade] { 20 | let response: TUMOnlineAPI.Response = try await MainAPI.makeRequest(endpoint: TUMOnlineAPI.personalGrades, token: token, forcedRefresh: forcedRefresh) 21 | 22 | return response.row 23 | .sorted { gradeA, gradeB in 24 | return gradeA.date > gradeB.date 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Campus-iOS/GradesComponent/ViewModel/GradesViewModel+State.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GradesViewModel+State.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 25.12.21. 6 | // 7 | 8 | import Foundation 9 | 10 | //extension GradesViewModel { 11 | // enum State { 12 | // case na 13 | // case loading 14 | // case success(data: [Grade]) 15 | // case failed(error: Error) 16 | // } 17 | //} 18 | -------------------------------------------------------------------------------- /Campus-iOS/GradesComponent/ViewModel/MockGradesViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MockGradesViewModel.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 03.05.22. 6 | // 7 | 8 | import Foundation 9 | import SwiftUICharts 10 | 11 | class MockGradesViewModel: GradesViewModel { 12 | 13 | let dummyGradesByDegreeAndSemester: [(String, [(String, [Grade])])] = [ 14 | ("1630 17 030", [("Wintersemester 2021/22", Grade.dummyData21W)]), 15 | ("1630 17 030", [("Sommersemester 2021", Grade.dummyData21S)]), 16 | ("1630 17 030", [("Wintersemester 2020/21", Grade.dummyData20W)]) 17 | ] 18 | 19 | override init(model: Model, gradesService: GradesService, averageGradesService: AverageGradesService) { 20 | super.init(model: model, gradesService: gradesService, averageGradesService: AverageGradesService()) 21 | 22 | self.gradesState = .success(data: Grade.dummyDataAll) 23 | self.averageGradesState = .success(data: AverageGrade.dummyData) 24 | } 25 | } 26 | 27 | -------------------------------------------------------------------------------- /Campus-iOS/GradesComponent/Views/BarChartView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // BarChartView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 24.12.21. 6 | // 7 | 8 | import SwiftUI 9 | import SwiftUICharts 10 | 11 | struct BarChartView: View { 12 | var barChartData: BarChartData 13 | 14 | var body: some View { 15 | BarChart(chartData: barChartData) 16 | .xAxisGrid(chartData: barChartData) 17 | .xAxisLabels(chartData: barChartData) 18 | .yAxisLabels(chartData: barChartData) 19 | .frame(height: UIScreen.main.bounds.size.height/5, alignment: .center) 20 | .padding(.top, 12) 21 | } 22 | } 23 | 24 | struct BarChartView_Previews: PreviewProvider { 25 | static var previews: some View { 26 | BarChartView( 27 | barChartData: 28 | .init( 29 | dataSets: .init(dataPoints: []) 30 | ) 31 | ) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Campus-iOS/GradesComponent/Views/GlowBorder.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GlowBorder.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 19.03.22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct GlowBorder: ViewModifier { 11 | var color: Color 12 | var lineWidth: Int 13 | 14 | func body(content: Content) -> some View { 15 | applyShadow(content: AnyView(content), lineWidth: lineWidth) 16 | } 17 | 18 | func applyShadow(content: AnyView, lineWidth: Int) -> AnyView { 19 | if lineWidth == 0 { 20 | return content 21 | } else { 22 | return applyShadow(content: AnyView(content.shadow(color: color, radius: 1)), lineWidth: lineWidth - 1) 23 | } 24 | } 25 | } 26 | 27 | extension View { 28 | func glowBorder(color: Color, lineWidth: Int) -> some View { 29 | self.modifier(GlowBorder(color: color, lineWidth: lineWidth)) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Campus-iOS/GradesComponent/Views/GradesSemesterView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GradesSemesterView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Anton Wyrowski on 23.05.23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct GradesSemesterView: View { 11 | let semesterName: String 12 | let grades: [Grade] 13 | 14 | var body: some View { 15 | Section(header: Text(semesterName) 16 | .font(.headline.bold()) 17 | .foregroundColor(Color("tumBlue")) 18 | .accessibilityHeading(.h2) 19 | ) { 20 | ForEach(grades) { item in 21 | VStack { 22 | GradeView(grade: item) 23 | 24 | if let id = grades.last?.id { 25 | if item.id != id { 26 | Divider() 27 | } 28 | } 29 | } 30 | } 31 | .listRowInsets( 32 | EdgeInsets( 33 | top: 4, 34 | leading: 18, 35 | bottom: 2, 36 | trailing: 18 37 | ) 38 | ) 39 | } 40 | } 41 | } 42 | 43 | -------------------------------------------------------------------------------- /Campus-iOS/GradesComponent/Views/GradesView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GradesView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 24.12.21. 6 | // 7 | 8 | import SwiftUI 9 | import SwiftUICharts 10 | 11 | @available(iOS 16.0, *) 12 | struct GradesView: View { 13 | 14 | @StateObject var vm: GradesViewModel 15 | 16 | var body: some View { 17 | let gradesWithAverage = self.vm.gradesByDegreeAndSemesterWithAverageGrade 18 | 19 | return List { 20 | ForEach(gradesWithAverage.indices, id: \.self) { index in 21 | GradesStudyProgramView(semesterGrades: gradesWithAverage[index], studyProgram: self.vm.getStudyProgram(studyID: gradesWithAverage[index].degree), barChartData: vm.barChartData.count > index ? vm.barChartData[index] : BarChartData(dataSets: BarDataSet(dataPoints: []))) 22 | } 23 | .listRowBackground(Color.secondaryBackground) 24 | } 25 | .background(Color.primaryBackground) 26 | .scrollContentBackground(.hidden) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Campus-iOS/HomeComponent/ContactComponent/ContactScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ContactView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Timothy Summers on 30.12.22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @available(iOS 16.0, *) 11 | struct ContactScreen: View { 12 | 13 | @StateObject var model: Model 14 | @StateObject var gradesViewModel: GradesViewModel //provides studyprogram info 15 | @StateObject var profileVm: ProfileViewModel 16 | let profile: Profile 17 | 18 | init (model: Model, profileVm: ProfileViewModel, profile: Profile) { 19 | self._model = StateObject(wrappedValue: model) 20 | self._gradesViewModel = StateObject(wrappedValue: GradesViewModel(model: model, gradesService: GradesService(), averageGradesService: AverageGradesService())) 21 | self._profileVm = StateObject(wrappedValue: profileVm) 22 | self.profile = profile 23 | } 24 | 25 | var body: some View { 26 | VStack(spacing: 0) { 27 | ContactCardView(model: self.model, profile: profile, profileVm: self.profileVm, gradesVm: self.gradesViewModel) 28 | .padding(.bottom, 10) 29 | 30 | 31 | TuitionScreen(vm: self.profileVm) 32 | .padding(.bottom, 10) 33 | 34 | LinkView() 35 | 36 | }.task { 37 | await gradesViewModel.getGrades(forcedRefresh: true) 38 | await gradesViewModel.getAverageGrades(forcedRefresh: true) 39 | } 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Campus-iOS/HomeComponent/WidgetComponent/Views/Widget/DeparturesWidgetScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DeparturesWidgetScreen.swift 3 | // Campus-iOS 4 | // 5 | // Created by Jakob Paul Körber on 01.03.23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct DeparturesWidgetScreen: View { 11 | 12 | @StateObject var departuresViewModel: DeparturesWidgetViewModel 13 | @State var showDetailsSheet = false 14 | 15 | var body: some View { 16 | Group { 17 | switch(self.departuresViewModel.state) { 18 | case .success, .failed, .loading: 19 | DeparturesWidgetView(departuresViewModel: departuresViewModel, showDetailsSheet: $showDetailsSheet) 20 | case .noLocation: 21 | EmptyView() 22 | } 23 | } 24 | .background(Color("primaryBackground")) 25 | .onAppear { 26 | departuresViewModel.timer = Timer() 27 | departuresViewModel.setTimerForRefetch() 28 | } 29 | .onDisappear { 30 | if !showDetailsSheet { 31 | departuresViewModel.timer?.invalidate() 32 | } 33 | } 34 | .onTapGesture { 35 | showDetailsSheet.toggle() 36 | } 37 | } 38 | } 39 | 40 | struct DeparturesWidgetScreen_Previews: PreviewProvider { 41 | static var previews: some View { 42 | DeparturesWidgetScreen(departuresViewModel: DeparturesWidgetViewModel()) 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /Campus-iOS/LectureComponent/Screen/LectureDetailsScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LectureDetailsScreen.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 25.12.21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct LectureDetailsScreen: View { 11 | @StateObject var vm: LectureDetailsViewModel 12 | 13 | init(model: Model, lecture: Lecture) { 14 | self._vm = StateObject(wrappedValue: 15 | LectureDetailsViewModel( 16 | model: model, 17 | service: LectureDetailsService(), 18 | lecture: lecture 19 | ) 20 | ) 21 | } 22 | 23 | var body: some View { 24 | Group { 25 | switch vm.state { 26 | case .success(let data): 27 | LecturesDetailView(viewModel: vm, lectureDetails: data) 28 | case .loading, .na: 29 | LoadingView(text: "Fetching details of lecture".localized) 30 | case .failed(let error): 31 | FailedView( 32 | errorDescription: error.localizedDescription, 33 | retryClosure: vm.getLectureDetails 34 | ) 35 | } 36 | } 37 | .task { 38 | await vm.getLectureDetails() 39 | } 40 | } 41 | } 42 | 43 | struct LectureDetailScreen_Previews: PreviewProvider { 44 | static var previews: some View { 45 | LectureDetailsScreen( 46 | model: MockModel(), lecture: Lecture.dummyData.first! 47 | ) 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /Campus-iOS/LectureComponent/Service/LectureDetailsService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LectureDetailsService.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 25.12.21. 6 | // 7 | 8 | import Foundation 9 | import Alamofire 10 | import XMLCoder 11 | 12 | protocol LectureDetailsServiceProtocol { 13 | func fetch(token: String, lvNr: UInt64, forcedRefresh: Bool) async throws -> LectureDetails 14 | } 15 | 16 | struct LectureDetailsService: LectureDetailsServiceProtocol { 17 | func fetch(token: String, lvNr: UInt64, forcedRefresh: Bool = false) async throws -> LectureDetails { 18 | let response: TUMOnlineAPI.Response = 19 | try await 20 | MainAPI 21 | .makeRequest( 22 | endpoint: TUMOnlineAPI.lectureDetails(lvNr: String(lvNr)), 23 | token: token, 24 | forcedRefresh: forcedRefresh 25 | ) 26 | 27 | guard let data = response.row.first else { 28 | throw NetworkingError.resourceNotFound 29 | } 30 | 31 | return data 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Campus-iOS/LectureComponent/Service/LecturesService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GradesService.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 21.12.21. 6 | // 7 | 8 | import Foundation 9 | import Alamofire 10 | import XMLCoder 11 | 12 | protocol LecturesServiceProtocol { 13 | func fetch(token: String, forcedRefresh: Bool) async throws -> [Lecture] 14 | } 15 | 16 | struct LecturesService: LecturesServiceProtocol { 17 | func fetch(token: String, forcedRefresh: Bool = false) async throws -> [Lecture] { 18 | let response: TUMOnlineAPI.Response = 19 | try await 20 | MainAPI.makeRequest(endpoint: TUMOnlineAPI.personalLectures, token: token, forcedRefresh: forcedRefresh) 21 | 22 | return response.row 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Campus-iOS/LectureComponent/ViewModel/LectureDetailsViewModel+State.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LectureDetailsViewModel+State.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 25.12.21. 6 | // 7 | 8 | import Foundation 9 | 10 | extension LectureDetailsViewModel { 11 | enum State { 12 | case na 13 | case loading 14 | case success(data: LectureDetails) 15 | case failed(error: Error) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Campus-iOS/LectureComponent/ViewModel/LecturesViewModel+State.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LecturesViewModel+State.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 25.12.21. 6 | // 7 | 8 | import Foundation 9 | 10 | extension LecturesViewModel { 11 | enum State { 12 | case na 13 | case loading 14 | case success(data: [Lecture]) 15 | case failed(error: Error) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Campus-iOS/LectureComponent/Views/LectureDetailsViews/LectureDetailsBasicInfoRowView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LectureDetailsBasicInfoRowView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 26.12.21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct LectureDetailsBasicInfoRowView: View { 11 | var iconName: String 12 | var text: String 13 | 14 | var body: some View { 15 | HStack(alignment: .center, spacing: 12) { 16 | Image(systemName: iconName) 17 | .imageScale(.medium) 18 | .frame(width: 25, height: 25, alignment: .center) 19 | Text(text) 20 | .font(.system(size: 16)) 21 | .multilineTextAlignment(.leading) 22 | } 23 | } 24 | } 25 | 26 | struct LectureDetailsRowView_Previews: PreviewProvider { 27 | static var previews: some View { 28 | LectureDetailsBasicInfoRowView( 29 | iconName: "number", 30 | text: "LOOOOOOOOOOOOOOOOOOONG (1234.01.001)" 31 | ) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Campus-iOS/LectureComponent/Views/LectureDetailsViews/LectureDetailsDetailedInfoRowView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LectureDetailsDetailedInfoRowView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 26.12.21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct LectureDetailsDetailedInfoRowView: View { 11 | var title: String 12 | var text: String 13 | 14 | var body: some View { 15 | VStack(alignment: .leading, spacing: 8) { 16 | Text(title) 17 | .font(.system(size: 16, weight: .semibold)) 18 | 19 | Text(text) 20 | .font(.system(size: 16)) 21 | } 22 | } 23 | } 24 | 25 | struct LectureDetailsDetailedInfoRowView_Previews: PreviewProvider { 26 | static var previews: some View { 27 | LectureDetailsDetailedInfoRowView( 28 | title: "number", 29 | text: "test" 30 | ) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Campus-iOS/LectureComponent/Views/LectureDetailsViews/LectureDetailsTitleView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LectureDetailsTitleView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 25.12.21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct LectureDetailsTitleView: View { 11 | var lectureDetails: LectureDetails 12 | 13 | var body: some View { 14 | VStack(alignment: .leading, spacing: 5) { 15 | Text(lectureDetails.title) 16 | .font(.title) 17 | .multilineTextAlignment(.leading) 18 | Text(lectureDetails.eventType) 19 | .font(.subheadline) 20 | } 21 | } 22 | } 23 | 24 | struct LectureDetailTitleView_Previews: PreviewProvider { 25 | static var previews: some View { 26 | LectureDetailsTitleView(lectureDetails: LectureDetails.dummyData) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Campus-iOS/LectureSearchComponent/Service/LectureSearchService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LectureSearchService.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 20.01.23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct LectureSearchService { 11 | func fetch(for query: String, token: String, forcedRefresh: Bool) async throws -> [Lecture] { 12 | let response : TUMOnlineAPI.Response = try await MainAPI.makeRequest(endpoint: TUMOnlineAPI.lectureSearch(search: query), token: token, forcedRefresh: forcedRefresh) 13 | 14 | return response.row 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Campus-iOS/LectureSearchComponent/View/LectureSearchView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LectureSearchListView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Milen Vitanov on 13.02.22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct LectureSearchView: View { 11 | let model: Model 12 | let lectures: [Lecture] 13 | 14 | var body: some View { 15 | List { 16 | ForEach(lectures) { lecture in 17 | NavigationLink( 18 | destination: LectureDetailsScreen(model: self.model, lecture: lecture) 19 | .navigationBarTitleDisplayMode(.inline) 20 | ) { 21 | HStack { 22 | Text(lecture.title) 23 | Spacer() 24 | Text(lecture.eventType) 25 | .foregroundColor(Color(.secondaryLabel)) 26 | } 27 | } 28 | } 29 | } 30 | } 31 | } 32 | 33 | struct LectureSearchListView_Previews: PreviewProvider { 34 | static var previews: some View { 35 | LectureSearchView(model: Model(), lectures: []) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Campus-iOS/LoginComponent/Model/Confirmation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Confirmation.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 21.01.23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Confirmation: Decodable { 11 | let value: Bool 12 | 13 | private enum CodingKeys: String, CodingKey { 14 | case value = "" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Campus-iOS/LoginComponent/Model/Token.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Token.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 21.01.23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Token: Decodable { 11 | let value: String 12 | 13 | private enum CodingKeys: String, CodingKey { 14 | case value = "" 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Campus-iOS/LoginComponent/ViewModel/LoginViewModel+LoginState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginViewModel+LoginState.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 17.10.22. 6 | // 7 | 8 | import Foundation 9 | 10 | extension LoginViewModel { 11 | enum LoginState { 12 | case notChecked 13 | case logInError 14 | case loggedIn 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Campus-iOS/LoginComponent/ViewModel/LoginViewModel+TokenState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LoginViewModel+TokenState.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 17.10.22. 6 | // 7 | 8 | import Foundation 9 | 10 | extension LoginViewModel { 11 | enum TokenState { 12 | case notChecked 13 | case inactive 14 | case active 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Campus-iOS/LoginComponent/ViewModel/TokenPermissionsViewModel+PermissionType.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TokenPermissionsViewModel+PermissionType.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 17.10.22. 6 | // 7 | 8 | import Foundation 9 | 10 | extension TokenPermissionsViewModel { 11 | enum PermissionType: String { 12 | case grades = "Grades" 13 | case calendar = "Calendar" 14 | case lectures = "Lectures" 15 | case tuitionFees = "Tuition Fees" 16 | case identification = "Identification (TUM ID and name)" 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /Campus-iOS/LoginComponent/ViewModel/TokenPermissionsViewModel+State.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TokenPermissionsViewModel+State.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 17.10.22. 6 | // 7 | 8 | import Foundation 9 | 10 | extension TokenPermissionsViewModel { 11 | enum State { 12 | case na 13 | case loading 14 | case success(data: Any?) 15 | case failed(error: Error) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/Model/MapLocation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapLocation.swift 3 | // Campus-iOS 4 | // 5 | // Created by Robyn Kölle on 08.07.22. 6 | // 7 | 8 | import Foundation 9 | import MapKit 10 | 11 | struct MapLocation: Identifiable { 12 | let id = UUID() 13 | let coordinate: CLLocationCoordinate2D 14 | } 15 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/Service/DishService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // DishService.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 23.01.23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct DishService { 11 | func fetch(forcedRefresh: Bool) async -> [String: DishLabel]? { 12 | do { 13 | let response: [DishLabel] = try await MainAPI.makeRequest(endpoint: EatAPI.labels, forcedRefresh: forcedRefresh) 14 | var labels = [String: DishLabel]() 15 | for dishLabel in response { 16 | labels[dishLabel.name] = dishLabel 17 | } 18 | 19 | return labels 20 | } catch { 21 | print(error) 22 | return nil 23 | // No error is thrown, since the labels, can still be displayed, but just as text, instead of an emoji. 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/Service/StudyRoomsService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StudyRoomsService.swift 3 | // Campus-iOS 4 | // 5 | // Created by Milen Vitanov on 09.06.22. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol StudyRoomsServiceProtocol { 11 | func fetch(forcedRefresh: Bool) async throws -> StudyRoomApiRespose 12 | } 13 | 14 | struct StudyRoomsService: StudyRoomsServiceProtocol { 15 | func fetch(forcedRefresh: Bool) async throws -> StudyRoomApiRespose { 16 | let response: StudyRoomApiRespose = try await MainAPI.makeRequest(endpoint: TUMDevAppAPI.rooms, forcedRefresh: forcedRefresh) 17 | return response 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/Types/Cafeterias/Cafeteria+PreviewData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Cafeteria+PreviewData.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 05.05.23. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Cafeteria { 11 | static let previewData = [ 12 | Cafeteria(location: Location(latitude: 48.147420, longitude: 11.567220, address: "Arcisstraße 17, München"), name: "Mensa Arcisstraße", id: "mensa-arcisstr", queueStatusApi: nil, queue: nil), 13 | Cafeteria(location: Location(latitude: 0.0, longitude: 0.0, address: "xystraße, München"), name: "Mensa XY", id: "mensa-xy", queueStatusApi: nil, queue: nil), 14 | // Mensa Garching has an maximum capacity of 1500 people (calculated reversed with the current and percentage of a day, e.g. current = 2 and percent: 0.13333334 then 0.13333334/100 * x = 2 => 2/(13333334/100) = 1500) 15 | Cafeteria(location: Location(latitude: 48.268132, longitude: 11.672263, address: "Boltzmannstraße 19, Garching"), name: "Mensa Garching", id: "mensa-garching", queueStatusApi: Optional("https://mensa.liste.party/api/"), queue: Queue(current: 150, percent: 0.1)) 16 | ] 17 | } 18 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/Types/Cafeterias/DishLabel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Label.swift 3 | // Campus-iOS 4 | // 5 | // Created by August Wittgenstein on 14.01.22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct DishLabel: Decodable { 11 | 12 | /* 13 | [ 14 | { 15 | "enum_name": "VEGETARIAN", 16 | "text": { 17 | "DE": "Vegetarisch", 18 | "EN": "vegetarian" 19 | }, 20 | "abbreviation": "🌽" 21 | } 22 | ] 23 | */ 24 | 25 | let name: String 26 | let text: [String: String] 27 | let abbreviation: String 28 | 29 | enum CodingKeys: String, CodingKey { 30 | case name = "enum_name" 31 | case text 32 | case abbreviation 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/Types/Cafeterias/MealPlan.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MealPlan.swift 3 | // Campus-iOS 4 | // 5 | // Created by August Wittgenstein on 17.12.21. 6 | // 7 | 8 | import Foundation 9 | 10 | struct MealPlan: Decodable { 11 | 12 | /* 13 | { 14 | "number": 10, 15 | "year": 2020, 16 | "days": [...] 17 | } 18 | */ 19 | 20 | let week: Int 21 | let year: Int 22 | let days: [MensaMenu] 23 | 24 | enum CodingKeys: String, CodingKey { 25 | case week = "number" 26 | case year 27 | case days 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/Types/Cafeterias/MensaCategory.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MensaCategory.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 02.04.23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct MenuCategory: Identifiable, Decodable { 11 | var id = UUID() 12 | let name: String 13 | let dishes: [Dish] 14 | 15 | init(name: String, dishes: [Dish]) { 16 | self.name = name 17 | self.dishes = dishes 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/Types/Cafeterias/MensaMenu.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Menu.swift 3 | // Campus-iOS 4 | // 5 | // Created by August Wittgenstein on 17.12.21. 6 | // 7 | 8 | import Foundation 9 | 10 | struct MensaMenu: Hashable, Decodable { 11 | /* 12 | "date": "2020-03-02", 13 | "dishes": [...] 14 | */ 15 | 16 | let date: Date 17 | let dishes: [Dish] 18 | 19 | enum CodingKeys: String, CodingKey { 20 | case date 21 | case dishes 22 | } 23 | 24 | init(from decoder: Decoder) throws { 25 | let container = try decoder.container(keyedBy: CodingKeys.self) 26 | 27 | self.date = try container.decode(Date.self, forKey: .date) 28 | self.dishes = try container.decode([Dish].self, forKey: .dishes) 29 | } 30 | } 31 | 32 | struct Category: Hashable, Decodable { 33 | var name: String 34 | var dishes: [Dish] 35 | 36 | enum CodingKeys: String, CodingKey { 37 | case name 38 | case dishes 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/Types/StudyRooms/RoomImageMapping.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RoomImageMapping.swift 3 | // Campus-iOS 4 | // 5 | // Created by Milen Vitanov on 05.06.22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct RoomImageMapping: Codable { 11 | /* 12 | { 13 | "map_id": 54, 14 | "description": "München", 15 | "scale": 200000, 16 | "width": 640, 17 | "height": 603 18 | }, 19 | */ 20 | 21 | let id: Int 22 | let description: String 23 | let scale: Int 24 | let width: Int 25 | let height: Int 26 | 27 | enum CodingKeys: String, CodingKey { 28 | case id = "map_id" 29 | case description 30 | case scale 31 | case width 32 | case height 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/Types/StudyRooms/StudyRoomApiResponse+PreviewData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StudyRoomApiResponse+PreviewData.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 05.05.23. 6 | // 7 | 8 | import Foundation 9 | 10 | extension StudyRoomApiRespose { 11 | static let previewData: StudyRoomApiRespose = StudyRoomApiRespose( 12 | rooms: [ 13 | StudyRoom(buildingCode: "5603", buildingName: "FMI/ Bibliothek", buildingNumber: 832, code: "01.03.010", id: 49770, name: "Windfang", number: "010", occupiedBy: "", occupiedFor: 0, occupiedFrom: "", occupiedIn: 0, occupiedUntil: "", raum_nr_architekt: "01.03.010@5603", res_nr: 27052, status: "frei", attributes: []), 14 | StudyRoom(buildingCode: "5603", buildingName: "FMI/ Bibliothek", buildingNumber: 832, code: "01.03.010", id: 58380, name: "Einzelarbeitsraum", number: "043B", occupiedBy: "", occupiedFor: 0, occupiedFrom: "", occupiedIn: 0, occupiedUntil: "", raum_nr_architekt: "01.03.043B@5603", res_nr: 26714, status: "frei", attributes: [])], 15 | groups: [ 16 | StudyRoomGroup(detail: "", id: 46, name: "Fachschaftsräume Mathe/Informatik", sorting: 1, rooms: [49770,58380,58372,58363,58354,58346,58227,58217,58211,58203,58195,58187,58068,58059,58050,58041])]) 17 | 18 | } 19 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/Types/StudyRooms/StudyRoomAttribute.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StudyRoomAttribute.swift 3 | // Campus-iOS 4 | // 5 | // Created by Milen Vitanov on 05.05.22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct StudyRoomAttribute: Decodable, Searchable { 11 | 12 | var detail: String? 13 | var name: String? 14 | 15 | enum CodingKeys: String, CodingKey { 16 | case detail 17 | case name 18 | } 19 | 20 | init(from decoder: Decoder) throws { 21 | let container = try decoder.container(keyedBy: CodingKeys.self) 22 | 23 | let detail = try container.decode(String.self, forKey: .detail) 24 | let name = try container.decode(String.self, forKey: .name) 25 | 26 | self.detail = detail 27 | self.name = name 28 | } 29 | 30 | var comparisonTokens: [ComparisonToken] { 31 | return [ 32 | ComparisonToken(value: detail ?? ""), 33 | ComparisonToken(value: name ?? "") 34 | ] 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/View/AnnotatedMapView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnnotatedMapView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Timothy Summers on 06.06.23. 6 | // 7 | 8 | import SwiftUI 9 | import MapKit 10 | 11 | struct AnnotatedMapView: View { 12 | 13 | @StateObject var vm: AnnotatedMapViewModel 14 | @State private var region: MKCoordinateRegion 15 | 16 | internal init(vm: AnnotatedMapViewModel, centerOfMap: CLLocationCoordinate2D, zoomLevel: Double) { 17 | self._vm = StateObject(wrappedValue: vm) 18 | self.region = MKCoordinateRegion(center: centerOfMap, span: MKCoordinateSpan(latitudeDelta: zoomLevel, longitudeDelta: zoomLevel)) 19 | } 20 | 21 | var body: some View { 22 | Label("View On Maps", systemImage: "map").titleStyle() 23 | .padding(.top, 20) 24 | 25 | Map(coordinateRegion: $region, showsUserLocation: true, annotationItems: vm.locations) {item in 26 | MapAnnotation(coordinate: item.coordinate) { 27 | AnnotationView(location: item, vm: self.vm) 28 | } 29 | } 30 | .frame(width: Size.cardWidth, height: Size.cardWidth) 31 | .clipShape(RoundedRectangle(cornerRadius: Radius.regular)) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/View/Cafeterias/CafeteriasListView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CafeteriasView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Timothy Summers on 05.06.23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CafeteriasListView: View { 11 | 12 | @StateObject var vm: AnnotatedMapViewModel 13 | 14 | var body: some View { 15 | Group { 16 | Label("Cafeterias", systemImage: "fork.knife").titleStyle() 17 | .padding(.top, 20) 18 | VStack { 19 | if !vm.cafeterias.isEmpty { 20 | ForEach(vm.cafeterias.indices, id: \.self) { index in 21 | CafeteriaView(cafeteria: vm.cafeterias[index], isListItem: true) 22 | if (index != vm.cafeterias.count - 1) { 23 | Divider().padding(.horizontal) 24 | } 25 | } 26 | } else { 27 | Text("Resource not found") //no cafeterias 28 | } 29 | }.sectionStyle() 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/View/Cafeterias/CafeteriasView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CafeteriaViewNEW.swift 3 | // Campus-iOS 4 | // 5 | // Created by Timothy Summers on 25.04.23. 6 | // 7 | 8 | import SwiftUI 9 | import MapKit 10 | 11 | struct CafeteriasView: View { 12 | @StateObject var vm: MapViewModel 13 | @State var sortedCafeterias: [Cafeteria] 14 | var vmAnno = AnnotatedMapViewModel() 15 | var locationManager = CLLocationManager() 16 | 17 | init(vm: MapViewModel) { 18 | self._vm = StateObject(wrappedValue: vm) 19 | //sorts cafeterias based on distance from user 20 | if let location = self.locationManager.location { 21 | self.sortedCafeterias = vm.cafeterias.sorted { 22 | $0.coordinate.location.distance(from: location) < $1.coordinate.location.distance(from: location) 23 | } 24 | } else { 25 | self.sortedCafeterias = vm.cafeterias 26 | } 27 | vmAnno.addCafeterias(cafeterias: self.sortedCafeterias) 28 | } 29 | 30 | var body: some View { 31 | ScrollView { 32 | VStack { 33 | AnnotatedMapView(vm: vmAnno, centerOfMap: CLLocationCoordinate2D(latitude: 48.1372, longitude: 11.5755), zoomLevel: 0.3) 34 | 35 | CafeteriasListView(vm: self.vmAnno) 36 | } 37 | } 38 | .scrollContentBackground(.hidden) 39 | .background(Color.primaryBackground) 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/View/Cafeterias/MealPlanScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MealPlanScreen.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 10.02.23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct MealPlanScreen: View { 11 | @Environment(\.colorScheme) var colorScheme 12 | @StateObject var vm: MealPlanViewModel 13 | 14 | init(cafeteria: Cafeteria) { 15 | self._vm = StateObject(wrappedValue: MealPlanViewModel(cafeteria: cafeteria)) 16 | } 17 | 18 | var body: some View { 19 | Group { 20 | switch vm.state { 21 | case .success(let menus): 22 | if let firstMenu = menus.first { 23 | VStack { 24 | MealPlanView(menus: menus, cafeteria: vm.cafeteria, selectedMenu: firstMenu).refreshable { 25 | await vm.getMenus() 26 | } 27 | } 28 | } 29 | case .loading, .na: 30 | LoadingView(text: "Fetching Menus") 31 | case .failed(_): 32 | VStack { 33 | Spacer() 34 | // Since some cafeterias do not update their menus this is how we handle error here. There could be a better differentiation. 35 | Text("No Menu available").foregroundColor(colorScheme == .dark ? .init(UIColor.lightGray) : .init(UIColor.darkGray)) 36 | Spacer() 37 | } 38 | } 39 | }.task { 40 | await vm.getMenus() 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/View/Cafeterias/MenuView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuViewNEW.swift 3 | // Campus-iOS 4 | // 5 | // Created by Timothy Summers on 30.04.23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct MenuView: View { //wird grad ned genutzt evtl delete 11 | 12 | let menu: cafeteriaMenu 13 | 14 | var body: some View { 15 | VStack{ 16 | ForEach(menu.categories.sorted { $0.name < $1.name }) { category in 17 | Text(category.name) 18 | ScrollView(.horizontal, showsIndicators: false) { 19 | HStack { 20 | ForEach(category.dishes, id: \.self) { dish in 21 | DishView(dish: dish) 22 | } 23 | }.padding(.horizontal) 24 | } 25 | } 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/View/Cafeterias/OldCafeteriaComponent/MenuViewOld.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MenuViewOld.swift 3 | // Campus-iOS 4 | // 5 | // Created by August Wittgenstein on 15.01.22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct MenuViewOld: View { 11 | let menu: cafeteriaMenu 12 | 13 | var body: some View { 14 | List { 15 | ForEach(menu.categories.sorted { $0.name < $1.name }) { category in 16 | Section(category.name) { 17 | ForEach(category.dishes, id: \.self) { dish in 18 | DishViewOld(dish: dish) 19 | } 20 | } 21 | } 22 | }.listStyle(GroupedListStyle()) // make sections non-collapsible 23 | } 24 | } 25 | 26 | //struct MenuView_Previews: PreviewProvider { 27 | // static var previews: some View { 28 | // MenuView(viewModel: MenuViewModel(date: Date(), categories: [])) 29 | // } 30 | //} 31 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/View/Cafeterias/Widget/CafeteriaWidgetScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CafeteriaWidgetScreen.swift 3 | // Campus-iOS 4 | // 5 | // Created by Timothy Summers on 08.02.23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CafeteriaWidgetScreen: View { 11 | 12 | @StateObject var viewModel: CafeteriaWidgetViewModel 13 | 14 | var body: some View { 15 | Group { 16 | switch(viewModel.status) { 17 | case .error: 18 | EmptyView() // -> keine cafeterias nearby 19 | case .loading: 20 | LoadingView(text: "Searching nearby cafeteria") 21 | default: 22 | CafeteriaWidget(cafeteriaWidgetVM: self.viewModel, dishes: viewModel.menu?.getDishes() ?? []) 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/View/Cafeterias/Widget/MealIngredientsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MealIngredientsView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Timothy Summers on 20.02.23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct MealIngredientsView: View { 11 | 12 | var mealTitle: String 13 | var labels: [String] 14 | var price: String 15 | @State private var showingAlert = false 16 | 17 | var body: some View { 18 | Image(systemName: "info.circle") 19 | .foregroundColor(Color.highlightText) 20 | .frame(width: 5) 21 | .onTapGesture { 22 | showingAlert.toggle() 23 | } 24 | .alert(isPresented: $showingAlert) { 25 | Alert(title: Text(mealTitle), message: Text("\(labels.joined(separator: "\n"))\n\n\(price)"), dismissButton: .default(Text("Got it!"))) 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/View/Campuses/CampusCellView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CampusCellView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Timothy Summers on 25.04.23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct CampusCellView: View { 11 | let campus: Campus 12 | 13 | var body: some View { 14 | VStack { 15 | campus.image 16 | .resizable() 17 | .scaledToFill() 18 | .frame(maxHeight: 100) 19 | .cornerRadius(Radius.regular, corners: [.topLeft, .topRight]) 20 | .clipped() 21 | .padding([.horizontal,.top], 5) 22 | 23 | HStack() { 24 | Text(campus.rawValue) 25 | .fontWeight(.semibold) 26 | .foregroundColor(.primaryText) 27 | Spacer() 28 | NavigationLink(destination: LocationView(location: TUMLocation(campus: self.campus))) { 29 | Image(systemName: "mappin.circle") 30 | .font(.system(size: 20)) 31 | .foregroundColor(.highlightText) 32 | } 33 | .padding(7) 34 | .cornerRadius(5) 35 | } 36 | .padding(12) 37 | } 38 | .background(Color.secondaryBackground) 39 | .cornerRadius(Radius.regular) 40 | .frame(width: Size.cardWidth) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/View/StudyGroups/StudyRoomGroupsView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StudyRoomViewNEW.swift 3 | // Campus-iOS 4 | // 5 | // Created by Timothy Summers on 25.04.23. 6 | // 7 | 8 | import SwiftUI 9 | import MapKit 10 | 11 | struct StudyRoomGroupsView: View { 12 | 13 | @StateObject var vm: MapViewModel 14 | var vmAnno = AnnotatedMapViewModel() 15 | 16 | init(vm: MapViewModel, studyRoomGroups: [StudyRoomGroup]? = nil) { 17 | self._vm = StateObject(wrappedValue: vm) 18 | if (studyRoomGroups != nil) { 19 | vmAnno.addStudyRoomGroups(studyRoomGroups: studyRoomGroups!) 20 | } 21 | } 22 | 23 | var body: some View { 24 | ScrollView { 25 | VStack { 26 | 27 | AnnotatedMapView(vm: vmAnno, centerOfMap: CLLocationCoordinate2D(latitude: 48.1372, longitude: 11.5755), zoomLevel: 0.3) 28 | 29 | StudyRoomGroupListView(vmAnno: self.vmAnno, vm: self.vm) 30 | 31 | } 32 | .padding(.bottom, 20) 33 | } 34 | .scrollContentBackground(.hidden) 35 | .background(Color.primaryBackground) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/View/StudyGroups/Widget/StudyRoomWidgetScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // StudyRoomWidgetScreen.swift 3 | // Campus-iOS 4 | // 5 | // Created by Timothy Summers on 06.02.23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct StudyRoomWidgetScreen: View { 11 | 12 | @StateObject var studyRoomWidgetVM : StudyRoomWidgetViewModel 13 | 14 | var body: some View { 15 | Group { 16 | switch(self.studyRoomWidgetVM.status) { 17 | case .error: 18 | EmptyView() // -> keine study rooms nearby 19 | case .loading: 20 | LoadingView(text: "Searching nearby study rooms") 21 | case .success: 22 | StudyRoomWidgetView(studyRoomWidgetVM: self.studyRoomWidgetVM) 23 | } 24 | } 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/ViewModel/AnnotatedMapViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // AnnotatedMapViewModel.swift 3 | // Campus-iOS 4 | // 5 | // Created by Timothy Summers on 05.06.23. 6 | // 7 | 8 | import Foundation 9 | 10 | @MainActor 11 | class AnnotatedMapViewModel: ObservableObject { 12 | 13 | @Published var locations = [TUMLocation]() 14 | @Published var cafeterias = [Cafeteria]() 15 | @Published var studyRoomGroups = [StudyRoomGroup]() 16 | @Published var rooms = [NavigaTumNavigationEntity]() 17 | @Published var annotationCloser = false //remove 18 | @Published var openAnnotation: UUID? 19 | 20 | func addCafeterias(cafeterias: [Cafeteria]) { 21 | locations.append(contentsOf: cafeterias.map {TUMLocation(cafeteria: $0)}) 22 | self.cafeterias.append(contentsOf: cafeterias) 23 | } 24 | 25 | func addStudyRoomGroups(studyRoomGroups: [StudyRoomGroup]) { 26 | locations.append(contentsOf: studyRoomGroups.map {TUMLocation(studyRoomGroup: $0)}) 27 | self.studyRoomGroups.append(contentsOf: studyRoomGroups) 28 | } 29 | 30 | func addRooms(rooms: [NavigaTumNavigationEntity]) async { 31 | for room in rooms { 32 | let tempVM = NavigaTumDetailsViewModel(id: room.id) 33 | await tempVM.fetchDetails() 34 | if let details = tempVM.details { 35 | locations.append(TUMLocation(room: room, details: details)) 36 | } 37 | } 38 | self.rooms.append(contentsOf: rooms) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/ViewModel/MapViewModel+State.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MapViewModel+State.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 14.05.22. 6 | // 7 | 8 | import Foundation 9 | 10 | extension MapViewModel { 11 | enum CafeteriasNetworkState { 12 | // to make it Equitable 13 | // static func == (lhs: MapViewModel.CafeteriasNetworkState, rhs: MapViewModel.CafeteriasNetworkState) -> Bool { 14 | // switch (lhs, rhs) { 15 | // case (.na, .na): 16 | // return true 17 | // case (.loading, .loading): 18 | // return true 19 | // case (.failed(error: _), .failed(error: _)): 20 | // return true 21 | // case (.success(data: let lhsData), .success(data: let rhsData)): 22 | // return lhsData == rhsData 23 | // default: 24 | // return false 25 | // } 26 | // } 27 | 28 | case na 29 | case loading 30 | case success(data: [Cafeteria]) 31 | case failed(error: Error) 32 | } 33 | 34 | enum StudyRoomsNetworkState { 35 | case na 36 | case loading 37 | case success(data: StudyRoomApiRespose) 38 | case failed(error: Error) 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/ViewModel/MealPlanViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // Campus-iOS 4 | // 5 | // Created by Tim Gymnich on 19.01.22. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | import Alamofire 11 | 12 | @MainActor 13 | class MealPlanViewModel: ObservableObject { 14 | @Published var state: APIState<[cafeteriaMenu]> = .na 15 | @Published var hasError: Bool = false 16 | 17 | let service = MealPlanService() 18 | let cafeteria: Cafeteria 19 | 20 | init(cafeteria: Cafeteria) { 21 | self.cafeteria = cafeteria 22 | } 23 | 24 | func getMenus(forcedRefresh: Bool = false) async { 25 | if !forcedRefresh { 26 | self.state = .loading 27 | } 28 | self.hasError = false 29 | 30 | do { 31 | let data = try await service.fetch(cafeteria: self.cafeteria, forcedRefresh: forcedRefresh) 32 | print(data) 33 | self.state = .success( 34 | data: data 35 | ) 36 | } catch { 37 | self.state = .failed(error: error) 38 | self.hasError = true 39 | } 40 | } 41 | 42 | } 43 | -------------------------------------------------------------------------------- /Campus-iOS/MapComponent/ViewModel/MenuViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MealPlanViewModel.swift 3 | // Campus-iOS 4 | // 5 | // Created by Tim Gymnich on 19.01.22. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | final class cafeteriaMenu: Identifiable, Decodable { 12 | var id = UUID() 13 | let date: Date 14 | var categories: [MenuCategory] 15 | 16 | init(date: Date, categories: [MenuCategory]) { 17 | self.date = date 18 | self.categories = categories 19 | } 20 | 21 | func getDishes() -> [Dish] { 22 | var dishes: [Dish] = [] 23 | for category in categories { 24 | for dish in category.dishes { 25 | dishes.append(dish) 26 | } 27 | } 28 | 29 | return dishes.sorted(by: { $0.name < $1.name }) 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Campus-iOS/MoviesComponent/Model/Movie+PreviewData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Movie+PreviewData.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 05.05.23. 6 | // 7 | 8 | import Foundation 9 | 10 | extension Movie: Identifiable { 11 | static let dummyData: Movie = .init( 12 | id: 123, 13 | actors: "Morgan Freeman", 14 | cover: URL(string:"https://www.google.com"), 15 | created: Date.now, 16 | date: Date.now, 17 | director: "Frank Darapant", 18 | genre: "Crime", 19 | link: URL(string:"https://www.google.com"), 20 | movieDescription: "Yes", 21 | rating: "11/10", 22 | runtime: "194", 23 | title: "Shawshank Redemption", 24 | year: "2020" 25 | ) 26 | 27 | static let previewData: [Movie] = [dummyData] 28 | } 29 | -------------------------------------------------------------------------------- /Campus-iOS/MoviesComponent/Service/MovieService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MovieService.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 14.01.23. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol MovieServiceProtocol { 11 | func fetch(forcedRefresh: Bool) async throws -> [Movie] 12 | } 13 | 14 | struct MovieService: ServiceProtocol, MovieServiceProtocol { 15 | func fetch(forcedRefresh: Bool = false) async throws -> [Movie] { 16 | 17 | let response: [Movie] = try await MainAPI.makeRequest(endpoint: TUMCabeAPI.movie, forcedRefresh: forcedRefresh) 18 | 19 | return response 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/MoviesComponent/Views/MovieDetailCellView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MovieDetailCellView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Milen Vitanov on 27.01.22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct MovieDetailCellView: View { 11 | var property: (key: LocalizedStringKey, value: [String]) 12 | 13 | var body: some View { 14 | HStack { 15 | Text(property.key) 16 | .font(.system(size: 18, weight: .semibold)) 17 | .foregroundColor(Color("tumBlue")) 18 | .multilineTextAlignment(.leading) 19 | .frame(maxWidth: .infinity, alignment: .leading) 20 | Spacer().frame(width: 20) 21 | VStack(alignment: .leading) { 22 | ForEach(property.value, id: \.self) { propVal in 23 | Text(propVal) 24 | .font(.system(size: 16)) 25 | .multilineTextAlignment(.leading) 26 | } 27 | }.frame(maxWidth: .infinity, alignment: .leading) 28 | }.font(.caption) 29 | .foregroundColor(.secondary) 30 | } 31 | } 32 | 33 | struct MovieDetailCellView_Previews: PreviewProvider { 34 | static var previews: some View { 35 | MovieDetailCellView(property: ("Description", ["Some Description"])) 36 | .previewLayout(.sizeThatFits) 37 | } 38 | } 39 | 40 | -------------------------------------------------------------------------------- /Campus-iOS/MoviesComponent/Views/MovieDetailsBasicInfoRowView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MovieDetaislBasicInfoRowView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Atharva Mathapati on 23.06.22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct MovieDetailsBasicInfoRowView: View { 11 | var iconName: String 12 | var text: String 13 | 14 | var body: some View { 15 | HStack(alignment: .center, spacing: 12) { 16 | Image(systemName: iconName) 17 | .imageScale(.medium) 18 | .frame(width: 25, height: 25, alignment: .center) 19 | Text(text) 20 | .font(.system(size: 16)) 21 | } 22 | } 23 | } 24 | 25 | struct MovieDetaislBasicInfoRowView_Previews: PreviewProvider { 26 | static var previews: some View { 27 | MovieDetailsBasicInfoRowView( 28 | iconName: "number", 29 | text: "test" 30 | ) 31 | } 32 | } 33 | 34 | -------------------------------------------------------------------------------- /Campus-iOS/MoviesComponent/Views/MovieDetailsDetailedInfoRowView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MovieDetailsDetailedInfoRowView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Atharva Mathapati on 29.06.22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct MovieDetailsDetailedInfoRowView: View { 11 | var title: String 12 | var text: String 13 | 14 | var body: some View { 15 | VStack(alignment: .leading, spacing: 8) { 16 | Text(title) 17 | .font(.system(size: 16, weight: .semibold)) 18 | 19 | Text(text) 20 | .font(.system(size: 16)) 21 | } 22 | } 23 | } 24 | 25 | struct MovieDetailsDetailedInfoRowView_Previews: PreviewProvider { 26 | static var previews: some View { 27 | MovieDetailsDetailedInfoRowView( 28 | title: "number", 29 | text: "test" 30 | ) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Campus-iOS/MoviesComponent/Views/MovieView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MoviesView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Milen Vitanov on 27.01.22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct MovieView: View { 11 | let movies: [Movie] 12 | @State private var selectedMovie: Movie? = nil 13 | 14 | var items: [GridItem] { 15 | Array(repeating: .init(.adaptive(minimum: 120)), count: 2) 16 | } 17 | 18 | var body: some View { 19 | ZStack { 20 | Text("No more movies this semester 😢\nGet excited for the next season!") 21 | .foregroundColor(Color(UIColor.lightGray)) 22 | ScrollView(.vertical) { 23 | LazyVGrid(columns: items, spacing: 10) { 24 | ForEach(self.movies, id: \.id ) { movie in 25 | MovieCard(movie: movie).padding(7) 26 | .onTapGesture { 27 | selectedMovie = movie 28 | } 29 | } 30 | .sheet(item: $selectedMovie) { movie in 31 | MovieDetailedView(movie: movie) 32 | } 33 | } 34 | .padding(10) 35 | .background(Color.systemsBackground) 36 | } 37 | } 38 | } 39 | } 40 | 41 | //struct MoviesView_Previews: PreviewProvider { 42 | // static var previews: some View { 43 | // MoviesView() 44 | // } 45 | //} 46 | -------------------------------------------------------------------------------- /Campus-iOS/MoviesComponent/Views/MoviesView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MoviesView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Milen Vitanov on 27.01.22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct MoviesView: View { 11 | let movies: [Movie] 12 | @State private var selectedMovie: Movie? = nil 13 | 14 | var items: [GridItem] { 15 | Array(repeating: .init(.adaptive(minimum: 120)), count: 2) 16 | } 17 | 18 | var body: some View { 19 | ZStack { 20 | Text("No more movies this semester 😢\nGet excited for the next season!") 21 | .foregroundColor(Color(UIColor.lightGray)) 22 | ScrollView(.vertical) { 23 | LazyVGrid(columns: items, spacing: 10) { 24 | ForEach(self.movies, id: \.id ) { movie in 25 | MovieCard(movie: movie).padding(7) 26 | .onTapGesture { 27 | selectedMovie = movie 28 | } 29 | } 30 | .sheet(item: $selectedMovie) { movie in 31 | MovieDetailedView(movie: movie) 32 | } 33 | } 34 | .padding(10) 35 | .background(Color.systemsBackground) 36 | } 37 | } 38 | } 39 | } 40 | 41 | //struct MoviesView_Previews: PreviewProvider { 42 | // static var previews: some View { 43 | // MoviesView() 44 | // } 45 | //} 46 | -------------------------------------------------------------------------------- /Campus-iOS/MoviesComponent/Views/Widget/MoviesWidgetView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MoviesWidgetView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Timothy Summers on 17.01.23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct MoviesWidgetView: View { 11 | 12 | let movies: [Movie] 13 | @State private var selectedMovie: Movie? = nil 14 | 15 | var items: [GridItem] { 16 | Array(repeating: .init(.adaptive(minimum: 120)), count: 2) 17 | } 18 | 19 | var body: some View { 20 | VStack(spacing: 0) { 21 | Text("TU Film").titleStyle() 22 | 23 | ScrollView(.horizontal, showsIndicators: false) { 24 | HStack(spacing: 15) { 25 | ForEach(movies, id: \.id ) { movie in 26 | MovieCard(movie: movie) 27 | .onTapGesture { 28 | selectedMovie = movie 29 | } 30 | } 31 | .sheet(item: $selectedMovie) { movie in 32 | MovieDetailedView(movie: movie) 33 | } 34 | } 35 | .padding(.horizontal, 20) 36 | } 37 | } 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Campus-iOS/NewsComponent/Model/News+PreviewData.swift: -------------------------------------------------------------------------------- 1 | // 2 | // News+PreviewData.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 05.05.23. 6 | // 7 | 8 | import Foundation 9 | 10 | extension News { 11 | static let previewData: [News] = [ 12 | News(id: "test", sourceId: 1, date: Date.now, created: Date.now, title: "Testing news 1", link: URL(string: "https://www.tum.de"), imageURL: nil), 13 | News(id: "test1", sourceId: 1, date: Date.now, created: Date.now, title: "Testing news 2", link: URL(string: "https://www.moodle.tum.de"), imageURL: nil), 14 | News(id: "test2", sourceId: 1, date: Date.now, created: Date.now, title: "Testing news 3", link: URL(string: "https://www.live.rgb.tum.de"), imageURL: nil) 15 | ] 16 | } 17 | -------------------------------------------------------------------------------- /Campus-iOS/NewsComponent/Screen/NewsScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NewsScreen.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 22.01.23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct NewsScreen: View { 11 | @StateObject var vm = NewsViewModel() 12 | let isWidget: Bool 13 | 14 | var body: some View { 15 | Group { 16 | switch vm.state { 17 | case .success(let newsSources): 18 | if isWidget { 19 | NewsWidgetView(latestFiveNews: vm.latestFiveNews) 20 | } else { 21 | VStack { 22 | NewsView(latestFiveNews: vm.latestFiveNews, newsSources: newsSources) 23 | .refreshable { 24 | await vm.getNewsSources(forcedRefresh: true) 25 | } 26 | } 27 | } 28 | case .loading, .na: 29 | LoadingView(text: "Fetching News") 30 | .padding(.vertical) 31 | case .failed(let error): 32 | if isWidget { 33 | EmptyView() 34 | } else { 35 | FailedView( 36 | errorDescription: error.localizedDescription, 37 | retryClosure: vm.getNewsSources 38 | ) 39 | } 40 | } 41 | }.task { 42 | await vm.getNewsSources(forcedRefresh: true) 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /Campus-iOS/NewsComponent/Service/NewsService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NewsService.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 13.01.23. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol NewsServiceProtocol { 11 | func fetch(forcedRefresh: Bool) async throws -> [NewsSource] 12 | 13 | func fetch(forcedRefresh: Bool, source: String) async throws -> [News] 14 | } 15 | 16 | struct NewsService: ServiceProtocol, NewsServiceProtocol { 17 | func fetch(forcedRefresh: Bool = false) async throws -> [NewsSource] { 18 | 19 | var newsSourceResponse: [NewsSource] = try await MainAPI.makeRequest(endpoint: TUMCabeAPI.newsSources, forcedRefresh: forcedRefresh) 20 | 21 | for i in newsSourceResponse.indices { 22 | guard let idDescription = newsSourceResponse[i].id?.description else { 23 | break 24 | } 25 | 26 | let news: [News] = try await MainAPI.makeRequest(endpoint: TUMCabeAPI.news(source: String(idDescription))) 27 | 28 | newsSourceResponse[i].news = news 29 | } 30 | 31 | return newsSourceResponse 32 | } 33 | 34 | func fetch(forcedRefresh: Bool, source: String) async throws -> [News] { 35 | let news: [News] = try await MainAPI.makeRequest(endpoint: TUMCabeAPI.news(source: "1")) 36 | 37 | return news 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /Campus-iOS/PersonDetailedComponent/Entity/Organization.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Organization.swift 3 | // Campus-iOS 4 | // 5 | // Created by Milen Vitanov on 06.02.22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Organisation: Decodable, Identifiable { 11 | let name: String 12 | let id: String 13 | let number: String 14 | let title: String 15 | let description: String 16 | 17 | enum CodingKeys: String, CodingKey { 18 | case name = "org" 19 | case id = "kennung" 20 | case number = "org_nr" 21 | case title = "titel" 22 | case description = "beschreibung" 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Campus-iOS/PersonDetailedComponent/Entity/PhoneExtension.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PhoneExtension.swift 3 | // Campus-iOS 4 | // 5 | // Created by Milen Vitanov on 06.02.22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct PhoneExtension: Decodable, Identifiable { 11 | let id = UUID() 12 | let phoneNumber: String 13 | let countryCode: String 14 | let areaCode: String 15 | let equipmentNumber: String 16 | let branchNumber: String 17 | 18 | enum CodingKeys: String, CodingKey { 19 | case phoneNumber = "telefonnummer" 20 | case countryCode = "tum_anlage_land" 21 | case areaCode = "tum_anlage_ortsvorwahl" 22 | case equipmentNumber = "tum_anlage_nummer" 23 | case branchNumber = "tum_nebenstelle" 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Campus-iOS/PersonDetailedComponent/Entity/Room.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Room.swift 3 | // Campus-iOS 4 | // 5 | // Created by Milen Vitanov on 06.02.22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct Room: Decodable, Identifiable { 11 | let number: String 12 | let buildingName: String 13 | let buildingNumber: String 14 | let floorName: String 15 | let floorNumber: String 16 | let id: String 17 | let locationDescription: String 18 | let shortLocationDescription: String 19 | let longLocationDescription: String 20 | 21 | enum CodingKeys: String, CodingKey { 22 | case number = "nummer" 23 | case buildingName = "gebaeudename" 24 | case buildingNumber = "gebaeudenummer" 25 | case floorName = "stockwerkname" 26 | case floorNumber = "stockwerknummer" 27 | case id = "architekt" 28 | case locationDescription = "ortsbeschreibung" 29 | case shortLocationDescription = "kurz" 30 | case longLocationDescription = "lang" 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Campus-iOS/PersonDetailedComponent/Service/PersonDetailedService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PersonDetailedService.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 21.01.23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct PersonDetailedService { 11 | func fetch(for id: String, token: String, forcedRefresh: Bool) async throws -> PersonDetails { 12 | let response : PersonDetails = try await MainAPI.makeRequest(endpoint: TUMOnlineAPI.personDetails(identNumber: id), token: token, forcedRefresh: forcedRefresh) 13 | 14 | return response 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Campus-iOS/PersonSearchComponent/Service/PersonSearchService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PersonSearchService.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 21.01.23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct PersonSearchService { 11 | func fetch(for query: String, token: String, forcedRefresh: Bool) async throws -> [Person] { 12 | let response : TUMOnlineAPI.Response = try await MainAPI.makeRequest(endpoint: TUMOnlineAPI.personSearch(search: query), token: token, forcedRefresh: forcedRefresh) 13 | 14 | return response.row 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Campus-iOS/PersonSearchComponent/View/PersonSearchView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PersonSearchListView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Milen Vitanov on 13.02.22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct PersonSearchView: View { 11 | let model: Model 12 | let persons: [Person] 13 | 14 | var body: some View { 15 | List { 16 | ForEach(self.persons, id: \.nr) { person in 17 | NavigationLink( 18 | destination: 19 | PersonDetailedScreenSearch(model: model, person: person) 20 | .navigationBarTitleDisplayMode(.inline) 21 | ) { 22 | Text(person.fullName) 23 | } 24 | } 25 | } 26 | } 27 | } 28 | 29 | struct PersonSearchView_Previews: PreviewProvider { 30 | static var previews: some View { 31 | PersonSearchView(model: Model(), persons: []) 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Campus-iOS/PersonSearchComponent/ViewModel/PersonSearchViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PersonSearchViewModel.swift 3 | // Campus-iOS 4 | // 5 | // Created by Milen Vitanov on 06.02.22. 6 | // 7 | 8 | import Foundation 9 | import Alamofire 10 | import XMLCoder 11 | 12 | @MainActor 13 | class PersonSearchViewModel: ObservableObject { 14 | @Published var state: APIState<[Person]> = .na 15 | @Published var hasError: Bool = false 16 | 17 | let model: Model 18 | let service: PersonSearchService 19 | 20 | init(model: Model, service: PersonSearchService) { 21 | self.model = model 22 | self.service = service 23 | } 24 | 25 | func getPersons(for query: String, forcedRefresh: Bool) async { 26 | if !forcedRefresh { 27 | self.state = .loading 28 | } 29 | self.hasError = false 30 | 31 | guard let token = self.model.token else { 32 | self.state = .failed(error: NetworkingError.unauthorized) 33 | self.hasError = true 34 | return 35 | } 36 | 37 | do { 38 | self.state = .success( 39 | data: try await service.fetch(for: query, token: token, forcedRefresh: forcedRefresh) 40 | ) 41 | 42 | } catch { 43 | self.state = .failed(error: error) 44 | self.hasError = true 45 | } 46 | } 47 | } 48 | -------------------------------------------------------------------------------- /Campus-iOS/ProfileComponent/View/ProfileToolbar.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ProfileToolbarGroup.swift 3 | // Campus-iOS 4 | // 5 | // Created by Anton Wyrowski on 11.12.21. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ProfileToolbar: View { 11 | @StateObject var model: Model 12 | @State var showProfile = false 13 | 14 | var body: some View { 15 | 16 | Button(action: {self.showProfile.toggle()}) { 17 | Image(systemName: "person.crop.circle") 18 | } 19 | .sheet(isPresented: $showProfile) { 20 | ProfileView(model: model) 21 | } 22 | 23 | } 24 | } 25 | 26 | struct ProfileToolbarGroup_Previews: PreviewProvider { 27 | static var previews: some View { 28 | ProfileToolbar(model: Model()) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /Campus-iOS/RoomFinderComponent/Entity/FoundRoom.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Room.swift 3 | // Campus-iOS 4 | // 5 | // Created by Milen Vitanov on 17.05.22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct FoundRoom: Codable, Hashable { 11 | /* 12 | { 13 | "room_id": "59773", 14 | "room_code": "5302.TP.208", 15 | "building_nr": "5302", 16 | "arch_id": "-1.208@5302", 17 | "info": "-1.208, Cleanroom\/EI. Mikroskop", 18 | "address": "Lichtenbergstr. 2(5302), Tiefparterre", 19 | "purpose": "Labor - Physik", 20 | "campus": "G", 21 | "name": "Garching" 22 | } 23 | */ 24 | 25 | let roomId: String 26 | let roomCode: String 27 | let buildingNumber: String 28 | let id: String 29 | let info: String 30 | let address: String 31 | let purpose: String 32 | let campus: String? 33 | let name: String? 34 | 35 | enum CodingKeys: String, CodingKey { 36 | case roomId = "room_id" 37 | case roomCode = "room_code" 38 | case buildingNumber = "building_nr" 39 | case id = "arch_id" 40 | case info 41 | case address 42 | case purpose 43 | case campus 44 | case name 45 | } 46 | 47 | } 48 | -------------------------------------------------------------------------------- /Campus-iOS/RoomFinderComponent/Entity/RoomFinderLocation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigaTumLocation.swift 3 | // Campus-iOS 4 | // 5 | // Created by Jakob Paul Körber on 06.10.23. 6 | // 7 | 8 | import Foundation 9 | import MapKit 10 | 11 | struct RoomFinderLocation: Identifiable { 12 | let id = UUID() 13 | let coordinate: CLLocationCoordinate2D 14 | } 15 | -------------------------------------------------------------------------------- /Campus-iOS/RoomFinderComponent/Model/Details/NavigaTumNavigationAdditionalProperties.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigationAdditionalProperties.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 01.01.23. 6 | // 7 | import Foundation 8 | 9 | struct NavigaTumNavigationAdditionalProperties: Codable { 10 | let properties: [NavigaTumNavigationProperty] 11 | 12 | enum CodingKeys: String, CodingKey { 13 | case properties = "computed" 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /Campus-iOS/RoomFinderComponent/Model/Details/NavigaTumNavigationCoordinates.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigationCoordinates.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 01.01.23. 6 | // 7 | import Foundation 8 | 9 | struct NavigaTumNavigationCoordinates: Codable { 10 | let latitude: Double 11 | let longitude: Double 12 | 13 | enum CodingKeys: String, CodingKey { 14 | case latitude = "lat" 15 | case longitude = "lon" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Campus-iOS/RoomFinderComponent/Model/Details/NavigaTumNavigationMaps.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigationMaps.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 01.01.23. 6 | // 7 | import Foundation 8 | 9 | struct NavigaTumNavigationMaps: Codable { 10 | let `default`: String 11 | let roomfinder: NavigaTumRoomFinderMaps? 12 | let overlays: NavigaTumOverlaysMaps? 13 | } 14 | -------------------------------------------------------------------------------- /Campus-iOS/RoomFinderComponent/Model/Details/NavigaTumOverlaysMaps.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigaTumOverlaysMaps.swift 3 | // Campus-iOS 4 | // 5 | // Created by Atharva Mathapati on 07.03.23. 6 | // 7 | import Foundation 8 | 9 | struct NavigaTumOverlaysMaps: Codable { 10 | let available: [NavigaTumOverlayMap] 11 | } 12 | -------------------------------------------------------------------------------- /Campus-iOS/RoomFinderComponent/Model/Details/NavigaTumRoomFinderMaps.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RoomFinderMaps.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 01.01.23. 6 | // 7 | import Foundation 8 | 9 | struct NavigaTumRoomFinderMaps: Codable { 10 | let available: [NavigaTumRoomFinderMap] 11 | let defaultMapId: String 12 | 13 | enum CodingKeys: String, CodingKey { 14 | case available 15 | case defaultMapId = "default" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Campus-iOS/RoomFinderComponent/Model/NavigaTumNavigationDetails.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigationDetails.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 01.01.23. 6 | // 7 | import Foundation 8 | 9 | struct NavigaTumNavigationDetails: Codable { 10 | let id: String 11 | let name: String 12 | let parentNames: [String] 13 | let type: String 14 | let typeCommonName: String 15 | let additionalProperties: NavigaTumNavigationAdditionalProperties 16 | let coordinates: NavigaTumNavigationCoordinates 17 | let maps: NavigaTumNavigationMaps 18 | 19 | enum CodingKeys: String, CodingKey { 20 | case id, name, type, maps 21 | case typeCommonName = "type_common_name" 22 | case additionalProperties = "props" 23 | case coordinates = "coords" 24 | case parentNames = "parent_names" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Campus-iOS/RoomFinderComponent/Model/NavigaTumNavigationProperty.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigationProperty.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 01.01.23. 6 | // 7 | import Foundation 8 | 9 | struct NavigaTumNavigationProperty: Codable { 10 | let name: String 11 | let text: String 12 | } 13 | -------------------------------------------------------------------------------- /Campus-iOS/RoomFinderComponent/Model/NavigaTumOverlayMap.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigaTumOverlayMap.swift 3 | // Campus-iOS 4 | // 5 | // Created by Atharva Mathapati on 07.03.23. 6 | // 7 | import Foundation 8 | 9 | struct NavigaTumOverlayMap: Codable, Identifiable { 10 | let id: Int 11 | let floor: String 12 | let imageUrl: String 13 | let name: String 14 | 15 | enum CodingKeys: String, CodingKey { 16 | case id, floor, name 17 | case imageUrl = "file" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /Campus-iOS/RoomFinderComponent/Model/NavigaTumRoomFinderMap.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RoomFinderMap.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 01.01.23. 6 | // 7 | import Foundation 8 | 9 | struct NavigaTumRoomFinderMap: Codable, Identifiable { 10 | let id: String 11 | let name: String 12 | let imageUrl: String // let baseMapUrl = "https://nav.tum.sexy/cdn/maps/roomfinder/" 13 | let height: Int 14 | let width: Int 15 | let x: Int 16 | let y: Int 17 | let scale: String 18 | 19 | enum CodingKeys: String, CodingKey { 20 | case id, name, height, width, x, y, scale 21 | case imageUrl = "file" 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /Campus-iOS/RoomFinderComponent/Model/Search/NavigaTumSearchResponse.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigaTumSearchResponse.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 01.01.23. 6 | // 7 | import Foundation 8 | 9 | struct NavigaTumSearchResponse: Codable { 10 | let id = UUID() 11 | let sections: [NavigaTumSearchResponseSection] 12 | 13 | enum CodingKeys: CodingKey { 14 | case sections 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /Campus-iOS/RoomFinderComponent/Model/Search/NavigaTumSearchResponseSection.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigaTumSearchResponseSection.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 01.01.23. 6 | // 7 | import Foundation 8 | 9 | struct NavigaTumSearchResponseSection: Codable { 10 | let type: String 11 | let entries: [NavigaTumNavigationEntity] 12 | 13 | enum CodingKeys: String, CodingKey { 14 | case type = "facet" 15 | case entries 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /Campus-iOS/RoomFinderComponent/Service/RoomFinderService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RoomFinderService.swift 3 | // Campus-iOS 4 | // 5 | // Created by Philipp Zagar on 01.01.23. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol RoomFinderServiceProtocol { 11 | func search(query: String) async throws -> NavigaTumSearchResponse 12 | func details(id: String) async throws -> NavigaTumNavigationDetails 13 | } 14 | 15 | struct RoomFinderService: RoomFinderServiceProtocol { 16 | 17 | func search(query: String) async throws -> NavigaTumSearchResponse { 18 | return try await MainAPI.makeRequest(endpoint: NavigaTUMAPI.search(query: query)) 19 | } 20 | 21 | func details(id: String) async throws -> NavigaTumNavigationDetails { 22 | let language = (Locale.current.language.languageCode?.identifier == "de") ? "de" : "en" 23 | return try await MainAPI.makeRequest(endpoint: NavigaTUMAPI.details(id: id, language: language)) 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Campus-iOS/RoomFinderComponent/ViewModel/NavigaTumDetailsViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigaTumDetailsViewModel.swift 3 | // Campus-iOS 4 | // 5 | // Created by Atharva Mathapati on 07.01.23. 6 | // 7 | import Foundation 8 | 9 | import Foundation 10 | import Alamofire 11 | import XMLCoder 12 | 13 | class NavigaTumDetailsViewModel: ObservableObject { 14 | @Published var details: NavigaTumNavigationDetails? 15 | @Published var errorMessage = "" 16 | let id: String 17 | 18 | init(id: String) { 19 | self.id = id 20 | } 21 | 22 | @MainActor func fetchDetails() async { 23 | guard !id.isEmpty else { 24 | self.errorMessage = "Couldn't fetch room details" 25 | return 26 | } 27 | do { 28 | self.details = try await RoomFinderService().details(id: id) 29 | } catch { 30 | self.errorMessage = "Room finder service failed" 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Campus-iOS/RoomFinderComponent/ViewModel/NavigaTumViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigaTumViewModel.swift 3 | // Campus-iOS 4 | // 5 | // Created by Atharva Mathapati on 07.01.23. 6 | // 7 | import Foundation 8 | import Alamofire 9 | import XMLCoder 10 | 11 | class NavigaTumViewModel: ObservableObject { 12 | @Published var searchResults: [NavigaTumNavigationEntity] = [] 13 | @Published var errorMessage: String = "" 14 | 15 | @MainActor func fetch(searchString: String) async { 16 | guard !searchString.isEmpty else { 17 | self.errorMessage = "" 18 | return 19 | } 20 | do { 21 | let results = try await RoomFinderService().search(query: searchString) 22 | self.searchResults = results.sections.flatMap(\.entries) 23 | } catch { 24 | self.errorMessage = "Room Service Failed" 25 | } 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /Campus-iOS/RoomFinderComponent/Views/NavigaTumView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // NavigaTumView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Atharva Mathapati on 06.01.23. 6 | // 7 | import SwiftUI 8 | 9 | struct NavigaTumView: View { 10 | @ObservedObject var model: Model 11 | @StateObject var viewModel = NavigaTumViewModel() 12 | @State var searchText = "" 13 | 14 | var body: some View { 15 | NavigaTumListView(model: self.model, viewModel: self.viewModel) 16 | .background(Color(.systemGroupedBackground)) 17 | .searchable(text: $searchText, placement: .navigationBarDrawer(displayMode: .always)) 18 | .onChange(of: self.searchText) { searchValue in 19 | if searchValue.count > 3 { 20 | Task { 21 | await search(searchValue) 22 | } 23 | } 24 | } 25 | .task { 26 | if !searchText.isEmpty { 27 | await search(searchText) 28 | } 29 | } 30 | .animation(.default, value: self.viewModel.searchResults) 31 | } 32 | 33 | func search(_ searchValue: String) async { 34 | await self.viewModel.fetch(searchString: searchValue) 35 | } 36 | } 37 | 38 | struct NavigaTumView_Previews: PreviewProvider { 39 | static var previews: some View { 40 | NavigaTumView(model: MockModel()) 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /Campus-iOS/RoomFinderComponent/Views/RoomDetailsScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RoomDetailsScreen.swift 3 | // Campus-iOS 4 | // 5 | // Created by Timothy Summers on 09.06.23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct RoomDetailsScreen: View { 11 | 12 | var room: NavigaTumNavigationEntity 13 | @StateObject var vm: NavigaTumDetailsViewModel 14 | 15 | init(room: NavigaTumNavigationEntity) { 16 | self.room = room 17 | self._vm = StateObject(wrappedValue: NavigaTumDetailsViewModel(id: room.id)) 18 | } 19 | 20 | var body: some View { 21 | Group { 22 | if let details = vm.details { 23 | LocationView(location: TUMLocation(room: self.room, details: details)) 24 | } else { 25 | ProgressView() 26 | } 27 | }.task { 28 | await vm.fetchDetails() 29 | } 30 | } 31 | } 32 | 33 | -------------------------------------------------------------------------------- /Campus-iOS/SearchComponent/Types and Protocols/ComparisonToken.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ComparisonToken.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 27.12.22. 6 | // 7 | 8 | import Foundation 9 | 10 | infix operator =/ 11 | 12 | struct ComparisonToken: Hashable { 13 | var value: String 14 | var type: ComparisonTokenType = .tokenized 15 | 16 | enum ComparisonTokenType { 17 | case tokenized 18 | case raw 19 | } 20 | 21 | static func =/ (lhs: Self, rhs: Self) -> Bool { 22 | guard lhs.value.count == rhs.value.count else { 23 | return false 24 | } 25 | 26 | for i in 0..) -> String { 12 | return String(self.filter {validChars.contains($0)}) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Campus-iOS/SearchComponent/Types and Protocols/SearchError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchError.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 07.03.23. 6 | // 7 | 8 | import Foundation 9 | 10 | enum SearchError: Error, CustomStringConvertible{ 11 | case empty(searchQuery: String) 12 | case unexpected 13 | 14 | public var description: String { 15 | switch self { 16 | case .empty(let searchQuery): 17 | return "No search results were found for: \"\(searchQuery)\"." 18 | case .unexpected: 19 | return "An unexpected error occurred." 20 | } 21 | } 22 | } 23 | 24 | -------------------------------------------------------------------------------- /Campus-iOS/SearchComponent/Types and Protocols/SearchState.swift: -------------------------------------------------------------------------------- 1 | // 2 | // File.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 05.05.23. 6 | // 7 | 8 | import Foundation 9 | 10 | enum SearchState { 11 | case na 12 | case loading 13 | case success(data: [(T, Distances)]) 14 | case failed(error: Error) 15 | } 16 | -------------------------------------------------------------------------------- /Campus-iOS/SearchComponent/ViewModels/Searchable ViewModels/CafeteriaSearchResultViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // CafeteriaSearchResultViewModel.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 27.12.22. 6 | // 7 | 8 | import Foundation 9 | 10 | @MainActor 11 | class CafeteriaSearchResultViewModel: ObservableObject { 12 | 13 | @Published var state: SearchState = .na 14 | @Published var hasError: Bool = false 15 | let service: CafeteriasServiceProtocol 16 | 17 | init(service: CafeteriasServiceProtocol) { 18 | self.service = service 19 | } 20 | 21 | func cafeteriasSearch(for query: String, forcedRefresh: Bool = false) async { 22 | if !forcedRefresh { 23 | self.state = .loading 24 | } 25 | self.hasError = false 26 | 27 | do { 28 | let data = try await service.fetch(forcedRefresh: forcedRefresh) 29 | if let optionalResults = GlobalSearch.tokenSearch(for: query, in: data) { 30 | self.state = .success(data: optionalResults) 31 | } else { 32 | self.state = .failed(error: SearchError.empty(searchQuery: query)) 33 | } 34 | 35 | } catch { 36 | self.state = .failed(error: error) 37 | self.hasError = true 38 | } 39 | } 40 | } 41 | -------------------------------------------------------------------------------- /Campus-iOS/SearchComponent/ViewModels/Searchable ViewModels/MovieSearchResultViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // MovieSearchResultView.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 07.03.23. 6 | // 7 | 8 | import Foundation 9 | 10 | @MainActor 11 | class MovieSearchResultViewModel: ObservableObject { 12 | @Published var state: SearchState = .na 13 | @Published var hasError: Bool = false 14 | 15 | let service: MovieServiceProtocol 16 | 17 | init(service: MovieServiceProtocol) { 18 | self.service = service 19 | } 20 | 21 | func movieSearch(for query: String, forcedRefresh: Bool = false) async { 22 | 23 | if !forcedRefresh { 24 | self.state = .loading 25 | } 26 | self.hasError = false 27 | 28 | do { 29 | let data = try await service.fetch(forcedRefresh: forcedRefresh) 30 | let filteredMovies = data.filter { movie in 31 | return (movie.date ?? Date.distantPast) >= Date() 32 | } 33 | 34 | if let optionalResults = GlobalSearch.tokenSearch(for: query, in: filteredMovies) { 35 | self.state = .success(data: optionalResults) 36 | } else { 37 | self.state = .failed(error: SearchError.empty(searchQuery: query)) 38 | self.hasError = true 39 | } 40 | 41 | } catch { 42 | self.state = .failed(error: error) 43 | self.hasError = true 44 | } 45 | } 46 | } 47 | 48 | -------------------------------------------------------------------------------- /Campus-iOS/SearchComponent/ViewModels/Searchable ViewModels/RoomFinderSearchResultViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RoomFinderSearchViewModel.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 13.01.23. 6 | // 7 | 8 | import Foundation 9 | 10 | @MainActor 11 | class RoomFinderSearchResultViewModel: ObservableObject { 12 | @Published var state: APIState<[NavigaTumNavigationEntity]> = .na 13 | @Published var hasError: Bool = false 14 | 15 | func roomFinderSearch(for query: String, forcedRefresh: Bool = false) async { 16 | if !forcedRefresh { 17 | self.state = .loading 18 | } 19 | self.hasError = false 20 | 21 | do { 22 | self.state = .success(data: try await RoomFinderService().search(query: query).sections.flatMap(\.entries)) 23 | } catch { 24 | self.state = .failed(error: error) 25 | self.hasError = true 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /Campus-iOS/SearchComponent/Views/Additional Views/SearchResultErrorView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchResultErrorView.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 07.03.23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct SearchResultErrorView: View { 11 | @State var title: String 12 | @State var error: String 13 | 14 | var body: some View { 15 | VStack { 16 | HStack { 17 | Image(systemName: "exclamationmark.triangle") 18 | .fontWeight(.semibold) 19 | .font(.title2) 20 | .foregroundColor(Color.highlightText) 21 | Text(title) 22 | .fontWeight(.semibold) 23 | .font(.title2) 24 | .foregroundColor(Color.highlightText) 25 | Spacer() 26 | } 27 | Divider() 28 | Text("Error searching: \(error)") 29 | } 30 | } 31 | } 32 | 33 | struct SearchResultErrorView_Previews: PreviewProvider { 34 | static var previews: some View { 35 | SearchResultErrorView(title: "Grades", error: "No internet connection.") 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /Campus-iOS/SearchComponent/Views/Additional Views/SearchResultLoadingView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchResultLoadingView.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 07.03.23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct SearchResultLoadingView: View { 11 | @State var title: String 12 | 13 | var body: some View { 14 | VStack { 15 | HStack { 16 | Image(systemName: "arrow.triangle.2.circlepath") 17 | .fontWeight(.semibold) 18 | .font(.title2) 19 | .foregroundColor(Color.highlightText) 20 | Text(title) 21 | .fontWeight(.semibold) 22 | .font(.title2) 23 | .foregroundColor(Color.highlightText) 24 | Spacer() 25 | } 26 | Divider() 27 | LoadingView(text: "Searching...") 28 | } 29 | } 30 | } 31 | 32 | struct SearchResultLoadingView_Previews: PreviewProvider { 33 | static var previews: some View { 34 | SearchResultLoadingView(title: "Grades") 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /Campus-iOS/SearchComponent/Views/Extensions and Custom Views/ExpandIcon.swift: -------------------------------------------------------------------------------- 1 | // 2 | // ExpandIcon.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 06.05.23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct ExpandIcon: View { 11 | @Binding var size: ResultSize 12 | 13 | var body: some View { 14 | HStack(alignment: .center) { 15 | Spacer() 16 | Button { 17 | withAnimation { 18 | switch size { 19 | case .big: 20 | self.size = .small 21 | case .small: 22 | self.size = .big 23 | } 24 | } 25 | } label: { 26 | if self.size == .small { 27 | Image(systemName: "arrow.up.left.and.arrow.down.right") 28 | } else { 29 | Image(systemName: "arrow.down.right.and.arrow.up.left") 30 | } 31 | } 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Campus-iOS/SearchComponent/Views/Extensions and Custom Views/View+Search.swift: -------------------------------------------------------------------------------- 1 | // 2 | // View+Search.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 06.05.23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | extension View { 11 | func searchStyle() -> some View { 12 | modifier(Search()) 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /Campus-iOS/SearchComponent/Views/SearchResultViews/Grade/GradeSearchResultScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // GradeSearchResultScreen.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 07.03.23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct GradesSearchResultScreen: View { 11 | @StateObject var vm: GradesSearchResultViewModel 12 | @Binding var query: String 13 | @State var size: ResultSize = .small 14 | 15 | var body: some View { 16 | Group { 17 | switch vm.state { 18 | case .success(let data): 19 | GradesSearchResultView(allResults: data, size: self.size) 20 | case .loading, .na: 21 | SearchResultLoadingView(title: "Grades") 22 | case .failed(let error): 23 | SearchResultErrorView(title: "Grades", error: error.localizedDescription) 24 | } 25 | }.onChange(of: query) { newQuery in 26 | Task { 27 | await vm.gradesSearch(for: newQuery) 28 | } 29 | } 30 | .task { 31 | await vm.gradesSearch(for: query) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Campus-iOS/SearchComponent/Views/SearchResultViews/Lecture/LectureSearchResultScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // LectureSearchResultScreen.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 07.03.23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct LectureSearchResultScreen: View { 11 | @StateObject var vm: LectureSearchResultViewModel 12 | @Binding var query: String 13 | @State var size: ResultSize = .small 14 | 15 | var body: some View { 16 | Group { 17 | switch vm.state { 18 | case .success(let data): 19 | LectureSearchResultView(allResults: data, model: vm.model, size: self.size) 20 | case .loading, .na: 21 | SearchResultLoadingView(title: "Lectures") 22 | case .failed(let error): 23 | SearchResultErrorView(title: "Lectures", error: error.localizedDescription) 24 | } 25 | }.onChange(of: query) { newQuery in 26 | Task { 27 | await vm.lectureSearch(for: newQuery) 28 | } 29 | }.task { 30 | await vm.lectureSearch(for: query) 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /Campus-iOS/SearchComponent/Views/SearchResultViews/Person/PersonSearchResultScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // PersonSearchResultScreen.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 07.03.23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @MainActor 11 | struct PersonSearchResultScreen: View { 12 | @StateObject var vm: PersonSearchResultViewModel 13 | @Binding var query: String 14 | @State var size: ResultSize = .small 15 | 16 | var body: some View { 17 | Group { 18 | switch vm.state { 19 | case .success(let data): 20 | PersonSearchResultView(allResults: data, model: vm.model, size: self.size) 21 | case .loading, .na: 22 | SearchResultLoadingView(title: "Person Search") 23 | case .failed(let error): 24 | SearchResultErrorView(title: "Person Search", error: error.localizedDescription) 25 | } 26 | }.onChange(of: query) { newQuery in 27 | Task { 28 | await vm.personSearch(for: newQuery) 29 | } 30 | } 31 | .task { 32 | await vm.personSearch(for: query) 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Campus-iOS/SearchComponent/Views/SearchResultViews/RoomFinder/RoomFinderSearchResultScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RoomFinderSearchResultScreen.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 07.03.23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | @MainActor 11 | struct RoomFinderSearchResultScreen: View { 12 | @StateObject var vm: RoomFinderSearchResultViewModel 13 | @Binding var query: String 14 | @State var size: ResultSize = .small 15 | 16 | var body: some View { 17 | Group { 18 | switch vm.state { 19 | case .success(let data): 20 | RoomFinderSearchResultView(allResults: data, size: self.size) 21 | case .loading, .na: 22 | SearchResultLoadingView(title: "RoomFinder") 23 | case .failed(let error): 24 | SearchResultErrorView(title: "RoomFinder", error: error.localizedDescription) 25 | } 26 | }.onChange(of: query) { newQuery in 27 | Task { 28 | await vm.roomFinderSearch(for: newQuery) 29 | } 30 | }.task { 31 | await vm.roomFinderSearch(for: query) 32 | } 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /Campus-iOS/SearchComponent/Views/SearchView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SearchView.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 27.12.22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct SearchView : View { 11 | 12 | @ObservedObject var model: Model 13 | @Binding var query: String 14 | @Environment(\.isSearching) var isSearching 15 | @ViewBuilder let content: () -> Content 16 | 17 | var body: some View { 18 | if isSearching { 19 | SearchResultView(vm: SearchResultViewModel(model: self.model)) 20 | } else { 21 | content() 22 | } 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /Campus-iOS/Secrets.xcconfig: -------------------------------------------------------------------------------- 1 | // 2 | // Secrets.xcconfig 3 | // Campus-iOS 4 | // 5 | // Created by Robyn Kölle on 10.09.22. 6 | // 7 | 8 | // Configuration settings file format documentation can be found at: 9 | // https://help.apple.com/xcode/#/dev745c5c974 10 | 11 | ANALYTICS_API = 12 | ANALYTICS_POST_TOKEN = 13 | -------------------------------------------------------------------------------- /Campus-iOS/Styles/Modifiers/RoundedCorners.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RoundedCorners.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 16.07.22. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | struct RoundedCorner: Shape { 12 | 13 | var radius: CGFloat = .infinity 14 | var corners: UIRectCorner = .allCorners 15 | 16 | func path(in rect: CGRect) -> Path { 17 | let path = UIBezierPath(roundedRect: rect, byRoundingCorners: corners, cornerRadii: CGSize(width: radius, height: radius)) 18 | return Path(path.cgPath) 19 | } 20 | } 21 | 22 | extension View { 23 | func cornerRadius(_ radius: CGFloat, corners: UIRectCorner) -> some View { 24 | clipShape( RoundedCorner(radius: radius, corners: corners) ) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /Campus-iOS/TUMSexyComponent/Model/TUMSexyLink.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TUMSexyLink.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 23.01.23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct TUMSexyLink: Decodable, Identifiable { 11 | var id = UUID() 12 | var description: String? 13 | var target: String? 14 | var moodleID: String? 15 | 16 | enum CodingKeys: String, CodingKey { 17 | case description = "description" 18 | case target = "target" 19 | case moodleID = "moodleID" 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /Campus-iOS/TUMSexyComponent/Screen/TUMSexyScreen.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TUMSexyScreen.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 23.01.23. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct TUMSexyScreen: View { 11 | @StateObject var vm = TUMSexyViewModel() 12 | 13 | var body: some View { 14 | Group { 15 | switch vm.state { 16 | case .success(let links): 17 | VStack { 18 | TUMSexyView(links: links) 19 | .refreshable { 20 | await vm.getLinks(forcedRefresh: true) 21 | } 22 | } 23 | case .loading, .na: 24 | LoadingView(text: "Fetching Links") 25 | case .failed(let error): 26 | FailedView( 27 | errorDescription: error.localizedDescription, 28 | retryClosure: vm.getLinks 29 | ) 30 | } 31 | }.task { 32 | await vm.getLinks() 33 | } 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /Campus-iOS/TUMSexyComponent/Service/TUMSexyService.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TUMSexyService.swift 3 | // Campus-iOS 4 | // 5 | // Created by David Lin on 23.01.23. 6 | // 7 | 8 | import Foundation 9 | 10 | struct TUMSexyService: ServiceProtocol { 11 | typealias T = TUMSexyLink 12 | 13 | func fetch(forcedRefresh: Bool) async throws -> [TUMSexyLink] { 14 | let response: [String : TUMSexyLink] = try await MainAPI.makeRequest(endpoint: TUMSexyAPI.standard, forcedRefresh: forcedRefresh) 15 | 16 | var links = [TUMSexyLink]() 17 | response.values.forEach { 18 | if $0.target != nil && $0.description != nil { 19 | links.append($0) 20 | } 21 | } 22 | 23 | return links 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /Campus-iOS/TUMSexyComponent/ViewModel/TUMSexyViewModel.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TUMSexyLink.swift 3 | // Campus-iOS 4 | // 5 | // Created by Milen Vitanov on 13.01.22. 6 | // 7 | 8 | import Foundation 9 | 10 | @MainActor 11 | class TUMSexyViewModel: ObservableObject { 12 | @Published var state: APIState<[TUMSexyLink]> = .na 13 | @Published var hasError: Bool = false 14 | 15 | let service = TUMSexyService() 16 | 17 | func getLinks(forcedRefresh: Bool = false) async { 18 | if !forcedRefresh { 19 | self.state = .loading 20 | } 21 | self.hasError = false 22 | 23 | do { 24 | self.state = .success( 25 | data: try await service.fetch(forcedRefresh: forcedRefresh) 26 | ) 27 | } catch { 28 | self.state = .failed(error: error) 29 | self.hasError = true 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Campus-iOS/TuitionComponent/View/TuitionView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // TuitionView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Milen Vitanov on 08.02.22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct TuitionView: View { 11 | 12 | let tuition: Tuition 13 | 14 | var body: some View { 15 | List { 16 | VStack(alignment: .center) { 17 | Spacer(minLength: 0.10 * UIScreen.main.bounds.width) 18 | TuitionCard(tuition: self.tuition) 19 | } 20 | .listRowBackground(Color.primaryBackground) 21 | } 22 | .scrollContentBackground(.hidden) 23 | .background(Color.primaryBackground) 24 | } 25 | } 26 | 27 | //struct TuitionView_Previews: PreviewProvider { 28 | // 29 | // static var previews: some View { 30 | // TuitionView(viewModel: ProfileViewModel()) 31 | // TuitionView(viewModel: ProfileViewModel()) 32 | // .preferredColorScheme(.dark) 33 | // } 34 | //} 35 | -------------------------------------------------------------------------------- /Campus-iOS/WidgetComponent/Recommender/RecommenderError.swift: -------------------------------------------------------------------------------- 1 | // 2 | // RecommenderError.swift 3 | // Campus-iOS 4 | // 5 | // Created by Robyn Kölle on 28.09.22. 6 | // 7 | 8 | import Foundation 9 | 10 | enum RecommenderError: Error { 11 | case missingData, missingModel, modelCreationFailed, impossiblePrediction, badRecommendation, missingPermissions 12 | } 13 | -------------------------------------------------------------------------------- /Campus-iOS/WidgetComponent/Recommender/Strategy/SpatioTemporalStrategy.swift: -------------------------------------------------------------------------------- 1 | // 2 | // SpatioTemporalStrategy.swift 3 | // Campus-iOS 4 | // 5 | // Created by Robyn Kölle on 02.08.22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct SpatioTemporalStrategy: WidgetRecommenderStrategy { 11 | 12 | func getRecommendation() async throws -> [WidgetRecommendation] { 13 | let timeStrategy = TimeStrategy() 14 | let locationStrategy = LocationStrategy() 15 | 16 | let timeRecommendations = try await timeStrategy.getRecommendation() 17 | let locationRecommendations = try await locationStrategy.getRecommendation() 18 | 19 | let recommendations = Widget.allCases.map { 20 | WidgetRecommendation( 21 | widget: $0, 22 | priority: priority(for: $0, in: timeRecommendations) + priority(for: $0, in: locationRecommendations) 23 | ) 24 | } 25 | 26 | return recommendations.filter { $0.priority > 0 } 27 | } 28 | 29 | private func priority(for widget: Widget, in array: [WidgetRecommendation]) -> Int { 30 | return array.first(where: { $0.widget == widget } )?.priority ?? 0 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /Campus-iOS/WidgetComponent/Recommender/Strategy/WidgetRecommenderStrategy.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WidgetRecommenderStrategy.swift 3 | // Campus-iOS 4 | // 5 | // Created by Robyn Kölle on 24.07.22. 6 | // 7 | 8 | import Foundation 9 | 10 | protocol WidgetRecommenderStrategy { 11 | func getRecommendation() async throws -> [WidgetRecommendation] 12 | } 13 | -------------------------------------------------------------------------------- /Campus-iOS/WidgetComponent/Recommender/Widget.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Widget.swift 3 | // Campus-iOS 4 | // 5 | // Created by Robyn Kölle on 24.07.22. 6 | // 7 | 8 | import Foundation 9 | 10 | enum Widget: CaseIterable { 11 | case cafeteria, studyRoom, calendar, tuition, grades, departures 12 | 13 | // Views associated to the widget in some way. 14 | // We can use this to make assumptions for widget recommendations, based on the views that the user visited. 15 | func associatedViews() -> [CampusAppView] { 16 | switch self { 17 | case .cafeteria: 18 | return [.cafeterias, .cafeteria] 19 | case .studyRoom: 20 | return [.studyRooms, .studyRoom] 21 | case .calendar: 22 | return [.calendar, .calendarEvent] 23 | case .tuition: 24 | return [.tuition] 25 | case .grades: 26 | return [.grades] 27 | case .departures: 28 | return [.departures] 29 | } 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Campus-iOS/WidgetComponent/Recommender/WidgetRecommendation.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WidgetRecommendation.swift 3 | // Campus-iOS 4 | // 5 | // Created by Robyn Kölle on 24.07.22. 6 | // 7 | 8 | import Foundation 9 | 10 | struct WidgetRecommendation { 11 | let widget: Widget 12 | let priority: Int 13 | let id = UUID() 14 | } 15 | -------------------------------------------------------------------------------- /Campus-iOS/WidgetComponent/Recommender/WidgetRecommender.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WidgetRecommender.swift 3 | // Campus-iOS 4 | // 5 | // Created by Robyn Kölle on 24.07.22. 6 | // 7 | 8 | import Foundation 9 | import SwiftUI 10 | 11 | @MainActor 12 | class WidgetRecommender: ObservableObject { 13 | 14 | @Published var status: WidgetRecommenderStatus 15 | @Published var recommendations: [WidgetRecommendation] 16 | 17 | private let strategy: WidgetRecommenderStrategy 18 | private let model: Model 19 | 20 | init(strategy: WidgetRecommenderStrategy, model: Model) { 21 | self.strategy = strategy 22 | self.model = model 23 | self.status = .loading 24 | self.recommendations = [] 25 | } 26 | 27 | func fetchRecommendations() async throws { 28 | let recommendations = try await strategy.getRecommendation().sorted(by: { $0.priority > $1.priority }) 29 | self.recommendations = recommendations 30 | self.status = .success 31 | } 32 | } 33 | 34 | enum WidgetRecommenderStatus { 35 | case loading, success 36 | } 37 | -------------------------------------------------------------------------------- /Campus-iOS/WidgetComponent/View/WidgetLoadingView.swift: -------------------------------------------------------------------------------- 1 | // 2 | // WidgetLoadingView.swift 3 | // Campus-iOS 4 | // 5 | // Created by Robyn Kölle on 05.07.22. 6 | // 7 | 8 | import SwiftUI 9 | 10 | struct WidgetLoadingView: View { 11 | 12 | @Environment(\.colorScheme) private var colorScheme 13 | let text: String 14 | 15 | var body: some View { 16 | Rectangle() 17 | .foregroundColor(.secondaryBackground) 18 | .overlay { 19 | VStack { 20 | Text(text) 21 | ProgressView() 22 | } 23 | } 24 | } 25 | } 26 | 27 | struct WidgetLoadingView_Previews: PreviewProvider { 28 | static var previews: some View { 29 | WidgetLoadingView(text: "Loading Preview") 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /Campus-iOSUITests/Campus_iOSUITestsLaunchTests.swift: -------------------------------------------------------------------------------- 1 | // 2 | // Campus_iOSUITestsLaunchTests.swift 3 | // Campus-iOSUITests 4 | // 5 | // Created by Milen Vitanov on 01.12.21. 6 | // 7 | 8 | import XCTest 9 | 10 | class Campus_iOSUITestsLaunchTests: XCTestCase { 11 | 12 | override class var runsForEachTargetApplicationUIConfiguration: Bool { 13 | true 14 | } 15 | 16 | override func setUpWithError() throws { 17 | continueAfterFailure = false 18 | } 19 | 20 | func testLaunch() throws { 21 | let app = XCUIApplication() 22 | app.launch() 23 | 24 | // Insert steps here to perform after app launch but before taking a screenshot, 25 | // such as logging into a test account or navigating somewhere in the app 26 | 27 | let attachment = XCTAttachment(screenshot: app.screenshot()) 28 | attachment.name = "Launch Screen" 29 | attachment.lifetime = .keepAlways 30 | add(attachment) 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /PULL_REQUEST_TEMPLATE.md: -------------------------------------------------------------------------------- 1 | ### Issue 2 | 3 | 4 | This fixes the following issue(s): 5 | 6 | 7 | ### Screenshots 8 | 9 | -------------------------------------------------------------------------------- /default.profraw: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/TUM-Dev/Campus-iOS/deb8254182fd714bf3404d7702edff94cf9982fd/default.profraw --------------------------------------------------------------------------------